Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * QuickJS Javascript Engine |
3 | | * |
4 | | * Copyright (c) 2017-2021 Fabrice Bellard |
5 | | * Copyright (c) 2017-2021 Charlie Gordon |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
20 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
23 | | * THE SOFTWARE. |
24 | | */ |
25 | | #include <stdlib.h> |
26 | | #include <stdio.h> |
27 | | #include <stdarg.h> |
28 | | #include <inttypes.h> |
29 | | #include <string.h> |
30 | | #include <assert.h> |
31 | | #include <sys/time.h> |
32 | | #include <time.h> |
33 | | #include <fenv.h> |
34 | | #include <math.h> |
35 | | #if defined(__APPLE__) |
36 | | #include <malloc/malloc.h> |
37 | | #elif defined(__linux__) |
38 | | #include <malloc.h> |
39 | | #elif defined(__FreeBSD__) |
40 | | #include <malloc_np.h> |
41 | | #endif |
42 | | |
43 | | #include "cutils.h" |
44 | | #include "list.h" |
45 | | #include "quickjs.h" |
46 | | #include "libregexp.h" |
47 | | #ifdef CONFIG_BIGNUM |
48 | | #include "libbf.h" |
49 | | #endif |
50 | | |
51 | 1.33M | #define OPTIMIZE 1 |
52 | | #define SHORT_OPCODES 1 |
53 | | #if defined(EMSCRIPTEN) |
54 | | #define DIRECT_DISPATCH 0 |
55 | | #else |
56 | | #define DIRECT_DISPATCH 1 |
57 | | #endif |
58 | | |
59 | | #if defined(__APPLE__) |
60 | | #define MALLOC_OVERHEAD 0 |
61 | | #else |
62 | 12.8M | #define MALLOC_OVERHEAD 8 |
63 | | #endif |
64 | | |
65 | | #if !defined(_WIN32) |
66 | | /* define it if printf uses the RNDN rounding mode instead of RNDNA */ |
67 | | #define CONFIG_PRINTF_RNDN |
68 | | #endif |
69 | | |
70 | | /* define to include Atomics.* operations which depend on the OS |
71 | | threads */ |
72 | | #if !defined(EMSCRIPTEN) |
73 | | #define CONFIG_ATOMICS |
74 | | #endif |
75 | | |
76 | | #if !defined(EMSCRIPTEN) |
77 | | /* enable stack limitation */ |
78 | | #define CONFIG_STACK_CHECK |
79 | | #endif |
80 | | |
81 | | |
82 | | /* dump object free */ |
83 | | //#define DUMP_FREE |
84 | | //#define DUMP_CLOSURE |
85 | | /* dump the bytecode of the compiled functions: combination of bits |
86 | | 1: dump pass 3 final byte code |
87 | | 2: dump pass 2 code |
88 | | 4: dump pass 1 code |
89 | | 8: dump stdlib functions |
90 | | 16: dump bytecode in hex |
91 | | 32: dump line number table |
92 | | */ |
93 | | //#define DUMP_BYTECODE (1) |
94 | | /* dump the occurence of the automatic GC */ |
95 | | //#define DUMP_GC |
96 | | /* dump objects freed by the garbage collector */ |
97 | | //#define DUMP_GC_FREE |
98 | | /* dump objects leaking when freeing the runtime */ |
99 | | //#define DUMP_LEAKS 1 |
100 | | /* dump memory usage before running the garbage collector */ |
101 | | //#define DUMP_MEM |
102 | | //#define DUMP_OBJECTS /* dump objects in JS_FreeContext */ |
103 | | //#define DUMP_ATOMS /* dump atoms in JS_FreeContext */ |
104 | | //#define DUMP_SHAPES /* dump shapes in JS_FreeContext */ |
105 | | //#define DUMP_MODULE_RESOLVE |
106 | | //#define DUMP_PROMISE |
107 | | //#define DUMP_READ_OBJECT |
108 | | |
109 | | /* test the GC by forcing it before each object allocation */ |
110 | | //#define FORCE_GC_AT_MALLOC |
111 | | |
112 | | #ifdef CONFIG_ATOMICS |
113 | | #include <pthread.h> |
114 | | #include <stdatomic.h> |
115 | | #include <errno.h> |
116 | | #endif |
117 | | |
118 | | enum { |
119 | | /* classid tag */ /* union usage | properties */ |
120 | | JS_CLASS_OBJECT = 1, /* must be first */ |
121 | | JS_CLASS_ARRAY, /* u.array | length */ |
122 | | JS_CLASS_ERROR, |
123 | | JS_CLASS_NUMBER, /* u.object_data */ |
124 | | JS_CLASS_STRING, /* u.object_data */ |
125 | | JS_CLASS_BOOLEAN, /* u.object_data */ |
126 | | JS_CLASS_SYMBOL, /* u.object_data */ |
127 | | JS_CLASS_ARGUMENTS, /* u.array | length */ |
128 | | JS_CLASS_MAPPED_ARGUMENTS, /* | length */ |
129 | | JS_CLASS_DATE, /* u.object_data */ |
130 | | JS_CLASS_MODULE_NS, |
131 | | JS_CLASS_C_FUNCTION, /* u.cfunc */ |
132 | | JS_CLASS_BYTECODE_FUNCTION, /* u.func */ |
133 | | JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ |
134 | | JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ |
135 | | JS_CLASS_GENERATOR_FUNCTION, /* u.func */ |
136 | | JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ |
137 | | JS_CLASS_REGEXP, /* u.regexp */ |
138 | | JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ |
139 | | JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ |
140 | | JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ |
141 | | JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ |
142 | | JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ |
143 | | JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ |
144 | | JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ |
145 | | JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ |
146 | | JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ |
147 | | #ifdef CONFIG_BIGNUM |
148 | | JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ |
149 | | JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ |
150 | | #endif |
151 | | JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ |
152 | | JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ |
153 | | JS_CLASS_DATAVIEW, /* u.typed_array */ |
154 | | #ifdef CONFIG_BIGNUM |
155 | | JS_CLASS_BIG_INT, /* u.object_data */ |
156 | | JS_CLASS_BIG_FLOAT, /* u.object_data */ |
157 | | JS_CLASS_FLOAT_ENV, /* u.float_env */ |
158 | | JS_CLASS_BIG_DECIMAL, /* u.object_data */ |
159 | | JS_CLASS_OPERATOR_SET, /* u.operator_set */ |
160 | | #endif |
161 | | JS_CLASS_MAP, /* u.map_state */ |
162 | | JS_CLASS_SET, /* u.map_state */ |
163 | | JS_CLASS_WEAKMAP, /* u.map_state */ |
164 | | JS_CLASS_WEAKSET, /* u.map_state */ |
165 | | JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ |
166 | | JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ |
167 | | JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ |
168 | | JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ |
169 | | JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ |
170 | | JS_CLASS_GENERATOR, /* u.generator_data */ |
171 | | JS_CLASS_PROXY, /* u.proxy_data */ |
172 | | JS_CLASS_PROMISE, /* u.promise_data */ |
173 | | JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ |
174 | | JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ |
175 | | JS_CLASS_ASYNC_FUNCTION, /* u.func */ |
176 | | JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ |
177 | | JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ |
178 | | JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ |
179 | | JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ |
180 | | JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ |
181 | | |
182 | | JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ |
183 | | }; |
184 | | |
185 | | /* number of typed array types */ |
186 | 24 | #define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) |
187 | | static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; |
188 | 44 | #define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) |
189 | | |
190 | | typedef enum JSErrorEnum { |
191 | | JS_EVAL_ERROR, |
192 | | JS_RANGE_ERROR, |
193 | | JS_REFERENCE_ERROR, |
194 | | JS_SYNTAX_ERROR, |
195 | | JS_TYPE_ERROR, |
196 | | JS_URI_ERROR, |
197 | | JS_INTERNAL_ERROR, |
198 | | JS_AGGREGATE_ERROR, |
199 | | |
200 | | JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ |
201 | | } JSErrorEnum; |
202 | | |
203 | 47.2k | #define JS_MAX_LOCAL_VARS 65536 |
204 | 46.8k | #define JS_STACK_SIZE_MAX 65534 |
205 | 439k | #define JS_STRING_LEN_MAX ((1 << 30) - 1) |
206 | | |
207 | | #define __exception __attribute__((warn_unused_result)) |
208 | | |
209 | | typedef struct JSShape JSShape; |
210 | | typedef struct JSString JSString; |
211 | | typedef struct JSString JSAtomStruct; |
212 | | |
213 | | typedef enum { |
214 | | JS_GC_PHASE_NONE, |
215 | | JS_GC_PHASE_DECREF, |
216 | | JS_GC_PHASE_REMOVE_CYCLES, |
217 | | } JSGCPhaseEnum; |
218 | | |
219 | | typedef enum OPCodeEnum OPCodeEnum; |
220 | | |
221 | | #ifdef CONFIG_BIGNUM |
222 | | /* function pointers are used for numeric operations so that it is |
223 | | possible to remove some numeric types */ |
224 | | typedef struct { |
225 | | JSValue (*to_string)(JSContext *ctx, JSValueConst val); |
226 | | JSValue (*from_string)(JSContext *ctx, const char *buf, |
227 | | int radix, int flags, slimb_t *pexponent); |
228 | | int (*unary_arith)(JSContext *ctx, |
229 | | JSValue *pres, OPCodeEnum op, JSValue op1); |
230 | | int (*binary_arith)(JSContext *ctx, OPCodeEnum op, |
231 | | JSValue *pres, JSValue op1, JSValue op2); |
232 | | int (*compare)(JSContext *ctx, OPCodeEnum op, |
233 | | JSValue op1, JSValue op2); |
234 | | /* only for bigfloat: */ |
235 | | JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, |
236 | | int64_t exponent); |
237 | | int (*mul_pow10)(JSContext *ctx, JSValue *sp); |
238 | | } JSNumericOperations; |
239 | | #endif |
240 | | |
241 | | struct JSRuntime { |
242 | | JSMallocFunctions mf; |
243 | | JSMallocState malloc_state; |
244 | | const char *rt_info; |
245 | | |
246 | | int atom_hash_size; /* power of two */ |
247 | | int atom_count; |
248 | | int atom_size; |
249 | | int atom_count_resize; /* resize hash table at this count */ |
250 | | uint32_t *atom_hash; |
251 | | JSAtomStruct **atom_array; |
252 | | int atom_free_index; /* 0 = none */ |
253 | | |
254 | | int class_count; /* size of class_array */ |
255 | | JSClass *class_array; |
256 | | |
257 | | struct list_head context_list; /* list of JSContext.link */ |
258 | | /* list of JSGCObjectHeader.link. List of allocated GC objects (used |
259 | | by the garbage collector) */ |
260 | | struct list_head gc_obj_list; |
261 | | /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ |
262 | | struct list_head gc_zero_ref_count_list; |
263 | | struct list_head tmp_obj_list; /* used during GC */ |
264 | | JSGCPhaseEnum gc_phase : 8; |
265 | | size_t malloc_gc_threshold; |
266 | | #ifdef DUMP_LEAKS |
267 | | struct list_head string_list; /* list of JSString.link */ |
268 | | #endif |
269 | | /* stack limitation */ |
270 | | uintptr_t stack_size; /* in bytes, 0 if no limit */ |
271 | | uintptr_t stack_top; |
272 | | uintptr_t stack_limit; /* lower stack limit */ |
273 | | |
274 | | JSValue current_exception; |
275 | | /* true if inside an out of memory error, to avoid recursing */ |
276 | | BOOL in_out_of_memory : 8; |
277 | | |
278 | | struct JSStackFrame *current_stack_frame; |
279 | | |
280 | | JSInterruptHandler *interrupt_handler; |
281 | | void *interrupt_opaque; |
282 | | |
283 | | JSHostPromiseRejectionTracker *host_promise_rejection_tracker; |
284 | | void *host_promise_rejection_tracker_opaque; |
285 | | |
286 | | struct list_head job_list; /* list of JSJobEntry.link */ |
287 | | |
288 | | JSModuleNormalizeFunc *module_normalize_func; |
289 | | JSModuleLoaderFunc *module_loader_func; |
290 | | void *module_loader_opaque; |
291 | | |
292 | | BOOL can_block : 8; /* TRUE if Atomics.wait can block */ |
293 | | /* used to allocate, free and clone SharedArrayBuffers */ |
294 | | JSSharedArrayBufferFunctions sab_funcs; |
295 | | |
296 | | /* Shape hash table */ |
297 | | int shape_hash_bits; |
298 | | int shape_hash_size; |
299 | | int shape_hash_count; /* number of hashed shapes */ |
300 | | JSShape **shape_hash; |
301 | | #ifdef CONFIG_BIGNUM |
302 | | bf_context_t bf_ctx; |
303 | | JSNumericOperations bigint_ops; |
304 | | JSNumericOperations bigfloat_ops; |
305 | | JSNumericOperations bigdecimal_ops; |
306 | | uint32_t operator_count; |
307 | | #endif |
308 | | void *user_opaque; |
309 | | }; |
310 | | |
311 | | struct JSClass { |
312 | | uint32_t class_id; /* 0 means free entry */ |
313 | | JSAtom class_name; |
314 | | JSClassFinalizer *finalizer; |
315 | | JSClassGCMark *gc_mark; |
316 | | JSClassCall *call; |
317 | | /* pointers for exotic behavior, can be NULL if none are present */ |
318 | | const JSClassExoticMethods *exotic; |
319 | | }; |
320 | | |
321 | 133k | #define JS_MODE_STRICT (1 << 0) |
322 | 282k | #define JS_MODE_STRIP (1 << 1) |
323 | 774k | #define JS_MODE_MATH (1 << 2) |
324 | | |
325 | | typedef struct JSStackFrame { |
326 | | struct JSStackFrame *prev_frame; /* NULL if first stack frame */ |
327 | | JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ |
328 | | JSValue *arg_buf; /* arguments */ |
329 | | JSValue *var_buf; /* variables */ |
330 | | struct list_head var_ref_list; /* list of JSVarRef.link */ |
331 | | const uint8_t *cur_pc; /* only used in bytecode functions : PC of the |
332 | | instruction after the call */ |
333 | | int arg_count; |
334 | | int js_mode; /* 0 or JS_MODE_MATH for C functions */ |
335 | | /* only used in generators. Current stack pointer value. NULL if |
336 | | the function is running. */ |
337 | | JSValue *cur_sp; |
338 | | } JSStackFrame; |
339 | | |
340 | | typedef enum { |
341 | | JS_GC_OBJ_TYPE_JS_OBJECT, |
342 | | JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, |
343 | | JS_GC_OBJ_TYPE_SHAPE, |
344 | | JS_GC_OBJ_TYPE_VAR_REF, |
345 | | JS_GC_OBJ_TYPE_ASYNC_FUNCTION, |
346 | | JS_GC_OBJ_TYPE_JS_CONTEXT, |
347 | | } JSGCObjectTypeEnum; |
348 | | |
349 | | /* header for GC objects. GC objects are C data structures with a |
350 | | reference count that can reference other GC objects. JS Objects are |
351 | | a particular type of GC object. */ |
352 | | struct JSGCObjectHeader { |
353 | | int ref_count; /* must come first, 32-bit */ |
354 | | JSGCObjectTypeEnum gc_obj_type : 4; |
355 | | uint8_t mark : 4; /* used by the GC */ |
356 | | uint8_t dummy1; /* not used by the GC */ |
357 | | uint16_t dummy2; /* not used by the GC */ |
358 | | struct list_head link; |
359 | | }; |
360 | | |
361 | | typedef struct JSVarRef { |
362 | | union { |
363 | | JSGCObjectHeader header; /* must come first */ |
364 | | struct { |
365 | | int __gc_ref_count; /* corresponds to header.ref_count */ |
366 | | uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ |
367 | | |
368 | | /* 0 : the JSVarRef is on the stack. header.link is an element |
369 | | of JSStackFrame.var_ref_list. |
370 | | 1 : the JSVarRef is detached. header.link has the normal meanning |
371 | | */ |
372 | | uint8_t is_detached : 1; |
373 | | uint8_t is_arg : 1; |
374 | | uint16_t var_idx; /* index of the corresponding function variable on |
375 | | the stack */ |
376 | | }; |
377 | | }; |
378 | | JSValue *pvalue; /* pointer to the value, either on the stack or |
379 | | to 'value' */ |
380 | | JSValue value; /* used when the variable is no longer on the stack */ |
381 | | } JSVarRef; |
382 | | |
383 | | #ifdef CONFIG_BIGNUM |
384 | | typedef struct JSFloatEnv { |
385 | | limb_t prec; |
386 | | bf_flags_t flags; |
387 | | unsigned int status; |
388 | | } JSFloatEnv; |
389 | | |
390 | | /* the same structure is used for big integers and big floats. Big |
391 | | integers are never infinite or NaNs */ |
392 | | typedef struct JSBigFloat { |
393 | | JSRefCountHeader header; /* must come first, 32-bit */ |
394 | | bf_t num; |
395 | | } JSBigFloat; |
396 | | |
397 | | typedef struct JSBigDecimal { |
398 | | JSRefCountHeader header; /* must come first, 32-bit */ |
399 | | bfdec_t num; |
400 | | } JSBigDecimal; |
401 | | #endif |
402 | | |
403 | | typedef enum { |
404 | | JS_AUTOINIT_ID_PROTOTYPE, |
405 | | JS_AUTOINIT_ID_MODULE_NS, |
406 | | JS_AUTOINIT_ID_PROP, |
407 | | } JSAutoInitIDEnum; |
408 | | |
409 | | /* must be large enough to have a negligible runtime cost and small |
410 | | enough to call the interrupt callback often. */ |
411 | 91 | #define JS_INTERRUPT_COUNTER_INIT 10000 |
412 | | |
413 | | struct JSContext { |
414 | | JSGCObjectHeader header; /* must come first */ |
415 | | JSRuntime *rt; |
416 | | struct list_head link; |
417 | | |
418 | | uint16_t binary_object_count; |
419 | | int binary_object_size; |
420 | | |
421 | | JSShape *array_shape; /* initial shape for Array objects */ |
422 | | |
423 | | JSValue *class_proto; |
424 | | JSValue function_proto; |
425 | | JSValue function_ctor; |
426 | | JSValue array_ctor; |
427 | | JSValue regexp_ctor; |
428 | | JSValue promise_ctor; |
429 | | JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; |
430 | | JSValue iterator_proto; |
431 | | JSValue async_iterator_proto; |
432 | | JSValue array_proto_values; |
433 | | JSValue throw_type_error; |
434 | | JSValue eval_obj; |
435 | | |
436 | | JSValue global_obj; /* global object */ |
437 | | JSValue global_var_obj; /* contains the global let/const definitions */ |
438 | | |
439 | | uint64_t random_state; |
440 | | #ifdef CONFIG_BIGNUM |
441 | | bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ |
442 | | JSFloatEnv fp_env; /* global FP environment */ |
443 | | BOOL bignum_ext : 8; /* enable math mode */ |
444 | | BOOL allow_operator_overloading : 8; |
445 | | #endif |
446 | | /* when the counter reaches zero, JSRutime.interrupt_handler is called */ |
447 | | int interrupt_counter; |
448 | | BOOL is_error_property_enabled; |
449 | | |
450 | | struct list_head loaded_modules; /* list of JSModuleDef.link */ |
451 | | |
452 | | /* if NULL, RegExp compilation is not supported */ |
453 | | JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, |
454 | | JSValueConst flags); |
455 | | /* if NULL, eval is not supported */ |
456 | | JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, |
457 | | const char *input, size_t input_len, |
458 | | const char *filename, int flags, int scope_idx); |
459 | | void *user_opaque; |
460 | | }; |
461 | | |
462 | | typedef union JSFloat64Union { |
463 | | double d; |
464 | | uint64_t u64; |
465 | | uint32_t u32[2]; |
466 | | } JSFloat64Union; |
467 | | |
468 | | enum { |
469 | | JS_ATOM_TYPE_STRING = 1, |
470 | | JS_ATOM_TYPE_GLOBAL_SYMBOL, |
471 | | JS_ATOM_TYPE_SYMBOL, |
472 | | JS_ATOM_TYPE_PRIVATE, |
473 | | }; |
474 | | |
475 | | enum { |
476 | | JS_ATOM_HASH_SYMBOL, |
477 | | JS_ATOM_HASH_PRIVATE, |
478 | | }; |
479 | | |
480 | | typedef enum { |
481 | | JS_ATOM_KIND_STRING, |
482 | | JS_ATOM_KIND_SYMBOL, |
483 | | JS_ATOM_KIND_PRIVATE, |
484 | | } JSAtomKindEnum; |
485 | | |
486 | 831k | #define JS_ATOM_HASH_MASK ((1 << 30) - 1) |
487 | | |
488 | | struct JSString { |
489 | | JSRefCountHeader header; /* must come first, 32-bit */ |
490 | | uint32_t len : 31; |
491 | | uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ |
492 | | /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, |
493 | | for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 |
494 | | XXX: could change encoding to have one more bit in hash */ |
495 | | uint32_t hash : 30; |
496 | | uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ |
497 | | uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ |
498 | | #ifdef DUMP_LEAKS |
499 | | struct list_head link; /* string list */ |
500 | | #endif |
501 | | union { |
502 | | uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ |
503 | | uint16_t str16[0]; |
504 | | } u; |
505 | | }; |
506 | | |
507 | | typedef struct JSClosureVar { |
508 | | uint8_t is_local : 1; |
509 | | uint8_t is_arg : 1; |
510 | | uint8_t is_const : 1; |
511 | | uint8_t is_lexical : 1; |
512 | | uint8_t var_kind : 4; /* see JSVarKindEnum */ |
513 | | /* 8 bits available */ |
514 | | uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the |
515 | | parent function. otherwise: index to a closure |
516 | | variable of the parent function */ |
517 | | JSAtom var_name; |
518 | | } JSClosureVar; |
519 | | |
520 | 118 | #define ARG_SCOPE_INDEX 1 |
521 | 577k | #define ARG_SCOPE_END (-2) |
522 | | |
523 | | typedef struct JSVarScope { |
524 | | int parent; /* index into fd->scopes of the enclosing scope */ |
525 | | int first; /* index into fd->vars of the last variable in this scope */ |
526 | | } JSVarScope; |
527 | | |
528 | | typedef enum { |
529 | | /* XXX: add more variable kinds here instead of using bit fields */ |
530 | | JS_VAR_NORMAL, |
531 | | JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ |
532 | | JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator |
533 | | function declaration */ |
534 | | JS_VAR_CATCH, |
535 | | JS_VAR_FUNCTION_NAME, /* function expression name */ |
536 | | JS_VAR_PRIVATE_FIELD, |
537 | | JS_VAR_PRIVATE_METHOD, |
538 | | JS_VAR_PRIVATE_GETTER, |
539 | | JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ |
540 | | JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ |
541 | | } JSVarKindEnum; |
542 | | |
543 | | /* XXX: could use a different structure in bytecode functions to save |
544 | | memory */ |
545 | | typedef struct JSVarDef { |
546 | | JSAtom var_name; |
547 | | /* index into fd->scopes of this variable lexical scope */ |
548 | | int scope_level; |
549 | | /* during compilation: |
550 | | - if scope_level = 0: scope in which the variable is defined |
551 | | - if scope_level != 0: index into fd->vars of the next |
552 | | variable in the same or enclosing lexical scope |
553 | | in a bytecode function: |
554 | | index into fd->vars of the next |
555 | | variable in the same or enclosing lexical scope |
556 | | */ |
557 | | int scope_next; |
558 | | uint8_t is_const : 1; |
559 | | uint8_t is_lexical : 1; |
560 | | uint8_t is_captured : 1; |
561 | | uint8_t var_kind : 4; /* see JSVarKindEnum */ |
562 | | /* only used during compilation: function pool index for lexical |
563 | | variables with var_kind = |
564 | | JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of |
565 | | the definition of the 'var' variables (they have scope_level = |
566 | | 0) */ |
567 | | int func_pool_idx : 24; /* only used during compilation : index in |
568 | | the constant pool for hoisted function |
569 | | definition */ |
570 | | } JSVarDef; |
571 | | |
572 | | /* for the encoding of the pc2line table */ |
573 | 5.75M | #define PC2LINE_BASE (-1) |
574 | 11.3M | #define PC2LINE_RANGE 5 |
575 | 5.68M | #define PC2LINE_OP_FIRST 1 |
576 | 18.1k | #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) |
577 | | |
578 | | typedef enum JSFunctionKindEnum { |
579 | | JS_FUNC_NORMAL = 0, |
580 | | JS_FUNC_GENERATOR = (1 << 0), |
581 | | JS_FUNC_ASYNC = (1 << 1), |
582 | | JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), |
583 | | } JSFunctionKindEnum; |
584 | | |
585 | | typedef struct JSFunctionBytecode { |
586 | | JSGCObjectHeader header; /* must come first */ |
587 | | uint8_t js_mode; |
588 | | uint8_t has_prototype : 1; /* true if a prototype field is necessary */ |
589 | | uint8_t has_simple_parameter_list : 1; |
590 | | uint8_t is_derived_class_constructor : 1; |
591 | | /* true if home_object needs to be initialized */ |
592 | | uint8_t need_home_object : 1; |
593 | | uint8_t func_kind : 2; |
594 | | uint8_t new_target_allowed : 1; |
595 | | uint8_t super_call_allowed : 1; |
596 | | uint8_t super_allowed : 1; |
597 | | uint8_t arguments_allowed : 1; |
598 | | uint8_t has_debug : 1; |
599 | | uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ |
600 | | uint8_t read_only_bytecode : 1; |
601 | | /* XXX: 4 bits available */ |
602 | | uint8_t *byte_code_buf; /* (self pointer) */ |
603 | | int byte_code_len; |
604 | | JSAtom func_name; |
605 | | JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ |
606 | | JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ |
607 | | uint16_t arg_count; |
608 | | uint16_t var_count; |
609 | | uint16_t defined_arg_count; /* for length function property */ |
610 | | uint16_t stack_size; /* maximum stack size */ |
611 | | JSContext *realm; /* function realm */ |
612 | | JSValue *cpool; /* constant pool (self pointer) */ |
613 | | int cpool_count; |
614 | | int closure_var_count; |
615 | | struct { |
616 | | /* debug info, move to separate structure to save memory? */ |
617 | | JSAtom filename; |
618 | | int line_num; |
619 | | int source_len; |
620 | | int pc2line_len; |
621 | | uint8_t *pc2line_buf; |
622 | | char *source; |
623 | | } debug; |
624 | | } JSFunctionBytecode; |
625 | | |
626 | | typedef struct JSBoundFunction { |
627 | | JSValue func_obj; |
628 | | JSValue this_val; |
629 | | int argc; |
630 | | JSValue argv[0]; |
631 | | } JSBoundFunction; |
632 | | |
633 | | typedef enum JSIteratorKindEnum { |
634 | | JS_ITERATOR_KIND_KEY, |
635 | | JS_ITERATOR_KIND_VALUE, |
636 | | JS_ITERATOR_KIND_KEY_AND_VALUE, |
637 | | } JSIteratorKindEnum; |
638 | | |
639 | | typedef struct JSForInIterator { |
640 | | JSValue obj; |
641 | | BOOL is_array; |
642 | | uint32_t array_length; |
643 | | uint32_t idx; |
644 | | } JSForInIterator; |
645 | | |
646 | | typedef struct JSRegExp { |
647 | | JSString *pattern; |
648 | | JSString *bytecode; /* also contains the flags */ |
649 | | } JSRegExp; |
650 | | |
651 | | typedef struct JSProxyData { |
652 | | JSValue target; |
653 | | JSValue handler; |
654 | | uint8_t is_func; |
655 | | uint8_t is_revoked; |
656 | | } JSProxyData; |
657 | | |
658 | | typedef struct JSArrayBuffer { |
659 | | int byte_length; /* 0 if detached */ |
660 | | uint8_t detached; |
661 | | uint8_t shared; /* if shared, the array buffer cannot be detached */ |
662 | | uint8_t *data; /* NULL if detached */ |
663 | | struct list_head array_list; |
664 | | void *opaque; |
665 | | JSFreeArrayBufferDataFunc *free_func; |
666 | | } JSArrayBuffer; |
667 | | |
668 | | typedef struct JSTypedArray { |
669 | | struct list_head link; /* link to arraybuffer */ |
670 | | JSObject *obj; /* back pointer to the TypedArray/DataView object */ |
671 | | JSObject *buffer; /* based array buffer */ |
672 | | uint32_t offset; /* offset in the array buffer */ |
673 | | uint32_t length; /* length in the array buffer */ |
674 | | } JSTypedArray; |
675 | | |
676 | | typedef struct JSAsyncFunctionState { |
677 | | JSValue this_val; /* 'this' generator argument */ |
678 | | int argc; /* number of function arguments */ |
679 | | BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ |
680 | | JSStackFrame frame; |
681 | | } JSAsyncFunctionState; |
682 | | |
683 | | /* XXX: could use an object instead to avoid the |
684 | | JS_TAG_ASYNC_FUNCTION tag for the GC */ |
685 | | typedef struct JSAsyncFunctionData { |
686 | | JSGCObjectHeader header; /* must come first */ |
687 | | JSValue resolving_funcs[2]; |
688 | | BOOL is_active; /* true if the async function state is valid */ |
689 | | JSAsyncFunctionState func_state; |
690 | | } JSAsyncFunctionData; |
691 | | |
692 | | typedef enum { |
693 | | /* binary operators */ |
694 | | JS_OVOP_ADD, |
695 | | JS_OVOP_SUB, |
696 | | JS_OVOP_MUL, |
697 | | JS_OVOP_DIV, |
698 | | JS_OVOP_MOD, |
699 | | JS_OVOP_POW, |
700 | | JS_OVOP_OR, |
701 | | JS_OVOP_AND, |
702 | | JS_OVOP_XOR, |
703 | | JS_OVOP_SHL, |
704 | | JS_OVOP_SAR, |
705 | | JS_OVOP_SHR, |
706 | | JS_OVOP_EQ, |
707 | | JS_OVOP_LESS, |
708 | | |
709 | | JS_OVOP_BINARY_COUNT, |
710 | | /* unary operators */ |
711 | | JS_OVOP_POS = JS_OVOP_BINARY_COUNT, |
712 | | JS_OVOP_NEG, |
713 | | JS_OVOP_INC, |
714 | | JS_OVOP_DEC, |
715 | | JS_OVOP_NOT, |
716 | | |
717 | | JS_OVOP_COUNT, |
718 | | } JSOverloadableOperatorEnum; |
719 | | |
720 | | typedef struct { |
721 | | uint32_t operator_index; |
722 | | JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ |
723 | | } JSBinaryOperatorDefEntry; |
724 | | |
725 | | typedef struct { |
726 | | int count; |
727 | | JSBinaryOperatorDefEntry *tab; |
728 | | } JSBinaryOperatorDef; |
729 | | |
730 | | typedef struct { |
731 | | uint32_t operator_counter; |
732 | | BOOL is_primitive; /* OperatorSet for a primitive type */ |
733 | | /* NULL if no operator is defined */ |
734 | | JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ |
735 | | JSBinaryOperatorDef left; |
736 | | JSBinaryOperatorDef right; |
737 | | } JSOperatorSetData; |
738 | | |
739 | | typedef struct JSReqModuleEntry { |
740 | | JSAtom module_name; |
741 | | JSModuleDef *module; /* used using resolution */ |
742 | | } JSReqModuleEntry; |
743 | | |
744 | | typedef enum JSExportTypeEnum { |
745 | | JS_EXPORT_TYPE_LOCAL, |
746 | | JS_EXPORT_TYPE_INDIRECT, |
747 | | } JSExportTypeEnum; |
748 | | |
749 | | typedef struct JSExportEntry { |
750 | | union { |
751 | | struct { |
752 | | int var_idx; /* closure variable index */ |
753 | | JSVarRef *var_ref; /* if != NULL, reference to the variable */ |
754 | | } local; /* for local export */ |
755 | | int req_module_idx; /* module for indirect export */ |
756 | | } u; |
757 | | JSExportTypeEnum export_type; |
758 | | JSAtom local_name; /* '*' if export ns from. not used for local |
759 | | export after compilation */ |
760 | | JSAtom export_name; /* exported variable name */ |
761 | | } JSExportEntry; |
762 | | |
763 | | typedef struct JSStarExportEntry { |
764 | | int req_module_idx; /* in req_module_entries */ |
765 | | } JSStarExportEntry; |
766 | | |
767 | | typedef struct JSImportEntry { |
768 | | int var_idx; /* closure variable index */ |
769 | | JSAtom import_name; |
770 | | int req_module_idx; /* in req_module_entries */ |
771 | | } JSImportEntry; |
772 | | |
773 | | struct JSModuleDef { |
774 | | JSRefCountHeader header; /* must come first, 32-bit */ |
775 | | JSAtom module_name; |
776 | | struct list_head link; |
777 | | |
778 | | JSReqModuleEntry *req_module_entries; |
779 | | int req_module_entries_count; |
780 | | int req_module_entries_size; |
781 | | |
782 | | JSExportEntry *export_entries; |
783 | | int export_entries_count; |
784 | | int export_entries_size; |
785 | | |
786 | | JSStarExportEntry *star_export_entries; |
787 | | int star_export_entries_count; |
788 | | int star_export_entries_size; |
789 | | |
790 | | JSImportEntry *import_entries; |
791 | | int import_entries_count; |
792 | | int import_entries_size; |
793 | | |
794 | | JSValue module_ns; |
795 | | JSValue func_obj; /* only used for JS modules */ |
796 | | JSModuleInitFunc *init_func; /* only used for C modules */ |
797 | | BOOL resolved : 8; |
798 | | BOOL func_created : 8; |
799 | | BOOL instantiated : 8; |
800 | | BOOL evaluated : 8; |
801 | | BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ |
802 | | /* true if evaluation yielded an exception. It is saved in |
803 | | eval_exception */ |
804 | | BOOL eval_has_exception : 8; |
805 | | JSValue eval_exception; |
806 | | JSValue meta_obj; /* for import.meta */ |
807 | | }; |
808 | | |
809 | | typedef struct JSJobEntry { |
810 | | struct list_head link; |
811 | | JSContext *ctx; |
812 | | JSJobFunc *job_func; |
813 | | int argc; |
814 | | JSValue argv[0]; |
815 | | } JSJobEntry; |
816 | | |
817 | | typedef struct JSProperty { |
818 | | union { |
819 | | JSValue value; /* JS_PROP_NORMAL */ |
820 | | struct { /* JS_PROP_GETSET */ |
821 | | JSObject *getter; /* NULL if undefined */ |
822 | | JSObject *setter; /* NULL if undefined */ |
823 | | } getset; |
824 | | JSVarRef *var_ref; /* JS_PROP_VARREF */ |
825 | | struct { /* JS_PROP_AUTOINIT */ |
826 | | /* in order to use only 2 pointers, we compress the realm |
827 | | and the init function pointer */ |
828 | | uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) |
829 | | in the 2 low bits */ |
830 | | void *opaque; |
831 | | } init; |
832 | | } u; |
833 | | } JSProperty; |
834 | | |
835 | 562k | #define JS_PROP_INITIAL_SIZE 2 |
836 | 562k | #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ |
837 | | #define JS_ARRAY_INITIAL_SIZE 2 |
838 | | |
839 | | typedef struct JSShapeProperty { |
840 | | uint32_t hash_next : 26; /* 0 if last in list */ |
841 | | uint32_t flags : 6; /* JS_PROP_XXX */ |
842 | | JSAtom atom; /* JS_ATOM_NULL = free property entry */ |
843 | | } JSShapeProperty; |
844 | | |
845 | | struct JSShape { |
846 | | /* hash table of size hash_mask + 1 before the start of the |
847 | | structure (see prop_hash_end()). */ |
848 | | JSGCObjectHeader header; |
849 | | /* true if the shape is inserted in the shape hash table. If not, |
850 | | JSShape.hash is not valid */ |
851 | | uint8_t is_hashed; |
852 | | /* If true, the shape may have small array index properties 'n' with 0 |
853 | | <= n <= 2^31-1. If false, the shape is guaranteed not to have |
854 | | small array index properties */ |
855 | | uint8_t has_small_array_index; |
856 | | uint32_t hash; /* current hash value */ |
857 | | uint32_t prop_hash_mask; |
858 | | int prop_size; /* allocated properties */ |
859 | | int prop_count; /* include deleted properties */ |
860 | | int deleted_prop_count; |
861 | | JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ |
862 | | JSObject *proto; |
863 | | JSShapeProperty prop[0]; /* prop_size elements */ |
864 | | }; |
865 | | |
866 | | struct JSObject { |
867 | | union { |
868 | | JSGCObjectHeader header; |
869 | | struct { |
870 | | int __gc_ref_count; /* corresponds to header.ref_count */ |
871 | | uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ |
872 | | |
873 | | uint8_t extensible : 1; |
874 | | uint8_t free_mark : 1; /* only used when freeing objects with cycles */ |
875 | | uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ |
876 | | uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ |
877 | | uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ |
878 | | uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ |
879 | | uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ |
880 | | uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ |
881 | | uint16_t class_id; /* see JS_CLASS_x */ |
882 | | }; |
883 | | }; |
884 | | /* byte offsets: 16/24 */ |
885 | | JSShape *shape; /* prototype and property names + flag */ |
886 | | JSProperty *prop; /* array of properties */ |
887 | | /* byte offsets: 24/40 */ |
888 | | struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ |
889 | | /* byte offsets: 28/48 */ |
890 | | union { |
891 | | void *opaque; |
892 | | struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ |
893 | | struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ |
894 | | struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ |
895 | | struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ |
896 | | struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ |
897 | | #ifdef CONFIG_BIGNUM |
898 | | struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ |
899 | | struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ |
900 | | #endif |
901 | | struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ |
902 | | struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ |
903 | | struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ |
904 | | struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ |
905 | | struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ |
906 | | struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ |
907 | | struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ |
908 | | struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ |
909 | | struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ |
910 | | struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ |
911 | | struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ |
912 | | struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ |
913 | | /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ |
914 | | struct JSFunctionBytecode *function_bytecode; |
915 | | JSVarRef **var_refs; |
916 | | JSObject *home_object; /* for 'super' access */ |
917 | | } func; |
918 | | struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ |
919 | | JSContext *realm; |
920 | | JSCFunctionType c_function; |
921 | | uint8_t length; |
922 | | uint8_t cproto; |
923 | | int16_t magic; |
924 | | } cfunc; |
925 | | /* array part for fast arrays and typed arrays */ |
926 | | struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ |
927 | | union { |
928 | | uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ |
929 | | struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ |
930 | | } u1; |
931 | | union { |
932 | | JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ |
933 | | void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ |
934 | | int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ |
935 | | uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ |
936 | | int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ |
937 | | uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ |
938 | | int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ |
939 | | uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ |
940 | | int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ |
941 | | uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ |
942 | | float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ |
943 | | double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ |
944 | | } u; |
945 | | uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ |
946 | | } array; /* 12/20 bytes */ |
947 | | JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ |
948 | | JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ |
949 | | } u; |
950 | | /* byte sizes: 40/48/72 */ |
951 | | }; |
952 | | enum { |
953 | | __JS_ATOM_NULL = JS_ATOM_NULL, |
954 | | #define DEF(name, str) JS_ATOM_ ## name, |
955 | | #include "quickjs-atom.h" |
956 | | #undef DEF |
957 | | JS_ATOM_END, |
958 | | }; |
959 | 1.36M | #define JS_ATOM_LAST_KEYWORD JS_ATOM_super |
960 | 1.33M | #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield |
961 | | |
962 | | static const char js_atom_init[] = |
963 | | #define DEF(name, str) str "\0" |
964 | | #include "quickjs-atom.h" |
965 | | #undef DEF |
966 | | ; |
967 | | |
968 | | typedef enum OPCodeFormat { |
969 | | #define FMT(f) OP_FMT_ ## f, |
970 | | #define DEF(id, size, n_pop, n_push, f) |
971 | | #include "quickjs-opcode.h" |
972 | | #undef DEF |
973 | | #undef FMT |
974 | | } OPCodeFormat; |
975 | | |
976 | | enum OPCodeEnum { |
977 | | #define FMT(f) |
978 | | #define DEF(id, size, n_pop, n_push, f) OP_ ## id, |
979 | | #define def(id, size, n_pop, n_push, f) |
980 | | #include "quickjs-opcode.h" |
981 | | #undef def |
982 | | #undef DEF |
983 | | #undef FMT |
984 | | OP_COUNT, /* excluding temporary opcodes */ |
985 | | /* temporary opcodes : overlap with the short opcodes */ |
986 | | OP_TEMP_START = OP_nop + 1, |
987 | | OP___dummy = OP_TEMP_START - 1, |
988 | | #define FMT(f) |
989 | | #define DEF(id, size, n_pop, n_push, f) |
990 | | #define def(id, size, n_pop, n_push, f) OP_ ## id, |
991 | | #include "quickjs-opcode.h" |
992 | | #undef def |
993 | | #undef DEF |
994 | | #undef FMT |
995 | | OP_TEMP_END, |
996 | | }; |
997 | | |
998 | | static int JS_InitAtoms(JSRuntime *rt); |
999 | | static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, |
1000 | | int atom_type); |
1001 | | static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); |
1002 | | static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); |
1003 | | static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, |
1004 | | JSValueConst this_obj, |
1005 | | int argc, JSValueConst *argv, int flags); |
1006 | | static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, |
1007 | | JSValueConst this_obj, |
1008 | | int argc, JSValueConst *argv, int flags); |
1009 | | static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, |
1010 | | JSValueConst this_obj, JSValueConst new_target, |
1011 | | int argc, JSValue *argv, int flags); |
1012 | | static JSValue JS_CallConstructorInternal(JSContext *ctx, |
1013 | | JSValueConst func_obj, |
1014 | | JSValueConst new_target, |
1015 | | int argc, JSValue *argv, int flags); |
1016 | | static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, |
1017 | | int argc, JSValueConst *argv); |
1018 | | static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, |
1019 | | int argc, JSValueConst *argv); |
1020 | | static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, |
1021 | | JSValue val, BOOL is_array_ctor); |
1022 | | static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, |
1023 | | JSValueConst val, int flags, int scope_idx); |
1024 | | JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); |
1025 | | static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); |
1026 | | static __maybe_unused void JS_DumpString(JSRuntime *rt, |
1027 | | const JSString *p); |
1028 | | static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); |
1029 | | static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); |
1030 | | static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); |
1031 | | static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, |
1032 | | JSValueConst val); |
1033 | | static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); |
1034 | | static __maybe_unused void JS_PrintValue(JSContext *ctx, |
1035 | | const char *str, |
1036 | | JSValueConst val); |
1037 | | static __maybe_unused void JS_DumpShapes(JSRuntime *rt); |
1038 | | static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, |
1039 | | int argc, JSValueConst *argv, int magic); |
1040 | | static void js_array_finalizer(JSRuntime *rt, JSValue val); |
1041 | | static void js_array_mark(JSRuntime *rt, JSValueConst val, |
1042 | | JS_MarkFunc *mark_func); |
1043 | | static void js_object_data_finalizer(JSRuntime *rt, JSValue val); |
1044 | | static void js_object_data_mark(JSRuntime *rt, JSValueConst val, |
1045 | | JS_MarkFunc *mark_func); |
1046 | | static void js_c_function_finalizer(JSRuntime *rt, JSValue val); |
1047 | | static void js_c_function_mark(JSRuntime *rt, JSValueConst val, |
1048 | | JS_MarkFunc *mark_func); |
1049 | | static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); |
1050 | | static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, |
1051 | | JS_MarkFunc *mark_func); |
1052 | | static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); |
1053 | | static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, |
1054 | | JS_MarkFunc *mark_func); |
1055 | | static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); |
1056 | | static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, |
1057 | | JS_MarkFunc *mark_func); |
1058 | | static void js_regexp_finalizer(JSRuntime *rt, JSValue val); |
1059 | | static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); |
1060 | | static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); |
1061 | | static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, |
1062 | | JS_MarkFunc *mark_func); |
1063 | | static void js_proxy_finalizer(JSRuntime *rt, JSValue val); |
1064 | | static void js_proxy_mark(JSRuntime *rt, JSValueConst val, |
1065 | | JS_MarkFunc *mark_func); |
1066 | | static void js_map_finalizer(JSRuntime *rt, JSValue val); |
1067 | | static void js_map_mark(JSRuntime *rt, JSValueConst val, |
1068 | | JS_MarkFunc *mark_func); |
1069 | | static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); |
1070 | | static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, |
1071 | | JS_MarkFunc *mark_func); |
1072 | | static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); |
1073 | | static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, |
1074 | | JS_MarkFunc *mark_func); |
1075 | | static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); |
1076 | | static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, |
1077 | | JS_MarkFunc *mark_func); |
1078 | | static void js_generator_finalizer(JSRuntime *rt, JSValue obj); |
1079 | | static void js_generator_mark(JSRuntime *rt, JSValueConst val, |
1080 | | JS_MarkFunc *mark_func); |
1081 | | static void js_promise_finalizer(JSRuntime *rt, JSValue val); |
1082 | | static void js_promise_mark(JSRuntime *rt, JSValueConst val, |
1083 | | JS_MarkFunc *mark_func); |
1084 | | static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); |
1085 | | static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, |
1086 | | JS_MarkFunc *mark_func); |
1087 | | #ifdef CONFIG_BIGNUM |
1088 | | static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); |
1089 | | static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, |
1090 | | JS_MarkFunc *mark_func); |
1091 | | #endif |
1092 | | static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); |
1093 | | static int JS_ToBoolFree(JSContext *ctx, JSValue val); |
1094 | | static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); |
1095 | | static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); |
1096 | | static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); |
1097 | | static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, |
1098 | | JSValueConst flags); |
1099 | | static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, |
1100 | | JSValue pattern, JSValue bc); |
1101 | | static void gc_decref(JSRuntime *rt); |
1102 | | static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, |
1103 | | const JSClassDef *class_def, JSAtom name); |
1104 | | |
1105 | | typedef enum JSStrictEqModeEnum { |
1106 | | JS_EQ_STRICT, |
1107 | | JS_EQ_SAME_VALUE, |
1108 | | JS_EQ_SAME_VALUE_ZERO, |
1109 | | } JSStrictEqModeEnum; |
1110 | | |
1111 | | static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, |
1112 | | JSStrictEqModeEnum eq_mode); |
1113 | | static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); |
1114 | | static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); |
1115 | | static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); |
1116 | | static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); |
1117 | | static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); |
1118 | | static JSProperty *add_property(JSContext *ctx, |
1119 | | JSObject *p, JSAtom prop, int prop_flags); |
1120 | | #ifdef CONFIG_BIGNUM |
1121 | | static void js_float_env_finalizer(JSRuntime *rt, JSValue val); |
1122 | | static JSValue JS_NewBigFloat(JSContext *ctx); |
1123 | | static inline bf_t *JS_GetBigFloat(JSValueConst val) |
1124 | 0 | { |
1125 | 0 | JSBigFloat *p = JS_VALUE_GET_PTR(val); |
1126 | 0 | return &p->num; |
1127 | 0 | } |
1128 | | static JSValue JS_NewBigDecimal(JSContext *ctx); |
1129 | | static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) |
1130 | 0 | { |
1131 | 0 | JSBigDecimal *p = JS_VALUE_GET_PTR(val); |
1132 | 0 | return &p->num; |
1133 | 0 | } |
1134 | | static JSValue JS_NewBigInt(JSContext *ctx); |
1135 | | static inline bf_t *JS_GetBigInt(JSValueConst val) |
1136 | 1.99k | { |
1137 | 1.99k | JSBigFloat *p = JS_VALUE_GET_PTR(val); |
1138 | 1.99k | return &p->num; |
1139 | 1.99k | } |
1140 | | static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, |
1141 | | BOOL convert_to_safe_integer); |
1142 | | static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); |
1143 | | static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); |
1144 | | static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); |
1145 | | static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); |
1146 | | static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); |
1147 | | static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, |
1148 | | BOOL allow_null_or_undefined); |
1149 | | static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); |
1150 | | #endif |
1151 | | JSValue JS_ThrowOutOfMemory(JSContext *ctx); |
1152 | | static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); |
1153 | | static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); |
1154 | | static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, |
1155 | | JSValueConst proto_val, BOOL throw_flag); |
1156 | | static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); |
1157 | | static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); |
1158 | | static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); |
1159 | | static int JS_CreateProperty(JSContext *ctx, JSObject *p, |
1160 | | JSAtom prop, JSValueConst val, |
1161 | | JSValueConst getter, JSValueConst setter, |
1162 | | int flags); |
1163 | | static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); |
1164 | | static void reset_weak_ref(JSRuntime *rt, JSObject *p); |
1165 | | static JSValue js_array_buffer_constructor3(JSContext *ctx, |
1166 | | JSValueConst new_target, |
1167 | | uint64_t len, JSClassID class_id, |
1168 | | uint8_t *buf, |
1169 | | JSFreeArrayBufferDataFunc *free_func, |
1170 | | void *opaque, BOOL alloc_flag); |
1171 | | static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); |
1172 | | static JSValue js_typed_array_constructor(JSContext *ctx, |
1173 | | JSValueConst this_val, |
1174 | | int argc, JSValueConst *argv, |
1175 | | int classid); |
1176 | | static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); |
1177 | | static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); |
1178 | | static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); |
1179 | | static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, |
1180 | | BOOL is_arg); |
1181 | | static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, |
1182 | | JSValueConst this_obj, |
1183 | | int argc, JSValueConst *argv, |
1184 | | int flags); |
1185 | | static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); |
1186 | | static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, |
1187 | | JS_MarkFunc *mark_func); |
1188 | | static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, |
1189 | | const char *input, size_t input_len, |
1190 | | const char *filename, int flags, int scope_idx); |
1191 | | static void js_free_module_def(JSContext *ctx, JSModuleDef *m); |
1192 | | static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, |
1193 | | JS_MarkFunc *mark_func); |
1194 | | static JSValue js_import_meta(JSContext *ctx); |
1195 | | static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); |
1196 | | static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); |
1197 | | static JSValue js_new_promise_capability(JSContext *ctx, |
1198 | | JSValue *resolving_funcs, |
1199 | | JSValueConst ctor); |
1200 | | static __exception int perform_promise_then(JSContext *ctx, |
1201 | | JSValueConst promise, |
1202 | | JSValueConst *resolve_reject, |
1203 | | JSValueConst *cap_resolving_funcs); |
1204 | | static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, |
1205 | | int argc, JSValueConst *argv, int magic); |
1206 | | static int js_string_compare(JSContext *ctx, |
1207 | | const JSString *p1, const JSString *p2); |
1208 | | static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); |
1209 | | static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, |
1210 | | JSValue prop, JSValue val, int flags); |
1211 | | static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); |
1212 | | static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); |
1213 | | static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); |
1214 | | static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, |
1215 | | JSObject *p, JSAtom prop); |
1216 | | static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); |
1217 | | static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, |
1218 | | JS_MarkFunc *mark_func); |
1219 | | static void JS_AddIntrinsicBasicObjects(JSContext *ctx); |
1220 | | static void js_free_shape(JSRuntime *rt, JSShape *sh); |
1221 | | static void js_free_shape_null(JSRuntime *rt, JSShape *sh); |
1222 | | static int js_shape_prepare_update(JSContext *ctx, JSObject *p, |
1223 | | JSShapeProperty **pprs); |
1224 | | static int init_shape_hash(JSRuntime *rt); |
1225 | | static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, |
1226 | | JSValueConst obj); |
1227 | | static __exception int js_get_length64(JSContext *ctx, int64_t *pres, |
1228 | | JSValueConst obj); |
1229 | | static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); |
1230 | | static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, |
1231 | | JSValueConst array_arg); |
1232 | | static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, |
1233 | | JSValue **arrpp, uint32_t *countp); |
1234 | | static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, |
1235 | | JSValueConst sync_iter); |
1236 | | static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); |
1237 | | static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, |
1238 | | JS_MarkFunc *mark_func); |
1239 | | static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, |
1240 | | JSValueConst this_val, |
1241 | | int argc, JSValueConst *argv, int flags); |
1242 | | static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); |
1243 | | static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, |
1244 | | JSGCObjectTypeEnum type); |
1245 | | static void remove_gc_object(JSGCObjectHeader *h); |
1246 | | static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); |
1247 | | static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); |
1248 | | static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, |
1249 | | void *opaque); |
1250 | | static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, |
1251 | | JSAtom atom, void *opaque); |
1252 | | void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag); |
1253 | | |
1254 | | static const JSClassExoticMethods js_arguments_exotic_methods; |
1255 | | static const JSClassExoticMethods js_string_exotic_methods; |
1256 | | static const JSClassExoticMethods js_proxy_exotic_methods; |
1257 | | static const JSClassExoticMethods js_module_ns_exotic_methods; |
1258 | | static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; |
1259 | | |
1260 | | static void js_trigger_gc(JSRuntime *rt, size_t size) |
1261 | 1.34M | { |
1262 | 1.34M | BOOL force_gc; |
1263 | | #ifdef FORCE_GC_AT_MALLOC |
1264 | | force_gc = TRUE; |
1265 | | #else |
1266 | 1.34M | force_gc = ((rt->malloc_state.malloc_size + size) > |
1267 | 1.34M | rt->malloc_gc_threshold); |
1268 | 1.34M | #endif |
1269 | 1.34M | if (force_gc) { |
1270 | | #ifdef DUMP_GC |
1271 | | printf("GC: size=%" PRIu64 "\n", |
1272 | | (uint64_t)rt->malloc_state.malloc_size); |
1273 | | #endif |
1274 | 23 | JS_RunGC(rt); |
1275 | 23 | rt->malloc_gc_threshold = rt->malloc_state.malloc_size + |
1276 | 23 | (rt->malloc_state.malloc_size >> 1); |
1277 | 23 | } |
1278 | 1.34M | } |
1279 | | |
1280 | | static size_t js_malloc_usable_size_unknown(const void *ptr) |
1281 | 0 | { |
1282 | 0 | return 0; |
1283 | 0 | } |
1284 | | |
1285 | | void *js_malloc_rt(JSRuntime *rt, size_t size) |
1286 | 6.60M | { |
1287 | 6.60M | return rt->mf.js_malloc(&rt->malloc_state, size); |
1288 | 6.60M | } |
1289 | | |
1290 | | void js_free_rt(JSRuntime *rt, void *ptr) |
1291 | 6.16M | { |
1292 | 6.16M | rt->mf.js_free(&rt->malloc_state, ptr); |
1293 | 6.16M | } |
1294 | | |
1295 | | void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) |
1296 | 1.88M | { |
1297 | 1.88M | return rt->mf.js_realloc(&rt->malloc_state, ptr, size); |
1298 | 1.88M | } |
1299 | | |
1300 | | size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) |
1301 | 168k | { |
1302 | 168k | return rt->mf.js_malloc_usable_size(ptr); |
1303 | 168k | } |
1304 | | |
1305 | | void *js_mallocz_rt(JSRuntime *rt, size_t size) |
1306 | 432k | { |
1307 | 432k | void *ptr; |
1308 | 432k | ptr = js_malloc_rt(rt, size); |
1309 | 432k | if (!ptr) |
1310 | 2 | return NULL; |
1311 | 432k | return memset(ptr, 0, size); |
1312 | 432k | } |
1313 | | |
1314 | | #ifdef CONFIG_BIGNUM |
1315 | | /* called by libbf */ |
1316 | | static void *js_bf_realloc(void *opaque, void *ptr, size_t size) |
1317 | 9.73k | { |
1318 | 9.73k | JSRuntime *rt = opaque; |
1319 | 9.73k | return js_realloc_rt(rt, ptr, size); |
1320 | 9.73k | } |
1321 | | #endif /* CONFIG_BIGNUM */ |
1322 | | |
1323 | | /* Throw out of memory in case of error */ |
1324 | | void *js_malloc(JSContext *ctx, size_t size) |
1325 | 4.96M | { |
1326 | 4.96M | void *ptr; |
1327 | 4.96M | ptr = js_malloc_rt(ctx->rt, size); |
1328 | 4.96M | if (unlikely(!ptr)) { |
1329 | 12 | JS_ThrowOutOfMemory(ctx); |
1330 | 12 | return NULL; |
1331 | 12 | } |
1332 | 4.96M | return ptr; |
1333 | 4.96M | } |
1334 | | |
1335 | | /* Throw out of memory in case of error */ |
1336 | | void *js_mallocz(JSContext *ctx, size_t size) |
1337 | 432k | { |
1338 | 432k | void *ptr; |
1339 | 432k | ptr = js_mallocz_rt(ctx->rt, size); |
1340 | 432k | if (unlikely(!ptr)) { |
1341 | 2 | JS_ThrowOutOfMemory(ctx); |
1342 | 2 | return NULL; |
1343 | 2 | } |
1344 | 432k | return ptr; |
1345 | 432k | } |
1346 | | |
1347 | | void js_free(JSContext *ctx, void *ptr) |
1348 | 986k | { |
1349 | 986k | js_free_rt(ctx->rt, ptr); |
1350 | 986k | } |
1351 | | |
1352 | | /* Throw out of memory in case of error */ |
1353 | | void *js_realloc(JSContext *ctx, void *ptr, size_t size) |
1354 | 296k | { |
1355 | 296k | void *ret; |
1356 | 296k | ret = js_realloc_rt(ctx->rt, ptr, size); |
1357 | 296k | if (unlikely(!ret && size != 0)) { |
1358 | 0 | JS_ThrowOutOfMemory(ctx); |
1359 | 0 | return NULL; |
1360 | 0 | } |
1361 | 296k | return ret; |
1362 | 296k | } |
1363 | | |
1364 | | /* store extra allocated size in *pslack if successful */ |
1365 | | void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) |
1366 | 96.3k | { |
1367 | 96.3k | void *ret; |
1368 | 96.3k | ret = js_realloc_rt(ctx->rt, ptr, size); |
1369 | 96.3k | if (unlikely(!ret && size != 0)) { |
1370 | 6 | JS_ThrowOutOfMemory(ctx); |
1371 | 6 | return NULL; |
1372 | 6 | } |
1373 | 96.3k | if (pslack) { |
1374 | 96.3k | size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); |
1375 | 96.3k | *pslack = (new_size > size) ? new_size - size : 0; |
1376 | 96.3k | } |
1377 | 96.3k | return ret; |
1378 | 96.3k | } |
1379 | | |
1380 | | size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) |
1381 | 71.9k | { |
1382 | 71.9k | return js_malloc_usable_size_rt(ctx->rt, ptr); |
1383 | 71.9k | } |
1384 | | |
1385 | | /* Throw out of memory exception in case of error */ |
1386 | | char *js_strndup(JSContext *ctx, const char *s, size_t n) |
1387 | 12.6k | { |
1388 | 12.6k | char *ptr; |
1389 | 12.6k | ptr = js_malloc(ctx, n + 1); |
1390 | 12.6k | if (ptr) { |
1391 | 12.6k | memcpy(ptr, s, n); |
1392 | 12.6k | ptr[n] = '\0'; |
1393 | 12.6k | } |
1394 | 12.6k | return ptr; |
1395 | 12.6k | } |
1396 | | |
1397 | | char *js_strdup(JSContext *ctx, const char *str) |
1398 | 0 | { |
1399 | 0 | return js_strndup(ctx, str, strlen(str)); |
1400 | 0 | } |
1401 | | |
1402 | | static no_inline int js_realloc_array(JSContext *ctx, void **parray, |
1403 | | int elem_size, int *psize, int req_size) |
1404 | 93.0k | { |
1405 | 93.0k | int new_size; |
1406 | 93.0k | size_t slack; |
1407 | 93.0k | void *new_array; |
1408 | | /* XXX: potential arithmetic overflow */ |
1409 | 93.0k | new_size = max_int(req_size, *psize * 3 / 2); |
1410 | 93.0k | new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); |
1411 | 93.0k | if (!new_array) |
1412 | 6 | return -1; |
1413 | 93.0k | new_size += slack / elem_size; |
1414 | 93.0k | *psize = new_size; |
1415 | 93.0k | *parray = new_array; |
1416 | 93.0k | return 0; |
1417 | 93.0k | } |
1418 | | |
1419 | | /* resize the array and update its size if req_size > *psize */ |
1420 | | static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, |
1421 | | int *psize, int req_size) |
1422 | 2.56M | { |
1423 | 2.56M | if (unlikely(req_size > *psize)) |
1424 | 93.0k | return js_realloc_array(ctx, parray, elem_size, psize, req_size); |
1425 | 2.47M | else |
1426 | 2.47M | return 0; |
1427 | 2.56M | } |
1428 | | |
1429 | | static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) |
1430 | 366k | { |
1431 | 366k | dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); |
1432 | 366k | } |
1433 | | |
1434 | 738k | static inline int is_digit(int c) { |
1435 | 738k | return c >= '0' && c <= '9'; |
1436 | 738k | } |
1437 | | |
1438 | | typedef struct JSClassShortDef { |
1439 | | JSAtom class_name; |
1440 | | JSClassFinalizer *finalizer; |
1441 | | JSClassGCMark *gc_mark; |
1442 | | } JSClassShortDef; |
1443 | | |
1444 | | static JSClassShortDef const js_std_class_def[] = { |
1445 | | { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ |
1446 | | { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ |
1447 | | { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ |
1448 | | { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ |
1449 | | { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ |
1450 | | { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ |
1451 | | { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ |
1452 | | { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ |
1453 | | { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ |
1454 | | { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ |
1455 | | { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ |
1456 | | { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ |
1457 | | { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ |
1458 | | { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ |
1459 | | { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ |
1460 | | { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ |
1461 | | { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ |
1462 | | { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ |
1463 | | { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ |
1464 | | { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ |
1465 | | { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ |
1466 | | { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ |
1467 | | { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ |
1468 | | { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ |
1469 | | { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ |
1470 | | { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ |
1471 | | { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ |
1472 | | #ifdef CONFIG_BIGNUM |
1473 | | { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ |
1474 | | { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ |
1475 | | #endif |
1476 | | { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ |
1477 | | { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ |
1478 | | { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ |
1479 | | #ifdef CONFIG_BIGNUM |
1480 | | { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ |
1481 | | { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ |
1482 | | { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ |
1483 | | { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ |
1484 | | { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ |
1485 | | #endif |
1486 | | { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ |
1487 | | { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ |
1488 | | { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ |
1489 | | { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ |
1490 | | { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ |
1491 | | { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ |
1492 | | { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ |
1493 | | { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ |
1494 | | { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ |
1495 | | { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ |
1496 | | }; |
1497 | | |
1498 | | static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, |
1499 | | int start, int count) |
1500 | 7 | { |
1501 | 7 | JSClassDef cm_s, *cm = &cm_s; |
1502 | 7 | int i, class_id; |
1503 | | |
1504 | 168 | for(i = 0; i < count; i++) { |
1505 | 161 | class_id = i + start; |
1506 | 161 | memset(cm, 0, sizeof(*cm)); |
1507 | 161 | cm->finalizer = tab[i].finalizer; |
1508 | 161 | cm->gc_mark = tab[i].gc_mark; |
1509 | 161 | if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) |
1510 | 0 | return -1; |
1511 | 161 | } |
1512 | 7 | return 0; |
1513 | 7 | } |
1514 | | |
1515 | | #ifdef CONFIG_BIGNUM |
1516 | | static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx) |
1517 | 0 | { |
1518 | 0 | return JS_ThrowTypeError(ctx, "unsupported operation"); |
1519 | 0 | } |
1520 | | |
1521 | | static JSValue invalid_to_string(JSContext *ctx, JSValueConst val) |
1522 | 0 | { |
1523 | 0 | return JS_ThrowUnsupportedOperation(ctx); |
1524 | 0 | } |
1525 | | |
1526 | | static JSValue invalid_from_string(JSContext *ctx, const char *buf, |
1527 | | int radix, int flags, slimb_t *pexponent) |
1528 | 0 | { |
1529 | 0 | return JS_NAN; |
1530 | 0 | } |
1531 | | |
1532 | | static int invalid_unary_arith(JSContext *ctx, |
1533 | | JSValue *pres, OPCodeEnum op, JSValue op1) |
1534 | 0 | { |
1535 | 0 | JS_FreeValue(ctx, op1); |
1536 | 0 | JS_ThrowUnsupportedOperation(ctx); |
1537 | 0 | return -1; |
1538 | 0 | } |
1539 | | |
1540 | | static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op, |
1541 | | JSValue *pres, JSValue op1, JSValue op2) |
1542 | 0 | { |
1543 | 0 | JS_FreeValue(ctx, op1); |
1544 | 0 | JS_FreeValue(ctx, op2); |
1545 | 0 | JS_ThrowUnsupportedOperation(ctx); |
1546 | 0 | return -1; |
1547 | 0 | } |
1548 | | |
1549 | | static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, |
1550 | | int64_t exponent) |
1551 | 0 | { |
1552 | 0 | return JS_ThrowUnsupportedOperation(ctx); |
1553 | 0 | } |
1554 | | |
1555 | | static int invalid_mul_pow10(JSContext *ctx, JSValue *sp) |
1556 | 0 | { |
1557 | 0 | JS_ThrowUnsupportedOperation(ctx); |
1558 | 0 | return -1; |
1559 | 0 | } |
1560 | | |
1561 | | static void set_dummy_numeric_ops(JSNumericOperations *ops) |
1562 | 9 | { |
1563 | 9 | ops->to_string = invalid_to_string; |
1564 | 9 | ops->from_string = invalid_from_string; |
1565 | 9 | ops->unary_arith = invalid_unary_arith; |
1566 | 9 | ops->binary_arith = invalid_binary_arith; |
1567 | 9 | ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64; |
1568 | 9 | ops->mul_pow10 = invalid_mul_pow10; |
1569 | 9 | } |
1570 | | |
1571 | | #endif /* CONFIG_BIGNUM */ |
1572 | | |
1573 | | #if !defined(CONFIG_STACK_CHECK) |
1574 | | /* no stack limitation */ |
1575 | | static inline uintptr_t js_get_stack_pointer(void) |
1576 | | { |
1577 | | return 0; |
1578 | | } |
1579 | | |
1580 | | static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) |
1581 | | { |
1582 | | return FALSE; |
1583 | | } |
1584 | | #else |
1585 | | /* Note: OS and CPU dependent */ |
1586 | | static inline uintptr_t js_get_stack_pointer(void) |
1587 | 2.23M | { |
1588 | 2.23M | return (uintptr_t)__builtin_frame_address(0); |
1589 | 2.23M | } |
1590 | | |
1591 | | static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) |
1592 | 2.23M | { |
1593 | 2.23M | uintptr_t sp; |
1594 | 2.23M | sp = js_get_stack_pointer() - alloca_size; |
1595 | 2.23M | return unlikely(sp < rt->stack_limit); |
1596 | 2.23M | } |
1597 | | #endif |
1598 | | |
1599 | | JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) |
1600 | 3 | { |
1601 | 3 | JSRuntime *rt; |
1602 | 3 | JSMallocState ms; |
1603 | | |
1604 | 3 | memset(&ms, 0, sizeof(ms)); |
1605 | 3 | ms.opaque = opaque; |
1606 | 3 | ms.malloc_limit = -1; |
1607 | | |
1608 | 3 | rt = mf->js_malloc(&ms, sizeof(JSRuntime)); |
1609 | 3 | if (!rt) |
1610 | 0 | return NULL; |
1611 | 3 | memset(rt, 0, sizeof(*rt)); |
1612 | 3 | rt->mf = *mf; |
1613 | 3 | if (!rt->mf.js_malloc_usable_size) { |
1614 | | /* use dummy function if none provided */ |
1615 | 0 | rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; |
1616 | 0 | } |
1617 | 3 | rt->malloc_state = ms; |
1618 | 3 | rt->malloc_gc_threshold = 256 * 1024; |
1619 | | |
1620 | 3 | #ifdef CONFIG_BIGNUM |
1621 | 3 | bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); |
1622 | 3 | set_dummy_numeric_ops(&rt->bigint_ops); |
1623 | 3 | set_dummy_numeric_ops(&rt->bigfloat_ops); |
1624 | 3 | set_dummy_numeric_ops(&rt->bigdecimal_ops); |
1625 | 3 | #endif |
1626 | | |
1627 | 3 | init_list_head(&rt->context_list); |
1628 | 3 | init_list_head(&rt->gc_obj_list); |
1629 | 3 | init_list_head(&rt->gc_zero_ref_count_list); |
1630 | 3 | rt->gc_phase = JS_GC_PHASE_NONE; |
1631 | | |
1632 | | #ifdef DUMP_LEAKS |
1633 | | init_list_head(&rt->string_list); |
1634 | | #endif |
1635 | 3 | init_list_head(&rt->job_list); |
1636 | | |
1637 | 3 | if (JS_InitAtoms(rt)) |
1638 | 0 | goto fail; |
1639 | | |
1640 | | /* create the object, array and function classes */ |
1641 | 3 | if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, |
1642 | 3 | countof(js_std_class_def)) < 0) |
1643 | 0 | goto fail; |
1644 | 3 | rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; |
1645 | 3 | rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; |
1646 | 3 | rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; |
1647 | | |
1648 | 3 | rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; |
1649 | 3 | rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; |
1650 | 3 | rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; |
1651 | 3 | rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; |
1652 | 3 | if (init_shape_hash(rt)) |
1653 | 0 | goto fail; |
1654 | | |
1655 | 3 | rt->stack_size = JS_DEFAULT_STACK_SIZE; |
1656 | 3 | JS_UpdateStackTop(rt); |
1657 | | |
1658 | 3 | rt->current_exception = JS_NULL; |
1659 | | |
1660 | 3 | return rt; |
1661 | 0 | fail: |
1662 | 0 | JS_FreeRuntime(rt); |
1663 | 0 | return NULL; |
1664 | 3 | } |
1665 | | |
1666 | | void *JS_GetRuntimeOpaque(JSRuntime *rt) |
1667 | 0 | { |
1668 | 0 | return rt->user_opaque; |
1669 | 0 | } |
1670 | | |
1671 | | void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) |
1672 | 0 | { |
1673 | 0 | rt->user_opaque = opaque; |
1674 | 0 | } |
1675 | | |
1676 | | /* default memory allocation functions with memory limitation */ |
1677 | | static inline size_t js_def_malloc_usable_size(void *ptr) |
1678 | 15.2M | { |
1679 | | #if defined(__APPLE__) |
1680 | | return malloc_size(ptr); |
1681 | | #elif defined(_WIN32) |
1682 | | return _msize(ptr); |
1683 | | #elif defined(EMSCRIPTEN) |
1684 | | return 0; |
1685 | | #elif defined(__linux__) |
1686 | 15.2M | return malloc_usable_size(ptr); |
1687 | | #else |
1688 | | /* change this to `return 0;` if compilation fails */ |
1689 | | return malloc_usable_size(ptr); |
1690 | | #endif |
1691 | 15.2M | } |
1692 | | |
1693 | | static void *js_def_malloc(JSMallocState *s, size_t size) |
1694 | 6.98M | { |
1695 | 6.98M | void *ptr; |
1696 | | |
1697 | | /* Do not allocate zero bytes: behavior is platform dependent */ |
1698 | 6.98M | assert(size != 0); |
1699 | | |
1700 | 6.98M | if (unlikely(s->malloc_size + size > s->malloc_limit)) |
1701 | 38 | return NULL; |
1702 | | |
1703 | 6.98M | ptr = malloc(size); |
1704 | 6.98M | if (!ptr) |
1705 | 0 | return NULL; |
1706 | | |
1707 | 6.98M | s->malloc_count++; |
1708 | 6.98M | s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; |
1709 | 6.98M | return ptr; |
1710 | 6.98M | } |
1711 | | |
1712 | | static void js_def_free(JSMallocState *s, void *ptr) |
1713 | 6.16M | { |
1714 | 6.16M | if (!ptr) |
1715 | 539k | return; |
1716 | | |
1717 | 5.62M | s->malloc_count--; |
1718 | 5.62M | s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; |
1719 | 5.62M | free(ptr); |
1720 | 5.62M | } |
1721 | | |
1722 | | static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) |
1723 | 1.88M | { |
1724 | 1.88M | size_t old_size; |
1725 | | |
1726 | 1.88M | if (!ptr) { |
1727 | 430k | if (size == 0) |
1728 | 43.4k | return NULL; |
1729 | 387k | return js_def_malloc(s, size); |
1730 | 430k | } |
1731 | 1.45M | old_size = js_def_malloc_usable_size(ptr); |
1732 | 1.45M | if (size == 0) { |
1733 | 244k | s->malloc_count--; |
1734 | 244k | s->malloc_size -= old_size + MALLOC_OVERHEAD; |
1735 | 244k | free(ptr); |
1736 | 244k | return NULL; |
1737 | 244k | } |
1738 | 1.21M | if (s->malloc_size + size - old_size > s->malloc_limit) |
1739 | 324 | return NULL; |
1740 | | |
1741 | 1.20M | ptr = realloc(ptr, size); |
1742 | 1.20M | if (!ptr) |
1743 | 0 | return NULL; |
1744 | | |
1745 | 1.20M | s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; |
1746 | 1.20M | return ptr; |
1747 | 1.20M | } |
1748 | | |
1749 | | static const JSMallocFunctions def_malloc_funcs = { |
1750 | | js_def_malloc, |
1751 | | js_def_free, |
1752 | | js_def_realloc, |
1753 | | #if defined(__APPLE__) |
1754 | | malloc_size, |
1755 | | #elif defined(_WIN32) |
1756 | | (size_t (*)(const void *))_msize, |
1757 | | #elif defined(EMSCRIPTEN) |
1758 | | NULL, |
1759 | | #elif defined(__linux__) |
1760 | | (size_t (*)(const void *))malloc_usable_size, |
1761 | | #else |
1762 | | /* change this to `NULL,` if compilation fails */ |
1763 | | malloc_usable_size, |
1764 | | #endif |
1765 | | }; |
1766 | | |
1767 | | JSRuntime *JS_NewRuntime(void) |
1768 | 3 | { |
1769 | 3 | return JS_NewRuntime2(&def_malloc_funcs, NULL); |
1770 | 3 | } |
1771 | | |
1772 | | void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) |
1773 | 3 | { |
1774 | 3 | rt->malloc_state.malloc_limit = limit; |
1775 | 3 | } |
1776 | | |
1777 | | /* use -1 to disable automatic GC */ |
1778 | | void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) |
1779 | 0 | { |
1780 | 0 | rt->malloc_gc_threshold = gc_threshold; |
1781 | 0 | } |
1782 | | |
1783 | | #define malloc(s) malloc_is_forbidden(s) |
1784 | | #define free(p) free_is_forbidden(p) |
1785 | | #define realloc(p,s) realloc_is_forbidden(p,s) |
1786 | | |
1787 | | void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) |
1788 | 2 | { |
1789 | 2 | rt->interrupt_handler = cb; |
1790 | 2 | rt->interrupt_opaque = opaque; |
1791 | 2 | } |
1792 | | |
1793 | | void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) |
1794 | 0 | { |
1795 | 0 | rt->can_block = can_block; |
1796 | 0 | } |
1797 | | |
1798 | | void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, |
1799 | | const JSSharedArrayBufferFunctions *sf) |
1800 | 0 | { |
1801 | 0 | rt->sab_funcs = *sf; |
1802 | 0 | } |
1803 | | |
1804 | | /* return 0 if OK, < 0 if exception */ |
1805 | | int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, |
1806 | | int argc, JSValueConst *argv) |
1807 | 81.9k | { |
1808 | 81.9k | JSRuntime *rt = ctx->rt; |
1809 | 81.9k | JSJobEntry *e; |
1810 | 81.9k | int i; |
1811 | | |
1812 | 81.9k | e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); |
1813 | 81.9k | if (!e) |
1814 | 0 | return -1; |
1815 | 81.9k | e->ctx = ctx; |
1816 | 81.9k | e->job_func = job_func; |
1817 | 81.9k | e->argc = argc; |
1818 | 409k | for(i = 0; i < argc; i++) { |
1819 | 327k | e->argv[i] = JS_DupValue(ctx, argv[i]); |
1820 | 327k | } |
1821 | 81.9k | list_add_tail(&e->link, &rt->job_list); |
1822 | 81.9k | return 0; |
1823 | 81.9k | } |
1824 | | |
1825 | | BOOL JS_IsJobPending(JSRuntime *rt) |
1826 | 0 | { |
1827 | 0 | return !list_empty(&rt->job_list); |
1828 | 0 | } |
1829 | | |
1830 | | /* return < 0 if exception, 0 if no job pending, 1 if a job was |
1831 | | executed successfully. the context of the job is stored in '*pctx' */ |
1832 | | int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) |
1833 | 16 | { |
1834 | 16 | JSContext *ctx; |
1835 | 16 | JSJobEntry *e; |
1836 | 16 | JSValue res; |
1837 | 16 | int i, ret; |
1838 | | |
1839 | 16 | if (list_empty(&rt->job_list)) { |
1840 | 16 | *pctx = NULL; |
1841 | 16 | return 0; |
1842 | 16 | } |
1843 | | |
1844 | | /* get the first pending job and execute it */ |
1845 | 0 | e = list_entry(rt->job_list.next, JSJobEntry, link); |
1846 | 0 | list_del(&e->link); |
1847 | 0 | ctx = e->ctx; |
1848 | 0 | res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv); |
1849 | 0 | for(i = 0; i < e->argc; i++) |
1850 | 0 | JS_FreeValue(ctx, e->argv[i]); |
1851 | 0 | if (JS_IsException(res)) |
1852 | 0 | ret = -1; |
1853 | 0 | else |
1854 | 0 | ret = 1; |
1855 | 0 | JS_FreeValue(ctx, res); |
1856 | 0 | js_free(ctx, e); |
1857 | 0 | *pctx = ctx; |
1858 | 0 | return ret; |
1859 | 16 | } |
1860 | | |
1861 | | static inline uint32_t atom_get_free(const JSAtomStruct *p) |
1862 | 86.4k | { |
1863 | 86.4k | return (uintptr_t)p >> 1; |
1864 | 86.4k | } |
1865 | | |
1866 | | static inline BOOL atom_is_free(const JSAtomStruct *p) |
1867 | 122k | { |
1868 | 122k | return (uintptr_t)p & 1; |
1869 | 122k | } |
1870 | | |
1871 | | static inline JSAtomStruct *atom_set_free(uint32_t v) |
1872 | 87.0k | { |
1873 | 87.0k | return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); |
1874 | 87.0k | } |
1875 | | |
1876 | | /* Note: the string contents are uninitialized */ |
1877 | | static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) |
1878 | 1.16M | { |
1879 | 1.16M | JSString *str; |
1880 | 1.16M | str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); |
1881 | 1.16M | if (unlikely(!str)) |
1882 | 0 | return NULL; |
1883 | 1.16M | str->header.ref_count = 1; |
1884 | 1.16M | str->is_wide_char = is_wide_char; |
1885 | 1.16M | str->len = max_len; |
1886 | 1.16M | str->atom_type = 0; |
1887 | 1.16M | str->hash = 0; /* optional but costless */ |
1888 | 1.16M | str->hash_next = 0; /* optional */ |
1889 | | #ifdef DUMP_LEAKS |
1890 | | list_add_tail(&str->link, &rt->string_list); |
1891 | | #endif |
1892 | 1.16M | return str; |
1893 | 1.16M | } |
1894 | | |
1895 | | static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) |
1896 | 1.16M | { |
1897 | 1.16M | JSString *p; |
1898 | 1.16M | p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); |
1899 | 1.16M | if (unlikely(!p)) { |
1900 | 0 | JS_ThrowOutOfMemory(ctx); |
1901 | 0 | return NULL; |
1902 | 0 | } |
1903 | 1.16M | return p; |
1904 | 1.16M | } |
1905 | | |
1906 | | /* same as JS_FreeValueRT() but faster */ |
1907 | | static inline void js_free_string(JSRuntime *rt, JSString *str) |
1908 | 91.4k | { |
1909 | 91.4k | if (--str->header.ref_count <= 0) { |
1910 | 1.76k | if (str->atom_type) { |
1911 | 0 | JS_FreeAtomStruct(rt, str); |
1912 | 1.76k | } else { |
1913 | | #ifdef DUMP_LEAKS |
1914 | | list_del(&str->link); |
1915 | | #endif |
1916 | 1.76k | js_free_rt(rt, str); |
1917 | 1.76k | } |
1918 | 1.76k | } |
1919 | 91.4k | } |
1920 | | |
1921 | | void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) |
1922 | 0 | { |
1923 | 0 | if (rt) |
1924 | 0 | rt->rt_info = s; |
1925 | 0 | } |
1926 | | |
1927 | | void JS_FreeRuntime(JSRuntime *rt) |
1928 | 0 | { |
1929 | 0 | struct list_head *el, *el1; |
1930 | 0 | int i; |
1931 | |
|
1932 | 0 | JS_FreeValueRT(rt, rt->current_exception); |
1933 | |
|
1934 | 0 | list_for_each_safe(el, el1, &rt->job_list) { |
1935 | 0 | JSJobEntry *e = list_entry(el, JSJobEntry, link); |
1936 | 0 | for(i = 0; i < e->argc; i++) |
1937 | 0 | JS_FreeValueRT(rt, e->argv[i]); |
1938 | 0 | js_free_rt(rt, e); |
1939 | 0 | } |
1940 | 0 | init_list_head(&rt->job_list); |
1941 | |
|
1942 | 0 | JS_RunGC(rt); |
1943 | |
|
1944 | | #ifdef DUMP_LEAKS |
1945 | | /* leaking objects */ |
1946 | | { |
1947 | | BOOL header_done; |
1948 | | JSGCObjectHeader *p; |
1949 | | int count; |
1950 | | |
1951 | | /* remove the internal refcounts to display only the object |
1952 | | referenced externally */ |
1953 | | list_for_each(el, &rt->gc_obj_list) { |
1954 | | p = list_entry(el, JSGCObjectHeader, link); |
1955 | | p->mark = 0; |
1956 | | } |
1957 | | gc_decref(rt); |
1958 | | |
1959 | | header_done = FALSE; |
1960 | | list_for_each(el, &rt->gc_obj_list) { |
1961 | | p = list_entry(el, JSGCObjectHeader, link); |
1962 | | if (p->ref_count != 0) { |
1963 | | if (!header_done) { |
1964 | | printf("Object leaks:\n"); |
1965 | | JS_DumpObjectHeader(rt); |
1966 | | header_done = TRUE; |
1967 | | } |
1968 | | JS_DumpGCObject(rt, p); |
1969 | | } |
1970 | | } |
1971 | | |
1972 | | count = 0; |
1973 | | list_for_each(el, &rt->gc_obj_list) { |
1974 | | p = list_entry(el, JSGCObjectHeader, link); |
1975 | | if (p->ref_count == 0) { |
1976 | | count++; |
1977 | | } |
1978 | | } |
1979 | | if (count != 0) |
1980 | | printf("Secondary object leaks: %d\n", count); |
1981 | | } |
1982 | | #endif |
1983 | 0 | assert(list_empty(&rt->gc_obj_list)); |
1984 | | |
1985 | | /* free the classes */ |
1986 | 0 | for(i = 0; i < rt->class_count; i++) { |
1987 | 0 | JSClass *cl = &rt->class_array[i]; |
1988 | 0 | if (cl->class_id != 0) { |
1989 | 0 | JS_FreeAtomRT(rt, cl->class_name); |
1990 | 0 | } |
1991 | 0 | } |
1992 | 0 | js_free_rt(rt, rt->class_array); |
1993 | |
|
1994 | 0 | #ifdef CONFIG_BIGNUM |
1995 | 0 | bf_context_end(&rt->bf_ctx); |
1996 | 0 | #endif |
1997 | |
|
1998 | | #ifdef DUMP_LEAKS |
1999 | | /* only the atoms defined in JS_InitAtoms() should be left */ |
2000 | | { |
2001 | | BOOL header_done = FALSE; |
2002 | | |
2003 | | for(i = 0; i < rt->atom_size; i++) { |
2004 | | JSAtomStruct *p = rt->atom_array[i]; |
2005 | | if (!atom_is_free(p) /* && p->str*/) { |
2006 | | if (i >= JS_ATOM_END || p->header.ref_count != 1) { |
2007 | | if (!header_done) { |
2008 | | header_done = TRUE; |
2009 | | if (rt->rt_info) { |
2010 | | printf("%s:1: atom leakage:", rt->rt_info); |
2011 | | } else { |
2012 | | printf("Atom leaks:\n" |
2013 | | " %6s %6s %s\n", |
2014 | | "ID", "REFCNT", "NAME"); |
2015 | | } |
2016 | | } |
2017 | | if (rt->rt_info) { |
2018 | | printf(" "); |
2019 | | } else { |
2020 | | printf(" %6u %6u ", i, p->header.ref_count); |
2021 | | } |
2022 | | switch (p->atom_type) { |
2023 | | case JS_ATOM_TYPE_STRING: |
2024 | | JS_DumpString(rt, p); |
2025 | | break; |
2026 | | case JS_ATOM_TYPE_GLOBAL_SYMBOL: |
2027 | | printf("Symbol.for("); |
2028 | | JS_DumpString(rt, p); |
2029 | | printf(")"); |
2030 | | break; |
2031 | | case JS_ATOM_TYPE_SYMBOL: |
2032 | | if (p->hash == JS_ATOM_HASH_SYMBOL) { |
2033 | | printf("Symbol("); |
2034 | | JS_DumpString(rt, p); |
2035 | | printf(")"); |
2036 | | } else { |
2037 | | printf("Private("); |
2038 | | JS_DumpString(rt, p); |
2039 | | printf(")"); |
2040 | | } |
2041 | | break; |
2042 | | } |
2043 | | if (rt->rt_info) { |
2044 | | printf(":%u", p->header.ref_count); |
2045 | | } else { |
2046 | | printf("\n"); |
2047 | | } |
2048 | | } |
2049 | | } |
2050 | | } |
2051 | | if (rt->rt_info && header_done) |
2052 | | printf("\n"); |
2053 | | } |
2054 | | #endif |
2055 | | |
2056 | | /* free the atoms */ |
2057 | 0 | for(i = 0; i < rt->atom_size; i++) { |
2058 | 0 | JSAtomStruct *p = rt->atom_array[i]; |
2059 | 0 | if (!atom_is_free(p)) { |
2060 | | #ifdef DUMP_LEAKS |
2061 | | list_del(&p->link); |
2062 | | #endif |
2063 | 0 | js_free_rt(rt, p); |
2064 | 0 | } |
2065 | 0 | } |
2066 | 0 | js_free_rt(rt, rt->atom_array); |
2067 | 0 | js_free_rt(rt, rt->atom_hash); |
2068 | 0 | js_free_rt(rt, rt->shape_hash); |
2069 | | #ifdef DUMP_LEAKS |
2070 | | if (!list_empty(&rt->string_list)) { |
2071 | | if (rt->rt_info) { |
2072 | | printf("%s:1: string leakage:", rt->rt_info); |
2073 | | } else { |
2074 | | printf("String leaks:\n" |
2075 | | " %6s %s\n", |
2076 | | "REFCNT", "VALUE"); |
2077 | | } |
2078 | | list_for_each_safe(el, el1, &rt->string_list) { |
2079 | | JSString *str = list_entry(el, JSString, link); |
2080 | | if (rt->rt_info) { |
2081 | | printf(" "); |
2082 | | } else { |
2083 | | printf(" %6u ", str->header.ref_count); |
2084 | | } |
2085 | | JS_DumpString(rt, str); |
2086 | | if (rt->rt_info) { |
2087 | | printf(":%u", str->header.ref_count); |
2088 | | } else { |
2089 | | printf("\n"); |
2090 | | } |
2091 | | list_del(&str->link); |
2092 | | js_free_rt(rt, str); |
2093 | | } |
2094 | | if (rt->rt_info) |
2095 | | printf("\n"); |
2096 | | } |
2097 | | { |
2098 | | JSMallocState *s = &rt->malloc_state; |
2099 | | if (s->malloc_count > 1) { |
2100 | | if (rt->rt_info) |
2101 | | printf("%s:1: ", rt->rt_info); |
2102 | | printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", |
2103 | | (uint64_t)(s->malloc_size - sizeof(JSRuntime)), |
2104 | | (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); |
2105 | | } |
2106 | | } |
2107 | | #endif |
2108 | |
|
2109 | 0 | { |
2110 | 0 | JSMallocState ms = rt->malloc_state; |
2111 | 0 | rt->mf.js_free(&ms, rt); |
2112 | 0 | } |
2113 | 0 | } |
2114 | | |
2115 | | JSContext *JS_NewContextRaw(JSRuntime *rt) |
2116 | 3 | { |
2117 | 3 | JSContext *ctx; |
2118 | 3 | int i; |
2119 | | |
2120 | 3 | ctx = js_mallocz_rt(rt, sizeof(JSContext)); |
2121 | 3 | if (!ctx) |
2122 | 0 | return NULL; |
2123 | 3 | ctx->header.ref_count = 1; |
2124 | 3 | add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); |
2125 | | |
2126 | 3 | ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * |
2127 | 3 | rt->class_count); |
2128 | 3 | if (!ctx->class_proto) { |
2129 | 0 | js_free_rt(rt, ctx); |
2130 | 0 | return NULL; |
2131 | 0 | } |
2132 | 3 | ctx->rt = rt; |
2133 | 3 | list_add_tail(&ctx->link, &rt->context_list); |
2134 | 3 | #ifdef CONFIG_BIGNUM |
2135 | 3 | ctx->bf_ctx = &rt->bf_ctx; |
2136 | 3 | ctx->fp_env.prec = 113; |
2137 | 3 | ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; |
2138 | 3 | #endif |
2139 | 177 | for(i = 0; i < rt->class_count; i++) |
2140 | 174 | ctx->class_proto[i] = JS_NULL; |
2141 | 3 | ctx->array_ctor = JS_NULL; |
2142 | 3 | ctx->regexp_ctor = JS_NULL; |
2143 | 3 | ctx->promise_ctor = JS_NULL; |
2144 | 3 | init_list_head(&ctx->loaded_modules); |
2145 | | |
2146 | 3 | JS_AddIntrinsicBasicObjects(ctx); |
2147 | 3 | return ctx; |
2148 | 3 | } |
2149 | | |
2150 | | JSContext *JS_NewContext(JSRuntime *rt) |
2151 | 0 | { |
2152 | 0 | JSContext *ctx; |
2153 | |
|
2154 | 0 | ctx = JS_NewContextRaw(rt); |
2155 | 0 | if (!ctx) |
2156 | 0 | return NULL; |
2157 | | |
2158 | 0 | JS_AddIntrinsicBaseObjects(ctx); |
2159 | 0 | JS_AddIntrinsicDate(ctx); |
2160 | 0 | JS_AddIntrinsicEval(ctx); |
2161 | 0 | JS_AddIntrinsicStringNormalize(ctx); |
2162 | 0 | JS_AddIntrinsicRegExp(ctx); |
2163 | 0 | JS_AddIntrinsicJSON(ctx); |
2164 | 0 | JS_AddIntrinsicProxy(ctx); |
2165 | 0 | JS_AddIntrinsicMapSet(ctx); |
2166 | 0 | JS_AddIntrinsicTypedArrays(ctx); |
2167 | 0 | JS_AddIntrinsicPromise(ctx); |
2168 | 0 | #ifdef CONFIG_BIGNUM |
2169 | 0 | JS_AddIntrinsicBigInt(ctx); |
2170 | 0 | #endif |
2171 | 0 | return ctx; |
2172 | 0 | } |
2173 | | |
2174 | | void *JS_GetContextOpaque(JSContext *ctx) |
2175 | 0 | { |
2176 | 0 | return ctx->user_opaque; |
2177 | 0 | } |
2178 | | |
2179 | | void JS_SetContextOpaque(JSContext *ctx, void *opaque) |
2180 | 0 | { |
2181 | 0 | ctx->user_opaque = opaque; |
2182 | 0 | } |
2183 | | |
2184 | | /* set the new value and free the old value after (freeing the value |
2185 | | can reallocate the object data) */ |
2186 | | static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) |
2187 | 2.18M | { |
2188 | 2.18M | JSValue old_val; |
2189 | 2.18M | old_val = *pval; |
2190 | 2.18M | *pval = new_val; |
2191 | 2.18M | JS_FreeValue(ctx, old_val); |
2192 | 2.18M | } |
2193 | | |
2194 | | void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) |
2195 | 0 | { |
2196 | 0 | JSRuntime *rt = ctx->rt; |
2197 | 0 | assert(class_id < rt->class_count); |
2198 | 0 | set_value(ctx, &ctx->class_proto[class_id], obj); |
2199 | 0 | } |
2200 | | |
2201 | | JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) |
2202 | 0 | { |
2203 | 0 | JSRuntime *rt = ctx->rt; |
2204 | 0 | assert(class_id < rt->class_count); |
2205 | 0 | return JS_DupValue(ctx, ctx->class_proto[class_id]); |
2206 | 0 | } |
2207 | | |
2208 | | typedef enum JSFreeModuleEnum { |
2209 | | JS_FREE_MODULE_ALL, |
2210 | | JS_FREE_MODULE_NOT_RESOLVED, |
2211 | | JS_FREE_MODULE_NOT_EVALUATED, |
2212 | | } JSFreeModuleEnum; |
2213 | | |
2214 | | /* XXX: would be more efficient with separate module lists */ |
2215 | | static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) |
2216 | 2 | { |
2217 | 2 | struct list_head *el, *el1; |
2218 | 5 | list_for_each_safe(el, el1, &ctx->loaded_modules) { |
2219 | 5 | JSModuleDef *m = list_entry(el, JSModuleDef, link); |
2220 | 5 | if (flag == JS_FREE_MODULE_ALL || |
2221 | 5 | (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) || |
2222 | 5 | (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) { |
2223 | 2 | js_free_module_def(ctx, m); |
2224 | 2 | } |
2225 | 5 | } |
2226 | 2 | } |
2227 | | |
2228 | | JSContext *JS_DupContext(JSContext *ctx) |
2229 | 47.3k | { |
2230 | 47.3k | ctx->header.ref_count++; |
2231 | 47.3k | return ctx; |
2232 | 47.3k | } |
2233 | | |
2234 | | /* used by the GC */ |
2235 | | static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, |
2236 | | JS_MarkFunc *mark_func) |
2237 | 46 | { |
2238 | 46 | int i; |
2239 | 46 | struct list_head *el; |
2240 | | |
2241 | | /* modules are not seen by the GC, so we directly mark the objects |
2242 | | referenced by each module */ |
2243 | 100 | list_for_each(el, &ctx->loaded_modules) { |
2244 | 100 | JSModuleDef *m = list_entry(el, JSModuleDef, link); |
2245 | 100 | js_mark_module_def(rt, m, mark_func); |
2246 | 100 | } |
2247 | | |
2248 | 46 | JS_MarkValue(rt, ctx->global_obj, mark_func); |
2249 | 46 | JS_MarkValue(rt, ctx->global_var_obj, mark_func); |
2250 | | |
2251 | 46 | JS_MarkValue(rt, ctx->throw_type_error, mark_func); |
2252 | 46 | JS_MarkValue(rt, ctx->eval_obj, mark_func); |
2253 | | |
2254 | 46 | JS_MarkValue(rt, ctx->array_proto_values, mark_func); |
2255 | 414 | for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { |
2256 | 368 | JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); |
2257 | 368 | } |
2258 | 2.71k | for(i = 0; i < rt->class_count; i++) { |
2259 | 2.66k | JS_MarkValue(rt, ctx->class_proto[i], mark_func); |
2260 | 2.66k | } |
2261 | 46 | JS_MarkValue(rt, ctx->iterator_proto, mark_func); |
2262 | 46 | JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); |
2263 | 46 | JS_MarkValue(rt, ctx->promise_ctor, mark_func); |
2264 | 46 | JS_MarkValue(rt, ctx->array_ctor, mark_func); |
2265 | 46 | JS_MarkValue(rt, ctx->regexp_ctor, mark_func); |
2266 | 46 | JS_MarkValue(rt, ctx->function_ctor, mark_func); |
2267 | 46 | JS_MarkValue(rt, ctx->function_proto, mark_func); |
2268 | | |
2269 | 46 | if (ctx->array_shape) |
2270 | 46 | mark_func(rt, &ctx->array_shape->header); |
2271 | 46 | } |
2272 | | |
2273 | | void JS_FreeContext(JSContext *ctx) |
2274 | 46.4k | { |
2275 | 46.4k | JSRuntime *rt = ctx->rt; |
2276 | 46.4k | int i; |
2277 | | |
2278 | 46.4k | if (--ctx->header.ref_count > 0) |
2279 | 46.4k | return; |
2280 | 0 | assert(ctx->header.ref_count == 0); |
2281 | | |
2282 | | #ifdef DUMP_ATOMS |
2283 | | JS_DumpAtoms(ctx->rt); |
2284 | | #endif |
2285 | | #ifdef DUMP_SHAPES |
2286 | | JS_DumpShapes(ctx->rt); |
2287 | | #endif |
2288 | | #ifdef DUMP_OBJECTS |
2289 | | { |
2290 | | struct list_head *el; |
2291 | | JSGCObjectHeader *p; |
2292 | | printf("JSObjects: {\n"); |
2293 | | JS_DumpObjectHeader(ctx->rt); |
2294 | | list_for_each(el, &rt->gc_obj_list) { |
2295 | | p = list_entry(el, JSGCObjectHeader, link); |
2296 | | JS_DumpGCObject(rt, p); |
2297 | | } |
2298 | | printf("}\n"); |
2299 | | } |
2300 | | #endif |
2301 | | #ifdef DUMP_MEM |
2302 | | { |
2303 | | JSMemoryUsage stats; |
2304 | | JS_ComputeMemoryUsage(rt, &stats); |
2305 | | JS_DumpMemoryUsage(stdout, &stats, rt); |
2306 | | } |
2307 | | #endif |
2308 | | |
2309 | 0 | js_free_modules(ctx, JS_FREE_MODULE_ALL); |
2310 | |
|
2311 | 0 | JS_FreeValue(ctx, ctx->global_obj); |
2312 | 0 | JS_FreeValue(ctx, ctx->global_var_obj); |
2313 | |
|
2314 | 0 | JS_FreeValue(ctx, ctx->throw_type_error); |
2315 | 0 | JS_FreeValue(ctx, ctx->eval_obj); |
2316 | |
|
2317 | 0 | JS_FreeValue(ctx, ctx->array_proto_values); |
2318 | 0 | for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { |
2319 | 0 | JS_FreeValue(ctx, ctx->native_error_proto[i]); |
2320 | 0 | } |
2321 | 0 | for(i = 0; i < rt->class_count; i++) { |
2322 | 0 | JS_FreeValue(ctx, ctx->class_proto[i]); |
2323 | 0 | } |
2324 | 0 | js_free_rt(rt, ctx->class_proto); |
2325 | 0 | JS_FreeValue(ctx, ctx->iterator_proto); |
2326 | 0 | JS_FreeValue(ctx, ctx->async_iterator_proto); |
2327 | 0 | JS_FreeValue(ctx, ctx->promise_ctor); |
2328 | 0 | JS_FreeValue(ctx, ctx->array_ctor); |
2329 | 0 | JS_FreeValue(ctx, ctx->regexp_ctor); |
2330 | 0 | JS_FreeValue(ctx, ctx->function_ctor); |
2331 | 0 | JS_FreeValue(ctx, ctx->function_proto); |
2332 | |
|
2333 | 0 | js_free_shape_null(ctx->rt, ctx->array_shape); |
2334 | |
|
2335 | 0 | list_del(&ctx->link); |
2336 | 0 | remove_gc_object(&ctx->header); |
2337 | 0 | js_free_rt(ctx->rt, ctx); |
2338 | 0 | } |
2339 | | |
2340 | | JSRuntime *JS_GetRuntime(JSContext *ctx) |
2341 | 18 | { |
2342 | 18 | return ctx->rt; |
2343 | 18 | } |
2344 | | |
2345 | | static void update_stack_limit(JSRuntime *rt) |
2346 | 3 | { |
2347 | 3 | if (rt->stack_size == 0) { |
2348 | 0 | rt->stack_limit = 0; /* no limit */ |
2349 | 3 | } else { |
2350 | 3 | rt->stack_limit = rt->stack_top - rt->stack_size; |
2351 | 3 | } |
2352 | 3 | } |
2353 | | |
2354 | | void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) |
2355 | 0 | { |
2356 | 0 | rt->stack_size = stack_size; |
2357 | 0 | update_stack_limit(rt); |
2358 | 0 | } |
2359 | | |
2360 | | void JS_UpdateStackTop(JSRuntime *rt) |
2361 | 3 | { |
2362 | 3 | rt->stack_top = js_get_stack_pointer(); |
2363 | 3 | update_stack_limit(rt); |
2364 | 3 | } |
2365 | | |
2366 | | static inline BOOL is_strict_mode(JSContext *ctx) |
2367 | 1.12k | { |
2368 | 1.12k | JSStackFrame *sf = ctx->rt->current_stack_frame; |
2369 | 1.12k | return (sf && (sf->js_mode & JS_MODE_STRICT)); |
2370 | 1.12k | } |
2371 | | |
2372 | | #ifdef CONFIG_BIGNUM |
2373 | | static inline BOOL is_math_mode(JSContext *ctx) |
2374 | 194k | { |
2375 | 194k | JSStackFrame *sf = ctx->rt->current_stack_frame; |
2376 | 194k | return (sf && (sf->js_mode & JS_MODE_MATH)); |
2377 | 194k | } |
2378 | | #endif |
2379 | | |
2380 | | /* JSAtom support */ |
2381 | | |
2382 | 85.5M | #define JS_ATOM_TAG_INT (1U << 31) |
2383 | 153k | #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) |
2384 | 17 | #define JS_ATOM_MAX ((1U << 30) - 1) |
2385 | | |
2386 | | /* return the max count from the hash size */ |
2387 | 8 | #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) |
2388 | | |
2389 | | static inline BOOL __JS_AtomIsConst(JSAtom v) |
2390 | 20.9M | { |
2391 | | #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 |
2392 | | return (int32_t)v <= 0; |
2393 | | #else |
2394 | 20.9M | return (int32_t)v < JS_ATOM_END; |
2395 | 20.9M | #endif |
2396 | 20.9M | } |
2397 | | |
2398 | | static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) |
2399 | 46.0M | { |
2400 | 46.0M | return (v & JS_ATOM_TAG_INT) != 0; |
2401 | 46.0M | } |
2402 | | |
2403 | | static inline JSAtom __JS_AtomFromUInt32(uint32_t v) |
2404 | 497k | { |
2405 | 497k | return v | JS_ATOM_TAG_INT; |
2406 | 497k | } |
2407 | | |
2408 | | static inline uint32_t __JS_AtomToUInt32(JSAtom atom) |
2409 | 38.8M | { |
2410 | 38.8M | return atom & ~JS_ATOM_TAG_INT; |
2411 | 38.8M | } |
2412 | | |
2413 | | static inline int is_num(int c) |
2414 | 46.7k | { |
2415 | 46.7k | return c >= '0' && c <= '9'; |
2416 | 46.7k | } |
2417 | | |
2418 | | /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ |
2419 | | static inline BOOL is_num_string(uint32_t *pval, const JSString *p) |
2420 | 95.4k | { |
2421 | 95.4k | uint32_t n; |
2422 | 95.4k | uint64_t n64; |
2423 | 95.4k | int c, i, len; |
2424 | | |
2425 | 95.4k | len = p->len; |
2426 | 95.4k | if (len == 0 || len > 10) |
2427 | 48.7k | return FALSE; |
2428 | 46.7k | if (p->is_wide_char) |
2429 | 1.70k | c = p->u.str16[0]; |
2430 | 45.0k | else |
2431 | 45.0k | c = p->u.str8[0]; |
2432 | 46.7k | if (is_num(c)) { |
2433 | 68 | if (c == '0') { |
2434 | 10 | if (len != 1) |
2435 | 8 | return FALSE; |
2436 | 2 | n = 0; |
2437 | 58 | } else { |
2438 | 58 | n = c - '0'; |
2439 | 104 | for(i = 1; i < len; i++) { |
2440 | 64 | if (p->is_wide_char) |
2441 | 0 | c = p->u.str16[i]; |
2442 | 64 | else |
2443 | 64 | c = p->u.str8[i]; |
2444 | 64 | if (!is_num(c)) |
2445 | 18 | return FALSE; |
2446 | 46 | n64 = (uint64_t)n * 10 + (c - '0'); |
2447 | 46 | if ((n64 >> 32) != 0) |
2448 | 0 | return FALSE; |
2449 | 46 | n = n64; |
2450 | 46 | } |
2451 | 58 | } |
2452 | 42 | *pval = n; |
2453 | 42 | return TRUE; |
2454 | 46.6k | } else { |
2455 | 46.6k | return FALSE; |
2456 | 46.6k | } |
2457 | 46.7k | } |
2458 | | |
2459 | | /* XXX: could use faster version ? */ |
2460 | | static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) |
2461 | 830k | { |
2462 | 830k | size_t i; |
2463 | | |
2464 | 4.92M | for(i = 0; i < len; i++) |
2465 | 4.09M | h = h * 263 + str[i]; |
2466 | 830k | return h; |
2467 | 830k | } |
2468 | | |
2469 | | static inline uint32_t hash_string16(const uint16_t *str, |
2470 | | size_t len, uint32_t h) |
2471 | 1.72k | { |
2472 | 1.72k | size_t i; |
2473 | | |
2474 | 1.02M | for(i = 0; i < len; i++) |
2475 | 1.02M | h = h * 263 + str[i]; |
2476 | 1.72k | return h; |
2477 | 1.72k | } |
2478 | | |
2479 | | static uint32_t hash_string(const JSString *str, uint32_t h) |
2480 | 95.0k | { |
2481 | 95.0k | if (str->is_wide_char) |
2482 | 1.72k | h = hash_string16(str->u.str16, str->len, h); |
2483 | 93.3k | else |
2484 | 93.3k | h = hash_string8(str->u.str8, str->len, h); |
2485 | 95.0k | return h; |
2486 | 95.0k | } |
2487 | | |
2488 | | static __maybe_unused void JS_DumpString(JSRuntime *rt, |
2489 | | const JSString *p) |
2490 | 0 | { |
2491 | 0 | int i, c, sep; |
2492 | 0 |
|
2493 | 0 | if (p == NULL) { |
2494 | 0 | printf("<null>"); |
2495 | 0 | return; |
2496 | 0 | } |
2497 | 0 | printf("%d", p->header.ref_count); |
2498 | 0 | sep = (p->header.ref_count == 1) ? '\"' : '\''; |
2499 | 0 | putchar(sep); |
2500 | 0 | for(i = 0; i < p->len; i++) { |
2501 | 0 | if (p->is_wide_char) |
2502 | 0 | c = p->u.str16[i]; |
2503 | 0 | else |
2504 | 0 | c = p->u.str8[i]; |
2505 | 0 | if (c == sep || c == '\\') { |
2506 | 0 | putchar('\\'); |
2507 | 0 | putchar(c); |
2508 | 0 | } else if (c >= ' ' && c <= 126) { |
2509 | 0 | putchar(c); |
2510 | 0 | } else if (c == '\n') { |
2511 | 0 | putchar('\\'); |
2512 | 0 | putchar('n'); |
2513 | 0 | } else { |
2514 | 0 | printf("\\u%04x", c); |
2515 | 0 | } |
2516 | 0 | } |
2517 | 0 | putchar(sep); |
2518 | 0 | } |
2519 | | |
2520 | | static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) |
2521 | 0 | { |
2522 | 0 | JSAtomStruct *p; |
2523 | 0 | int h, i; |
2524 | 0 | /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ |
2525 | 0 | printf("JSAtom count=%d size=%d hash_size=%d:\n", |
2526 | 0 | rt->atom_count, rt->atom_size, rt->atom_hash_size); |
2527 | 0 | printf("JSAtom hash table: {\n"); |
2528 | 0 | for(i = 0; i < rt->atom_hash_size; i++) { |
2529 | 0 | h = rt->atom_hash[i]; |
2530 | 0 | if (h) { |
2531 | 0 | printf(" %d:", i); |
2532 | 0 | while (h) { |
2533 | 0 | p = rt->atom_array[h]; |
2534 | 0 | printf(" "); |
2535 | 0 | JS_DumpString(rt, p); |
2536 | 0 | h = p->hash_next; |
2537 | 0 | } |
2538 | 0 | printf("\n"); |
2539 | 0 | } |
2540 | 0 | } |
2541 | 0 | printf("}\n"); |
2542 | 0 | printf("JSAtom table: {\n"); |
2543 | 0 | for(i = 0; i < rt->atom_size; i++) { |
2544 | 0 | p = rt->atom_array[i]; |
2545 | 0 | if (!atom_is_free(p)) { |
2546 | 0 | printf(" %d: { %d %08x ", i, p->atom_type, p->hash); |
2547 | 0 | if (!(p->len == 0 && p->is_wide_char != 0)) |
2548 | 0 | JS_DumpString(rt, p); |
2549 | 0 | printf(" %d }\n", p->hash_next); |
2550 | 0 | } |
2551 | 0 | } |
2552 | 0 | printf("}\n"); |
2553 | 0 | } |
2554 | | |
2555 | | static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) |
2556 | 8 | { |
2557 | 8 | JSAtomStruct *p; |
2558 | 8 | uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; |
2559 | | |
2560 | 8 | assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ |
2561 | 8 | new_hash_mask = new_hash_size - 1; |
2562 | 8 | new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); |
2563 | 8 | if (!new_hash) |
2564 | 0 | return -1; |
2565 | 4.10k | for(i = 0; i < rt->atom_hash_size; i++) { |
2566 | 4.09k | h = rt->atom_hash[i]; |
2567 | 6.66k | while (h != 0) { |
2568 | 2.57k | p = rt->atom_array[h]; |
2569 | 2.57k | hash_next1 = p->hash_next; |
2570 | | /* add in new hash table */ |
2571 | 2.57k | j = p->hash & new_hash_mask; |
2572 | 2.57k | p->hash_next = new_hash[j]; |
2573 | 2.57k | new_hash[j] = h; |
2574 | 2.57k | h = hash_next1; |
2575 | 2.57k | } |
2576 | 4.09k | } |
2577 | 8 | js_free_rt(rt, rt->atom_hash); |
2578 | 8 | rt->atom_hash = new_hash; |
2579 | 8 | rt->atom_hash_size = new_hash_size; |
2580 | 8 | rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); |
2581 | | // JS_DumpAtoms(rt); |
2582 | 8 | return 0; |
2583 | 8 | } |
2584 | | |
2585 | | static int JS_InitAtoms(JSRuntime *rt) |
2586 | 3 | { |
2587 | 3 | int i, len, atom_type; |
2588 | 3 | const char *p; |
2589 | | |
2590 | 3 | rt->atom_hash_size = 0; |
2591 | 3 | rt->atom_hash = NULL; |
2592 | 3 | rt->atom_count = 0; |
2593 | 3 | rt->atom_size = 0; |
2594 | 3 | rt->atom_free_index = 0; |
2595 | 3 | if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ |
2596 | 0 | return -1; |
2597 | | |
2598 | 3 | p = js_atom_init; |
2599 | 675 | for(i = 1; i < JS_ATOM_END; i++) { |
2600 | 672 | if (i == JS_ATOM_Private_brand) |
2601 | 3 | atom_type = JS_ATOM_TYPE_PRIVATE; |
2602 | 669 | else if (i >= JS_ATOM_Symbol_toPrimitive) |
2603 | 42 | atom_type = JS_ATOM_TYPE_SYMBOL; |
2604 | 627 | else |
2605 | 627 | atom_type = JS_ATOM_TYPE_STRING; |
2606 | 672 | len = strlen(p); |
2607 | 672 | if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) |
2608 | 0 | return -1; |
2609 | 672 | p = p + len + 1; |
2610 | 672 | } |
2611 | 3 | return 0; |
2612 | 3 | } |
2613 | | |
2614 | | static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) |
2615 | 161 | { |
2616 | 161 | JSAtomStruct *p; |
2617 | | |
2618 | 161 | if (!__JS_AtomIsConst(v)) { |
2619 | 0 | p = rt->atom_array[v]; |
2620 | 0 | p->header.ref_count++; |
2621 | 0 | } |
2622 | 161 | return v; |
2623 | 161 | } |
2624 | | |
2625 | | JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) |
2626 | 9.42M | { |
2627 | 9.42M | JSRuntime *rt; |
2628 | 9.42M | JSAtomStruct *p; |
2629 | | |
2630 | 9.42M | if (!__JS_AtomIsConst(v)) { |
2631 | 4.15M | rt = ctx->rt; |
2632 | 4.15M | p = rt->atom_array[v]; |
2633 | 4.15M | p->header.ref_count++; |
2634 | 4.15M | } |
2635 | 9.42M | return v; |
2636 | 9.42M | } |
2637 | | |
2638 | | static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) |
2639 | 2.51M | { |
2640 | 2.51M | JSRuntime *rt; |
2641 | 2.51M | JSAtomStruct *p; |
2642 | | |
2643 | 2.51M | rt = ctx->rt; |
2644 | 2.51M | if (__JS_AtomIsTaggedInt(v)) |
2645 | 2.51M | return JS_ATOM_KIND_STRING; |
2646 | 402 | p = rt->atom_array[v]; |
2647 | 402 | switch(p->atom_type) { |
2648 | 396 | case JS_ATOM_TYPE_STRING: |
2649 | 396 | return JS_ATOM_KIND_STRING; |
2650 | 0 | case JS_ATOM_TYPE_GLOBAL_SYMBOL: |
2651 | 0 | return JS_ATOM_KIND_SYMBOL; |
2652 | 6 | case JS_ATOM_TYPE_SYMBOL: |
2653 | 6 | switch(p->hash) { |
2654 | 6 | case JS_ATOM_HASH_SYMBOL: |
2655 | 6 | return JS_ATOM_KIND_SYMBOL; |
2656 | 0 | case JS_ATOM_HASH_PRIVATE: |
2657 | 0 | return JS_ATOM_KIND_PRIVATE; |
2658 | 0 | default: |
2659 | 0 | abort(); |
2660 | 6 | } |
2661 | 0 | default: |
2662 | 0 | abort(); |
2663 | 402 | } |
2664 | 402 | } |
2665 | | |
2666 | | static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) |
2667 | 0 | { |
2668 | 0 | return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; |
2669 | 0 | } |
2670 | | |
2671 | | static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) |
2672 | 680 | { |
2673 | 680 | uint32_t i = p->hash_next; /* atom_index */ |
2674 | 680 | if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { |
2675 | 678 | JSAtomStruct *p1; |
2676 | | |
2677 | 678 | i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; |
2678 | 678 | p1 = rt->atom_array[i]; |
2679 | 691 | while (p1 != p) { |
2680 | 13 | assert(i != 0); |
2681 | 13 | i = p1->hash_next; |
2682 | 13 | p1 = rt->atom_array[i]; |
2683 | 13 | } |
2684 | 678 | } |
2685 | 680 | return i; |
2686 | 680 | } |
2687 | | |
2688 | | /* string case (internal). Return JS_ATOM_NULL if error. 'str' is |
2689 | | freed. */ |
2690 | | static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) |
2691 | 137k | { |
2692 | 137k | uint32_t h, h1, i; |
2693 | 137k | JSAtomStruct *p; |
2694 | 137k | int len; |
2695 | | |
2696 | | #if 0 |
2697 | | printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); |
2698 | | #endif |
2699 | 137k | if (atom_type < JS_ATOM_TYPE_SYMBOL) { |
2700 | | /* str is not NULL */ |
2701 | 95.7k | if (str->atom_type == atom_type) { |
2702 | | /* str is the atom, return its index */ |
2703 | 678 | i = js_get_atom_index(rt, str); |
2704 | | /* reduce string refcount and increase atom's unless constant */ |
2705 | 678 | if (__JS_AtomIsConst(i)) |
2706 | 639 | str->header.ref_count--; |
2707 | 678 | return i; |
2708 | 678 | } |
2709 | | /* try and locate an already registered atom */ |
2710 | 95.0k | len = str->len; |
2711 | 95.0k | h = hash_string(str, atom_type); |
2712 | 95.0k | h &= JS_ATOM_HASH_MASK; |
2713 | 95.0k | h1 = h & (rt->atom_hash_size - 1); |
2714 | 95.0k | i = rt->atom_hash[h1]; |
2715 | 108k | while (i != 0) { |
2716 | 63.1k | p = rt->atom_array[i]; |
2717 | 63.1k | if (p->hash == h && |
2718 | 63.1k | p->atom_type == atom_type && |
2719 | 63.1k | p->len == len && |
2720 | 63.1k | js_string_memcmp(p, str, len) == 0) { |
2721 | 50.0k | if (!__JS_AtomIsConst(i)) |
2722 | 50.0k | p->header.ref_count++; |
2723 | 50.0k | goto done; |
2724 | 50.0k | } |
2725 | 13.0k | i = p->hash_next; |
2726 | 13.0k | } |
2727 | 95.0k | } else { |
2728 | 41.3k | h1 = 0; /* avoid warning */ |
2729 | 41.3k | if (atom_type == JS_ATOM_TYPE_SYMBOL) { |
2730 | 42 | h = JS_ATOM_HASH_SYMBOL; |
2731 | 41.3k | } else { |
2732 | 41.3k | h = JS_ATOM_HASH_PRIVATE; |
2733 | 41.3k | atom_type = JS_ATOM_TYPE_SYMBOL; |
2734 | 41.3k | } |
2735 | 41.3k | } |
2736 | | |
2737 | 86.4k | if (rt->atom_free_index == 0) { |
2738 | | /* allow new atom entries */ |
2739 | 17 | uint32_t new_size, start; |
2740 | 17 | JSAtomStruct **new_array; |
2741 | | |
2742 | | /* alloc new with size progression 3/2: |
2743 | | 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 |
2744 | | preallocating space for predefined atoms (at least 195). |
2745 | | */ |
2746 | 17 | new_size = max_int(211, rt->atom_size * 3 / 2); |
2747 | 17 | if (new_size > JS_ATOM_MAX) |
2748 | 0 | goto fail; |
2749 | | /* XXX: should use realloc2 to use slack space */ |
2750 | 17 | new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); |
2751 | 17 | if (!new_array) |
2752 | 0 | goto fail; |
2753 | | /* Note: the atom 0 is not used */ |
2754 | 17 | start = rt->atom_size; |
2755 | 17 | if (start == 0) { |
2756 | | /* JS_ATOM_NULL entry */ |
2757 | 3 | p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); |
2758 | 3 | if (!p) { |
2759 | 0 | js_free_rt(rt, new_array); |
2760 | 0 | goto fail; |
2761 | 0 | } |
2762 | 3 | p->header.ref_count = 1; /* not refcounted */ |
2763 | 3 | p->atom_type = JS_ATOM_TYPE_SYMBOL; |
2764 | | #ifdef DUMP_LEAKS |
2765 | | list_add_tail(&p->link, &rt->string_list); |
2766 | | #endif |
2767 | 3 | new_array[0] = p; |
2768 | 3 | rt->atom_count++; |
2769 | 3 | start = 1; |
2770 | 3 | } |
2771 | 17 | rt->atom_size = new_size; |
2772 | 17 | rt->atom_array = new_array; |
2773 | 17 | rt->atom_free_index = start; |
2774 | 9.48k | for(i = start; i < new_size; i++) { |
2775 | 9.47k | uint32_t next; |
2776 | 9.47k | if (i == (new_size - 1)) |
2777 | 17 | next = 0; |
2778 | 9.45k | else |
2779 | 9.45k | next = i + 1; |
2780 | 9.47k | rt->atom_array[i] = atom_set_free(next); |
2781 | 9.47k | } |
2782 | 17 | } |
2783 | | |
2784 | 86.4k | if (str) { |
2785 | 86.4k | if (str->atom_type == 0) { |
2786 | 45.0k | p = str; |
2787 | 45.0k | p->atom_type = atom_type; |
2788 | 45.0k | } else { |
2789 | 41.3k | p = js_malloc_rt(rt, sizeof(JSString) + |
2790 | 41.3k | (str->len << str->is_wide_char) + |
2791 | 41.3k | 1 - str->is_wide_char); |
2792 | 41.3k | if (unlikely(!p)) |
2793 | 0 | goto fail; |
2794 | 41.3k | p->header.ref_count = 1; |
2795 | 41.3k | p->is_wide_char = str->is_wide_char; |
2796 | 41.3k | p->len = str->len; |
2797 | | #ifdef DUMP_LEAKS |
2798 | | list_add_tail(&p->link, &rt->string_list); |
2799 | | #endif |
2800 | 41.3k | memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + |
2801 | 41.3k | 1 - str->is_wide_char); |
2802 | 41.3k | js_free_string(rt, str); |
2803 | 41.3k | } |
2804 | 86.4k | } else { |
2805 | 0 | p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ |
2806 | 0 | if (!p) |
2807 | 0 | return JS_ATOM_NULL; |
2808 | 0 | p->header.ref_count = 1; |
2809 | 0 | p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ |
2810 | 0 | p->len = 0; |
2811 | | #ifdef DUMP_LEAKS |
2812 | | list_add_tail(&p->link, &rt->string_list); |
2813 | | #endif |
2814 | 0 | } |
2815 | | |
2816 | | /* use an already free entry */ |
2817 | 86.4k | i = rt->atom_free_index; |
2818 | 86.4k | rt->atom_free_index = atom_get_free(rt->atom_array[i]); |
2819 | 86.4k | rt->atom_array[i] = p; |
2820 | | |
2821 | 86.4k | p->hash = h; |
2822 | 86.4k | p->hash_next = i; /* atom_index */ |
2823 | 86.4k | p->atom_type = atom_type; |
2824 | | |
2825 | 86.4k | rt->atom_count++; |
2826 | | |
2827 | 86.4k | if (atom_type != JS_ATOM_TYPE_SYMBOL) { |
2828 | 45.0k | p->hash_next = rt->atom_hash[h1]; |
2829 | 45.0k | rt->atom_hash[h1] = i; |
2830 | 45.0k | if (unlikely(rt->atom_count >= rt->atom_count_resize)) |
2831 | 5 | JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); |
2832 | 45.0k | } |
2833 | | |
2834 | | // JS_DumpAtoms(rt); |
2835 | 86.4k | return i; |
2836 | | |
2837 | 0 | fail: |
2838 | 0 | i = JS_ATOM_NULL; |
2839 | 50.0k | done: |
2840 | 50.0k | if (str) |
2841 | 50.0k | js_free_string(rt, str); |
2842 | 50.0k | return i; |
2843 | 0 | } |
2844 | | |
2845 | | /* only works with zero terminated 8 bit strings */ |
2846 | | static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, |
2847 | | int atom_type) |
2848 | 672 | { |
2849 | 672 | JSString *p; |
2850 | 672 | p = js_alloc_string_rt(rt, len, 0); |
2851 | 672 | if (!p) |
2852 | 0 | return JS_ATOM_NULL; |
2853 | 672 | memcpy(p->u.str8, str, len); |
2854 | 672 | p->u.str8[len] = '\0'; |
2855 | 672 | return __JS_NewAtom(rt, p, atom_type); |
2856 | 672 | } |
2857 | | |
2858 | | static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, |
2859 | | int atom_type) |
2860 | 736k | { |
2861 | 736k | uint32_t h, h1, i; |
2862 | 736k | JSAtomStruct *p; |
2863 | | |
2864 | 736k | h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); |
2865 | 736k | h &= JS_ATOM_HASH_MASK; |
2866 | 736k | h1 = h & (rt->atom_hash_size - 1); |
2867 | 736k | i = rt->atom_hash[h1]; |
2868 | 869k | while (i != 0) { |
2869 | 823k | p = rt->atom_array[i]; |
2870 | 823k | if (p->hash == h && |
2871 | 823k | p->atom_type == JS_ATOM_TYPE_STRING && |
2872 | 823k | p->len == len && |
2873 | 823k | p->is_wide_char == 0 && |
2874 | 823k | memcmp(p->u.str8, str, len) == 0) { |
2875 | 691k | if (!__JS_AtomIsConst(i)) |
2876 | 476k | p->header.ref_count++; |
2877 | 691k | return i; |
2878 | 691k | } |
2879 | 132k | i = p->hash_next; |
2880 | 132k | } |
2881 | 45.8k | return JS_ATOM_NULL; |
2882 | 736k | } |
2883 | | |
2884 | | static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) |
2885 | 77.5k | { |
2886 | | #if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ |
2887 | | if (unlikely(i == JS_ATOM_NULL)) { |
2888 | | p->header.ref_count = INT32_MAX / 2; |
2889 | | return; |
2890 | | } |
2891 | | #endif |
2892 | 77.5k | uint32_t i = p->hash_next; /* atom_index */ |
2893 | 77.5k | if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { |
2894 | 43.7k | JSAtomStruct *p0, *p1; |
2895 | 43.7k | uint32_t h0; |
2896 | | |
2897 | 43.7k | h0 = p->hash & (rt->atom_hash_size - 1); |
2898 | 43.7k | i = rt->atom_hash[h0]; |
2899 | 43.7k | p1 = rt->atom_array[i]; |
2900 | 43.7k | if (p1 == p) { |
2901 | 43.6k | rt->atom_hash[h0] = p1->hash_next; |
2902 | 43.6k | } else { |
2903 | 81 | for(;;) { |
2904 | 81 | assert(i != 0); |
2905 | 81 | p0 = p1; |
2906 | 81 | i = p1->hash_next; |
2907 | 81 | p1 = rt->atom_array[i]; |
2908 | 81 | if (p1 == p) { |
2909 | 62 | p0->hash_next = p1->hash_next; |
2910 | 62 | break; |
2911 | 62 | } |
2912 | 81 | } |
2913 | 62 | } |
2914 | 43.7k | } |
2915 | | /* insert in free atom list */ |
2916 | 77.5k | rt->atom_array[i] = atom_set_free(rt->atom_free_index); |
2917 | 77.5k | rt->atom_free_index = i; |
2918 | | /* free the string structure */ |
2919 | | #ifdef DUMP_LEAKS |
2920 | | list_del(&p->link); |
2921 | | #endif |
2922 | 77.5k | js_free_rt(rt, p); |
2923 | 77.5k | rt->atom_count--; |
2924 | 77.5k | assert(rt->atom_count >= 0); |
2925 | 77.5k | } |
2926 | | |
2927 | | static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) |
2928 | 4.72M | { |
2929 | 4.72M | JSAtomStruct *p; |
2930 | | |
2931 | 4.72M | p = rt->atom_array[i]; |
2932 | 4.72M | if (--p->header.ref_count > 0) |
2933 | 4.67M | return; |
2934 | 43.7k | JS_FreeAtomStruct(rt, p); |
2935 | 43.7k | } |
2936 | | |
2937 | | /* Warning: 'p' is freed */ |
2938 | | static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) |
2939 | 95.1k | { |
2940 | 95.1k | JSRuntime *rt = ctx->rt; |
2941 | 95.1k | uint32_t n; |
2942 | 95.1k | if (is_num_string(&n, p)) { |
2943 | 42 | if (n <= JS_ATOM_MAX_INT) { |
2944 | 40 | js_free_string(rt, p); |
2945 | 40 | return __JS_AtomFromUInt32(n); |
2946 | 40 | } |
2947 | 42 | } |
2948 | | /* XXX: should generate an exception */ |
2949 | 95.1k | return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); |
2950 | 95.1k | } |
2951 | | |
2952 | | JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) |
2953 | 736k | { |
2954 | 736k | JSValue val; |
2955 | | |
2956 | 736k | if (len == 0 || !is_digit(*str)) { |
2957 | 736k | JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); |
2958 | 736k | if (atom) |
2959 | 691k | return atom; |
2960 | 736k | } |
2961 | 45.8k | val = JS_NewStringLen(ctx, str, len); |
2962 | 45.8k | if (JS_IsException(val)) |
2963 | 0 | return JS_ATOM_NULL; |
2964 | 45.8k | return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); |
2965 | 45.8k | } |
2966 | | |
2967 | | JSAtom JS_NewAtom(JSContext *ctx, const char *str) |
2968 | 54.8k | { |
2969 | 54.8k | return JS_NewAtomLen(ctx, str, strlen(str)); |
2970 | 54.8k | } |
2971 | | |
2972 | | JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) |
2973 | 0 | { |
2974 | 0 | if (n <= JS_ATOM_MAX_INT) { |
2975 | 0 | return __JS_AtomFromUInt32(n); |
2976 | 0 | } else { |
2977 | 0 | char buf[11]; |
2978 | 0 | JSValue val; |
2979 | 0 | snprintf(buf, sizeof(buf), "%u", n); |
2980 | 0 | val = JS_NewString(ctx, buf); |
2981 | 0 | if (JS_IsException(val)) |
2982 | 0 | return JS_ATOM_NULL; |
2983 | 0 | return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), |
2984 | 0 | JS_ATOM_TYPE_STRING); |
2985 | 0 | } |
2986 | 0 | } |
2987 | | |
2988 | | static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) |
2989 | 0 | { |
2990 | 0 | if ((uint64_t)n <= JS_ATOM_MAX_INT) { |
2991 | 0 | return __JS_AtomFromUInt32((uint32_t)n); |
2992 | 0 | } else { |
2993 | 0 | char buf[24]; |
2994 | 0 | JSValue val; |
2995 | 0 | snprintf(buf, sizeof(buf), "%" PRId64 , n); |
2996 | 0 | val = JS_NewString(ctx, buf); |
2997 | 0 | if (JS_IsException(val)) |
2998 | 0 | return JS_ATOM_NULL; |
2999 | 0 | return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), |
3000 | 0 | JS_ATOM_TYPE_STRING); |
3001 | 0 | } |
3002 | 0 | } |
3003 | | |
3004 | | /* 'p' is freed */ |
3005 | | static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) |
3006 | 41.3k | { |
3007 | 41.3k | JSRuntime *rt = ctx->rt; |
3008 | 41.3k | JSAtom atom; |
3009 | 41.3k | atom = __JS_NewAtom(rt, p, atom_type); |
3010 | 41.3k | if (atom == JS_ATOM_NULL) |
3011 | 0 | return JS_ThrowOutOfMemory(ctx); |
3012 | 41.3k | return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); |
3013 | 41.3k | } |
3014 | | |
3015 | | /* descr must be a non-numeric string atom */ |
3016 | | static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, |
3017 | | int atom_type) |
3018 | 41.3k | { |
3019 | 41.3k | JSRuntime *rt = ctx->rt; |
3020 | 41.3k | JSString *p; |
3021 | | |
3022 | 41.3k | assert(!__JS_AtomIsTaggedInt(descr)); |
3023 | 41.3k | assert(descr < rt->atom_size); |
3024 | 41.3k | p = rt->atom_array[descr]; |
3025 | 41.3k | JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); |
3026 | 41.3k | return JS_NewSymbol(ctx, p, atom_type); |
3027 | 41.3k | } |
3028 | | |
3029 | | #define ATOM_GET_STR_BUF_SIZE 64 |
3030 | | |
3031 | | /* Should only be used for debug. */ |
3032 | | static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, |
3033 | | JSAtom atom) |
3034 | 122k | { |
3035 | 122k | if (__JS_AtomIsTaggedInt(atom)) { |
3036 | 0 | snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); |
3037 | 122k | } else { |
3038 | 122k | JSAtomStruct *p; |
3039 | 122k | assert(atom < rt->atom_size); |
3040 | 122k | if (atom == JS_ATOM_NULL) { |
3041 | 0 | snprintf(buf, buf_size, "<null>"); |
3042 | 122k | } else { |
3043 | 122k | int i, c; |
3044 | 122k | char *q; |
3045 | 122k | JSString *str; |
3046 | | |
3047 | 122k | q = buf; |
3048 | 122k | p = rt->atom_array[atom]; |
3049 | 122k | assert(!atom_is_free(p)); |
3050 | 122k | str = p; |
3051 | 122k | if (str) { |
3052 | 122k | if (!str->is_wide_char) { |
3053 | | /* special case ASCII strings */ |
3054 | 122k | c = 0; |
3055 | 1.10M | for(i = 0; i < str->len; i++) { |
3056 | 984k | c |= str->u.str8[i]; |
3057 | 984k | } |
3058 | 122k | if (c < 0x80) |
3059 | 122k | return (const char *)str->u.str8; |
3060 | 122k | } |
3061 | 0 | for(i = 0; i < str->len; i++) { |
3062 | 0 | if (str->is_wide_char) |
3063 | 0 | c = str->u.str16[i]; |
3064 | 0 | else |
3065 | 0 | c = str->u.str8[i]; |
3066 | 0 | if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) |
3067 | 0 | break; |
3068 | 0 | if (c < 128) { |
3069 | 0 | *q++ = c; |
3070 | 0 | } else { |
3071 | 0 | q += unicode_to_utf8((uint8_t *)q, c); |
3072 | 0 | } |
3073 | 0 | } |
3074 | 0 | } |
3075 | 0 | *q = '\0'; |
3076 | 0 | } |
3077 | 122k | } |
3078 | 0 | return buf; |
3079 | 122k | } |
3080 | | |
3081 | | static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) |
3082 | 122k | { |
3083 | 122k | return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); |
3084 | 122k | } |
3085 | | |
3086 | | static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) |
3087 | 1.79M | { |
3088 | 1.79M | char buf[ATOM_GET_STR_BUF_SIZE]; |
3089 | | |
3090 | 1.79M | if (__JS_AtomIsTaggedInt(atom)) { |
3091 | 30 | snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); |
3092 | 30 | return JS_NewString(ctx, buf); |
3093 | 1.79M | } else { |
3094 | 1.79M | JSRuntime *rt = ctx->rt; |
3095 | 1.79M | JSAtomStruct *p; |
3096 | 1.79M | assert(atom < rt->atom_size); |
3097 | 1.79M | p = rt->atom_array[atom]; |
3098 | 1.79M | if (p->atom_type == JS_ATOM_TYPE_STRING) { |
3099 | 1.79M | goto ret_string; |
3100 | 1.79M | } else if (force_string) { |
3101 | 0 | if (p->len == 0 && p->is_wide_char != 0) { |
3102 | | /* no description string */ |
3103 | 0 | p = rt->atom_array[JS_ATOM_empty_string]; |
3104 | 0 | } |
3105 | 1.79M | ret_string: |
3106 | 1.79M | return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); |
3107 | 28 | } else { |
3108 | 28 | return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p)); |
3109 | 28 | } |
3110 | 1.79M | } |
3111 | 1.79M | } |
3112 | | |
3113 | | JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) |
3114 | 450k | { |
3115 | 450k | return __JS_AtomToValue(ctx, atom, FALSE); |
3116 | 450k | } |
3117 | | |
3118 | | JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) |
3119 | 1.34M | { |
3120 | 1.34M | return __JS_AtomToValue(ctx, atom, TRUE); |
3121 | 1.34M | } |
3122 | | |
3123 | | /* return TRUE if the atom is an array index (i.e. 0 <= index <= |
3124 | | 2^32-2 and return its value */ |
3125 | | static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) |
3126 | 38.1M | { |
3127 | 38.1M | if (__JS_AtomIsTaggedInt(atom)) { |
3128 | 38.1M | *pval = __JS_AtomToUInt32(atom); |
3129 | 38.1M | return TRUE; |
3130 | 38.1M | } else { |
3131 | 317 | JSRuntime *rt = ctx->rt; |
3132 | 317 | JSAtomStruct *p; |
3133 | 317 | uint32_t val; |
3134 | | |
3135 | 317 | assert(atom < rt->atom_size); |
3136 | 317 | p = rt->atom_array[atom]; |
3137 | 317 | if (p->atom_type == JS_ATOM_TYPE_STRING && |
3138 | 317 | is_num_string(&val, p) && val != -1) { |
3139 | 0 | *pval = val; |
3140 | 0 | return TRUE; |
3141 | 317 | } else { |
3142 | 317 | *pval = 0; |
3143 | 317 | return FALSE; |
3144 | 317 | } |
3145 | 317 | } |
3146 | 38.1M | } |
3147 | | |
3148 | | /* This test must be fast if atom is not a numeric index (e.g. a |
3149 | | method name). Return JS_UNDEFINED if not a numeric |
3150 | | index. JS_EXCEPTION can also be returned. */ |
3151 | | static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) |
3152 | 0 | { |
3153 | 0 | JSRuntime *rt = ctx->rt; |
3154 | 0 | JSAtomStruct *p1; |
3155 | 0 | JSString *p; |
3156 | 0 | int c, len, ret; |
3157 | 0 | JSValue num, str; |
3158 | |
|
3159 | 0 | if (__JS_AtomIsTaggedInt(atom)) |
3160 | 0 | return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); |
3161 | 0 | assert(atom < rt->atom_size); |
3162 | 0 | p1 = rt->atom_array[atom]; |
3163 | 0 | if (p1->atom_type != JS_ATOM_TYPE_STRING) |
3164 | 0 | return JS_UNDEFINED; |
3165 | 0 | p = p1; |
3166 | 0 | len = p->len; |
3167 | 0 | if (p->is_wide_char) { |
3168 | 0 | const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; |
3169 | 0 | if (r >= r_end) |
3170 | 0 | return JS_UNDEFINED; |
3171 | 0 | c = *r; |
3172 | 0 | if (c == '-') { |
3173 | 0 | if (r >= r_end) |
3174 | 0 | return JS_UNDEFINED; |
3175 | 0 | r++; |
3176 | 0 | c = *r; |
3177 | | /* -0 case is specific */ |
3178 | 0 | if (c == '0' && len == 2) |
3179 | 0 | goto minus_zero; |
3180 | 0 | } |
3181 | | /* XXX: should test NaN, but the tests do not check it */ |
3182 | 0 | if (!is_num(c)) { |
3183 | | /* XXX: String should be normalized, therefore 8-bit only */ |
3184 | 0 | const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; |
3185 | 0 | if (!(c =='I' && (r_end - r) == 8 && |
3186 | 0 | !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) |
3187 | 0 | return JS_UNDEFINED; |
3188 | 0 | } |
3189 | 0 | } else { |
3190 | 0 | const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; |
3191 | 0 | if (r >= r_end) |
3192 | 0 | return JS_UNDEFINED; |
3193 | 0 | c = *r; |
3194 | 0 | if (c == '-') { |
3195 | 0 | if (r >= r_end) |
3196 | 0 | return JS_UNDEFINED; |
3197 | 0 | r++; |
3198 | 0 | c = *r; |
3199 | | /* -0 case is specific */ |
3200 | 0 | if (c == '0' && len == 2) { |
3201 | 0 | minus_zero: |
3202 | 0 | return __JS_NewFloat64(ctx, -0.0); |
3203 | 0 | } |
3204 | 0 | } |
3205 | 0 | if (!is_num(c)) { |
3206 | 0 | if (!(c =='I' && (r_end - r) == 8 && |
3207 | 0 | !memcmp(r + 1, "nfinity", 7))) |
3208 | 0 | return JS_UNDEFINED; |
3209 | 0 | } |
3210 | 0 | } |
3211 | | /* XXX: bignum: would be better to only accept integer to avoid |
3212 | | relying on current floating point precision */ |
3213 | | /* this is ECMA CanonicalNumericIndexString primitive */ |
3214 | 0 | num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); |
3215 | 0 | if (JS_IsException(num)) |
3216 | 0 | return num; |
3217 | 0 | str = JS_ToString(ctx, num); |
3218 | 0 | if (JS_IsException(str)) { |
3219 | 0 | JS_FreeValue(ctx, num); |
3220 | 0 | return str; |
3221 | 0 | } |
3222 | 0 | ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); |
3223 | 0 | JS_FreeValue(ctx, str); |
3224 | 0 | if (ret == 0) { |
3225 | 0 | return num; |
3226 | 0 | } else { |
3227 | 0 | JS_FreeValue(ctx, num); |
3228 | 0 | return JS_UNDEFINED; |
3229 | 0 | } |
3230 | 0 | } |
3231 | | |
3232 | | /* return -1 if exception or TRUE/FALSE */ |
3233 | | static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) |
3234 | 0 | { |
3235 | 0 | JSValue num; |
3236 | 0 | num = JS_AtomIsNumericIndex1(ctx, atom); |
3237 | 0 | if (likely(JS_IsUndefined(num))) |
3238 | 0 | return FALSE; |
3239 | 0 | if (JS_IsException(num)) |
3240 | 0 | return -1; |
3241 | 0 | JS_FreeValue(ctx, num); |
3242 | 0 | return TRUE; |
3243 | 0 | } |
3244 | | |
3245 | | void JS_FreeAtom(JSContext *ctx, JSAtom v) |
3246 | 3.49M | { |
3247 | 3.49M | if (!__JS_AtomIsConst(v)) |
3248 | 1.21M | __JS_FreeAtom(ctx->rt, v); |
3249 | 3.49M | } |
3250 | | |
3251 | | void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) |
3252 | 7.23M | { |
3253 | 7.23M | if (!__JS_AtomIsConst(v)) |
3254 | 3.50M | __JS_FreeAtom(rt, v); |
3255 | 7.23M | } |
3256 | | |
3257 | | /* return TRUE if 'v' is a symbol with a string description */ |
3258 | | static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) |
3259 | 46.8k | { |
3260 | 46.8k | JSRuntime *rt; |
3261 | 46.8k | JSAtomStruct *p; |
3262 | | |
3263 | 46.8k | rt = ctx->rt; |
3264 | 46.8k | if (__JS_AtomIsTaggedInt(v)) |
3265 | 0 | return FALSE; |
3266 | 46.8k | p = rt->atom_array[v]; |
3267 | 46.8k | return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && |
3268 | 46.8k | p->hash == JS_ATOM_HASH_SYMBOL) || |
3269 | 46.8k | p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && |
3270 | 46.8k | !(p->len == 0 && p->is_wide_char != 0)); |
3271 | 46.8k | } |
3272 | | |
3273 | | static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) |
3274 | 0 | { |
3275 | 0 | char buf[ATOM_GET_STR_BUF_SIZE]; |
3276 | 0 | const char *p; |
3277 | 0 | int i; |
3278 | 0 |
|
3279 | 0 | /* XXX: should handle embedded null characters */ |
3280 | 0 | /* XXX: should move encoding code to JS_AtomGetStr */ |
3281 | 0 | p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); |
3282 | 0 | for (i = 0; p[i]; i++) { |
3283 | 0 | int c = (unsigned char)p[i]; |
3284 | 0 | if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || |
3285 | 0 | (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) |
3286 | 0 | break; |
3287 | 0 | } |
3288 | 0 | if (i > 0 && p[i] == '\0') { |
3289 | 0 | printf("%s", p); |
3290 | 0 | } else { |
3291 | 0 | putchar('"'); |
3292 | 0 | printf("%.*s", i, p); |
3293 | 0 | for (; p[i]; i++) { |
3294 | 0 | int c = (unsigned char)p[i]; |
3295 | 0 | if (c == '\"' || c == '\\') { |
3296 | 0 | putchar('\\'); |
3297 | 0 | putchar(c); |
3298 | 0 | } else if (c >= ' ' && c <= 126) { |
3299 | 0 | putchar(c); |
3300 | 0 | } else if (c == '\n') { |
3301 | 0 | putchar('\\'); |
3302 | 0 | putchar('n'); |
3303 | 0 | } else { |
3304 | 0 | printf("\\u%04x", c); |
3305 | 0 | } |
3306 | 0 | } |
3307 | 0 | putchar('\"'); |
3308 | 0 | } |
3309 | 0 | } |
3310 | | |
3311 | | /* free with JS_FreeCString() */ |
3312 | | const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) |
3313 | 163k | { |
3314 | 163k | JSValue str; |
3315 | 163k | const char *cstr; |
3316 | | |
3317 | 163k | str = JS_AtomToString(ctx, atom); |
3318 | 163k | if (JS_IsException(str)) |
3319 | 0 | return NULL; |
3320 | 163k | cstr = JS_ToCString(ctx, str); |
3321 | 163k | JS_FreeValue(ctx, str); |
3322 | 163k | return cstr; |
3323 | 163k | } |
3324 | | |
3325 | | /* return a string atom containing name concatenated with str1 */ |
3326 | | static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) |
3327 | 76 | { |
3328 | 76 | JSValue str; |
3329 | 76 | JSAtom atom; |
3330 | 76 | const char *cstr; |
3331 | 76 | char *cstr2; |
3332 | 76 | size_t len, len1; |
3333 | | |
3334 | 76 | str = JS_AtomToString(ctx, name); |
3335 | 76 | if (JS_IsException(str)) |
3336 | 0 | return JS_ATOM_NULL; |
3337 | 76 | cstr = JS_ToCStringLen(ctx, &len, str); |
3338 | 76 | if (!cstr) |
3339 | 0 | goto fail; |
3340 | 76 | len1 = strlen(str1); |
3341 | 76 | cstr2 = js_malloc(ctx, len + len1 + 1); |
3342 | 76 | if (!cstr2) |
3343 | 0 | goto fail; |
3344 | 76 | memcpy(cstr2, cstr, len); |
3345 | 76 | memcpy(cstr2 + len, str1, len1); |
3346 | 76 | cstr2[len + len1] = '\0'; |
3347 | 76 | atom = JS_NewAtomLen(ctx, cstr2, len + len1); |
3348 | 76 | js_free(ctx, cstr2); |
3349 | 76 | JS_FreeCString(ctx, cstr); |
3350 | 76 | JS_FreeValue(ctx, str); |
3351 | 76 | return atom; |
3352 | 0 | fail: |
3353 | 0 | JS_FreeCString(ctx, cstr); |
3354 | 0 | JS_FreeValue(ctx, str); |
3355 | 0 | return JS_ATOM_NULL; |
3356 | 76 | } |
3357 | | |
3358 | | static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) |
3359 | 76 | { |
3360 | 76 | char buf[16]; |
3361 | 76 | snprintf(buf, sizeof(buf), "%u", n); |
3362 | 76 | return js_atom_concat_str(ctx, name, buf); |
3363 | 76 | } |
3364 | | |
3365 | | static inline BOOL JS_IsEmptyString(JSValueConst v) |
3366 | 2 | { |
3367 | 2 | return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; |
3368 | 2 | } |
3369 | | |
3370 | | /* JSClass support */ |
3371 | | |
3372 | | /* a new class ID is allocated if *pclass_id != 0 */ |
3373 | | JSClassID JS_NewClassID(JSClassID *pclass_id) |
3374 | 0 | { |
3375 | 0 | JSClassID class_id; |
3376 | | /* XXX: make it thread safe */ |
3377 | 0 | class_id = *pclass_id; |
3378 | 0 | if (class_id == 0) { |
3379 | 0 | class_id = js_class_id_alloc++; |
3380 | 0 | *pclass_id = class_id; |
3381 | 0 | } |
3382 | 0 | return class_id; |
3383 | 0 | } |
3384 | | |
3385 | | BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) |
3386 | 4 | { |
3387 | 4 | return (class_id < rt->class_count && |
3388 | 4 | rt->class_array[class_id].class_id != 0); |
3389 | 4 | } |
3390 | | |
3391 | | /* create a new object internal class. Return -1 if error, 0 if |
3392 | | OK. The finalizer can be NULL if none is needed. */ |
3393 | | static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, |
3394 | | const JSClassDef *class_def, JSAtom name) |
3395 | 161 | { |
3396 | 161 | int new_size, i; |
3397 | 161 | JSClass *cl, *new_class_array; |
3398 | 161 | struct list_head *el; |
3399 | | |
3400 | 161 | if (class_id >= (1 << 16)) |
3401 | 0 | return -1; |
3402 | 161 | if (class_id < rt->class_count && |
3403 | 161 | rt->class_array[class_id].class_id != 0) |
3404 | 0 | return -1; |
3405 | | |
3406 | 161 | if (class_id >= rt->class_count) { |
3407 | 3 | new_size = max_int(JS_CLASS_INIT_COUNT, |
3408 | 3 | max_int(class_id + 1, rt->class_count * 3 / 2)); |
3409 | | |
3410 | | /* reallocate the context class prototype array, if any */ |
3411 | 3 | list_for_each(el, &rt->context_list) { |
3412 | 0 | JSContext *ctx = list_entry(el, JSContext, link); |
3413 | 0 | JSValue *new_tab; |
3414 | 0 | new_tab = js_realloc_rt(rt, ctx->class_proto, |
3415 | 0 | sizeof(ctx->class_proto[0]) * new_size); |
3416 | 0 | if (!new_tab) |
3417 | 0 | return -1; |
3418 | 0 | for(i = rt->class_count; i < new_size; i++) |
3419 | 0 | new_tab[i] = JS_NULL; |
3420 | 0 | ctx->class_proto = new_tab; |
3421 | 0 | } |
3422 | | /* reallocate the class array */ |
3423 | 3 | new_class_array = js_realloc_rt(rt, rt->class_array, |
3424 | 3 | sizeof(JSClass) * new_size); |
3425 | 3 | if (!new_class_array) |
3426 | 0 | return -1; |
3427 | 3 | memset(new_class_array + rt->class_count, 0, |
3428 | 3 | (new_size - rt->class_count) * sizeof(JSClass)); |
3429 | 3 | rt->class_array = new_class_array; |
3430 | 3 | rt->class_count = new_size; |
3431 | 3 | } |
3432 | 161 | cl = &rt->class_array[class_id]; |
3433 | 161 | cl->class_id = class_id; |
3434 | 161 | cl->class_name = JS_DupAtomRT(rt, name); |
3435 | 161 | cl->finalizer = class_def->finalizer; |
3436 | 161 | cl->gc_mark = class_def->gc_mark; |
3437 | 161 | cl->call = class_def->call; |
3438 | 161 | cl->exotic = class_def->exotic; |
3439 | 161 | return 0; |
3440 | 161 | } |
3441 | | |
3442 | | int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) |
3443 | 0 | { |
3444 | 0 | int ret, len; |
3445 | 0 | JSAtom name; |
3446 | |
|
3447 | 0 | len = strlen(class_def->class_name); |
3448 | 0 | name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); |
3449 | 0 | if (name == JS_ATOM_NULL) { |
3450 | 0 | name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); |
3451 | 0 | if (name == JS_ATOM_NULL) |
3452 | 0 | return -1; |
3453 | 0 | } |
3454 | 0 | ret = JS_NewClass1(rt, class_id, class_def, name); |
3455 | 0 | JS_FreeAtomRT(rt, name); |
3456 | 0 | return ret; |
3457 | 0 | } |
3458 | | |
3459 | | static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) |
3460 | 858k | { |
3461 | 858k | JSString *str; |
3462 | | |
3463 | 858k | if (len <= 0) { |
3464 | 0 | return JS_AtomToString(ctx, JS_ATOM_empty_string); |
3465 | 0 | } |
3466 | 858k | str = js_alloc_string(ctx, len, 0); |
3467 | 858k | if (!str) |
3468 | 0 | return JS_EXCEPTION; |
3469 | 858k | memcpy(str->u.str8, buf, len); |
3470 | 858k | str->u.str8[len] = '\0'; |
3471 | 858k | return JS_MKPTR(JS_TAG_STRING, str); |
3472 | 858k | } |
3473 | | |
3474 | | static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) |
3475 | 141 | { |
3476 | 141 | JSString *str; |
3477 | 141 | str = js_alloc_string(ctx, len, 1); |
3478 | 141 | if (!str) |
3479 | 0 | return JS_EXCEPTION; |
3480 | 141 | memcpy(str->u.str16, buf, len * 2); |
3481 | 141 | return JS_MKPTR(JS_TAG_STRING, str); |
3482 | 141 | } |
3483 | | |
3484 | | static JSValue js_new_string_char(JSContext *ctx, uint16_t c) |
3485 | 396k | { |
3486 | 396k | if (c < 0x100) { |
3487 | 396k | uint8_t ch8 = c; |
3488 | 396k | return js_new_string8(ctx, &ch8, 1); |
3489 | 396k | } else { |
3490 | 141 | uint16_t ch16 = c; |
3491 | 141 | return js_new_string16(ctx, &ch16, 1); |
3492 | 141 | } |
3493 | 396k | } |
3494 | | |
3495 | | static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) |
3496 | 100k | { |
3497 | 100k | int len = end - start; |
3498 | 100k | if (start == 0 && end == p->len) { |
3499 | 0 | return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); |
3500 | 0 | } |
3501 | 100k | if (p->is_wide_char && len > 0) { |
3502 | 0 | JSString *str; |
3503 | 0 | int i; |
3504 | 0 | uint16_t c = 0; |
3505 | 0 | for (i = start; i < end; i++) { |
3506 | 0 | c |= p->u.str16[i]; |
3507 | 0 | } |
3508 | 0 | if (c > 0xFF) |
3509 | 0 | return js_new_string16(ctx, p->u.str16 + start, len); |
3510 | | |
3511 | 0 | str = js_alloc_string(ctx, len, 0); |
3512 | 0 | if (!str) |
3513 | 0 | return JS_EXCEPTION; |
3514 | 0 | for (i = 0; i < len; i++) { |
3515 | 0 | str->u.str8[i] = p->u.str16[start + i]; |
3516 | 0 | } |
3517 | 0 | str->u.str8[len] = '\0'; |
3518 | 0 | return JS_MKPTR(JS_TAG_STRING, str); |
3519 | 100k | } else { |
3520 | 100k | return js_new_string8(ctx, p->u.str8 + start, len); |
3521 | 100k | } |
3522 | 100k | } |
3523 | | |
3524 | | typedef struct StringBuffer { |
3525 | | JSContext *ctx; |
3526 | | JSString *str; |
3527 | | int len; |
3528 | | int size; |
3529 | | int is_wide_char; |
3530 | | int error_status; |
3531 | | } StringBuffer; |
3532 | | |
3533 | | /* It is valid to call string_buffer_end() and all string_buffer functions even |
3534 | | if string_buffer_init() or another string_buffer function returns an error. |
3535 | | If the error_status is set, string_buffer_end() returns JS_EXCEPTION. |
3536 | | */ |
3537 | | static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, |
3538 | | int is_wide) |
3539 | 231k | { |
3540 | 231k | s->ctx = ctx; |
3541 | 231k | s->size = size; |
3542 | 231k | s->len = 0; |
3543 | 231k | s->is_wide_char = is_wide; |
3544 | 231k | s->error_status = 0; |
3545 | 231k | s->str = js_alloc_string(ctx, size, is_wide); |
3546 | 231k | if (unlikely(!s->str)) { |
3547 | 0 | s->size = 0; |
3548 | 0 | return s->error_status = -1; |
3549 | 0 | } |
3550 | | #ifdef DUMP_LEAKS |
3551 | | /* the StringBuffer may reallocate the JSString, only link it at the end */ |
3552 | | list_del(&s->str->link); |
3553 | | #endif |
3554 | 231k | return 0; |
3555 | 231k | } |
3556 | | |
3557 | | static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) |
3558 | 5.26k | { |
3559 | 5.26k | return string_buffer_init2(ctx, s, size, 0); |
3560 | 5.26k | } |
3561 | | |
3562 | | static void string_buffer_free(StringBuffer *s) |
3563 | 6 | { |
3564 | 6 | js_free(s->ctx, s->str); |
3565 | 6 | s->str = NULL; |
3566 | 6 | } |
3567 | | |
3568 | | static int string_buffer_set_error(StringBuffer *s) |
3569 | 0 | { |
3570 | 0 | js_free(s->ctx, s->str); |
3571 | 0 | s->str = NULL; |
3572 | 0 | s->size = 0; |
3573 | 0 | s->len = 0; |
3574 | 0 | return s->error_status = -1; |
3575 | 0 | } |
3576 | | |
3577 | | static no_inline int string_buffer_widen(StringBuffer *s, int size) |
3578 | 1.78k | { |
3579 | 1.78k | JSString *str; |
3580 | 1.78k | size_t slack; |
3581 | 1.78k | int i; |
3582 | | |
3583 | 1.78k | if (s->error_status) |
3584 | 0 | return -1; |
3585 | | |
3586 | 1.78k | str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); |
3587 | 1.78k | if (!str) |
3588 | 0 | return string_buffer_set_error(s); |
3589 | 1.78k | size += slack >> 1; |
3590 | 618k | for(i = s->len; i-- > 0;) { |
3591 | 616k | str->u.str16[i] = str->u.str8[i]; |
3592 | 616k | } |
3593 | 1.78k | s->is_wide_char = 1; |
3594 | 1.78k | s->size = size; |
3595 | 1.78k | s->str = str; |
3596 | 1.78k | return 0; |
3597 | 1.78k | } |
3598 | | |
3599 | | static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) |
3600 | 1.12k | { |
3601 | 1.12k | JSString *new_str; |
3602 | 1.12k | int new_size; |
3603 | 1.12k | size_t new_size_bytes, slack; |
3604 | | |
3605 | 1.12k | if (s->error_status) |
3606 | 0 | return -1; |
3607 | | |
3608 | 1.12k | if (new_len > JS_STRING_LEN_MAX) { |
3609 | 0 | JS_ThrowInternalError(s->ctx, "string too long"); |
3610 | 0 | return string_buffer_set_error(s); |
3611 | 0 | } |
3612 | 1.12k | new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); |
3613 | 1.12k | if (!s->is_wide_char && c >= 0x100) { |
3614 | 2 | return string_buffer_widen(s, new_size); |
3615 | 2 | } |
3616 | 1.11k | new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; |
3617 | 1.11k | new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); |
3618 | 1.11k | if (!new_str) |
3619 | 0 | return string_buffer_set_error(s); |
3620 | 1.11k | new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); |
3621 | 1.11k | s->size = new_size; |
3622 | 1.11k | s->str = new_str; |
3623 | 1.11k | return 0; |
3624 | 1.11k | } |
3625 | | |
3626 | | static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) |
3627 | 2.82k | { |
3628 | 2.82k | if (unlikely(s->len >= s->size)) { |
3629 | 1.04k | if (string_buffer_realloc(s, s->len + 1, c)) |
3630 | 0 | return -1; |
3631 | 1.04k | } |
3632 | 2.82k | if (s->is_wide_char) { |
3633 | 145 | s->str->u.str16[s->len++] = c; |
3634 | 2.67k | } else if (c < 0x100) { |
3635 | 899 | s->str->u.str8[s->len++] = c; |
3636 | 1.77k | } else { |
3637 | 1.77k | if (string_buffer_widen(s, s->size)) |
3638 | 0 | return -1; |
3639 | 1.77k | s->str->u.str16[s->len++] = c; |
3640 | 1.77k | } |
3641 | 2.82k | return 0; |
3642 | 2.82k | } |
3643 | | |
3644 | | /* 0 <= c <= 0xff */ |
3645 | | static int string_buffer_putc8(StringBuffer *s, uint32_t c) |
3646 | 2.44M | { |
3647 | 2.44M | if (unlikely(s->len >= s->size)) { |
3648 | 62 | if (string_buffer_realloc(s, s->len + 1, c)) |
3649 | 0 | return -1; |
3650 | 62 | } |
3651 | 2.44M | if (s->is_wide_char) { |
3652 | 2.31M | s->str->u.str16[s->len++] = c; |
3653 | 2.31M | } else { |
3654 | 126k | s->str->u.str8[s->len++] = c; |
3655 | 126k | } |
3656 | 2.44M | return 0; |
3657 | 2.44M | } |
3658 | | |
3659 | | /* 0 <= c <= 0xffff */ |
3660 | | static int string_buffer_putc16(StringBuffer *s, uint32_t c) |
3661 | 1.37M | { |
3662 | 1.37M | if (likely(s->len < s->size)) { |
3663 | 1.37M | if (s->is_wide_char) { |
3664 | 1.04M | s->str->u.str16[s->len++] = c; |
3665 | 1.04M | return 0; |
3666 | 1.04M | } else if (c < 0x100) { |
3667 | 331k | s->str->u.str8[s->len++] = c; |
3668 | 331k | return 0; |
3669 | 331k | } |
3670 | 1.37M | } |
3671 | 2.82k | return string_buffer_putc_slow(s, c); |
3672 | 1.37M | } |
3673 | | |
3674 | | /* 0 <= c <= 0x10ffff */ |
3675 | | static int string_buffer_putc(StringBuffer *s, uint32_t c) |
3676 | 1.02M | { |
3677 | 1.02M | if (unlikely(c >= 0x10000)) { |
3678 | | /* surrogate pair */ |
3679 | 10 | c -= 0x10000; |
3680 | 10 | if (string_buffer_putc16(s, (c >> 10) + 0xd800)) |
3681 | 0 | return -1; |
3682 | 10 | c = (c & 0x3ff) + 0xdc00; |
3683 | 10 | } |
3684 | 1.02M | return string_buffer_putc16(s, c); |
3685 | 1.02M | } |
3686 | | |
3687 | 52.7k | static int string_get(const JSString *p, int idx) { |
3688 | 52.7k | return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; |
3689 | 52.7k | } |
3690 | | |
3691 | | static int string_getc(const JSString *p, int *pidx) |
3692 | 53.0k | { |
3693 | 53.0k | int idx, c, c1; |
3694 | 53.0k | idx = *pidx; |
3695 | 53.0k | if (p->is_wide_char) { |
3696 | 52.7k | c = p->u.str16[idx++]; |
3697 | 52.7k | if (c >= 0xd800 && c < 0xdc00 && idx < p->len) { |
3698 | 0 | c1 = p->u.str16[idx]; |
3699 | 0 | if (c1 >= 0xdc00 && c1 < 0xe000) { |
3700 | 0 | c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; |
3701 | 0 | idx++; |
3702 | 0 | } |
3703 | 0 | } |
3704 | 52.7k | } else { |
3705 | 359 | c = p->u.str8[idx++]; |
3706 | 359 | } |
3707 | 53.0k | *pidx = idx; |
3708 | 53.0k | return c; |
3709 | 53.0k | } |
3710 | | |
3711 | | static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) |
3712 | 832k | { |
3713 | 832k | int i; |
3714 | | |
3715 | 832k | if (s->len + len > s->size) { |
3716 | 12 | if (string_buffer_realloc(s, s->len + len, 0)) |
3717 | 0 | return -1; |
3718 | 12 | } |
3719 | 832k | if (s->is_wide_char) { |
3720 | 61.5k | for (i = 0; i < len; i++) { |
3721 | 30.7k | s->str->u.str16[s->len + i] = p[i]; |
3722 | 30.7k | } |
3723 | 30.7k | s->len += len; |
3724 | 801k | } else { |
3725 | 801k | memcpy(&s->str->u.str8[s->len], p, len); |
3726 | 801k | s->len += len; |
3727 | 801k | } |
3728 | 832k | return 0; |
3729 | 832k | } |
3730 | | |
3731 | | static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) |
3732 | 4 | { |
3733 | 4 | int c = 0, i; |
3734 | | |
3735 | 131k | for (i = 0; i < len; i++) { |
3736 | 131k | c |= p[i]; |
3737 | 131k | } |
3738 | 4 | if (s->len + len > s->size) { |
3739 | 2 | if (string_buffer_realloc(s, s->len + len, c)) |
3740 | 0 | return -1; |
3741 | 2 | } else if (!s->is_wide_char && c >= 0x100) { |
3742 | 1 | if (string_buffer_widen(s, s->size)) |
3743 | 0 | return -1; |
3744 | 1 | } |
3745 | 4 | if (s->is_wide_char) { |
3746 | 4 | memcpy(&s->str->u.str16[s->len], p, len << 1); |
3747 | 4 | s->len += len; |
3748 | 4 | } else { |
3749 | 0 | for (i = 0; i < len; i++) { |
3750 | 0 | s->str->u.str8[s->len + i] = p[i]; |
3751 | 0 | } |
3752 | 0 | s->len += len; |
3753 | 0 | } |
3754 | 4 | return 0; |
3755 | 4 | } |
3756 | | |
3757 | | /* appending an ASCII string */ |
3758 | | static int string_buffer_puts8(StringBuffer *s, const char *str) |
3759 | 0 | { |
3760 | 0 | return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); |
3761 | 0 | } |
3762 | | |
3763 | | static int string_buffer_concat(StringBuffer *s, const JSString *p, |
3764 | | uint32_t from, uint32_t to) |
3765 | 378k | { |
3766 | 378k | if (to <= from) |
3767 | 3 | return 0; |
3768 | 378k | if (p->is_wide_char) |
3769 | 4 | return string_buffer_write16(s, p->u.str16 + from, to - from); |
3770 | 378k | else |
3771 | 378k | return string_buffer_write8(s, p->u.str8 + from, to - from); |
3772 | 378k | } |
3773 | | |
3774 | | static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) |
3775 | 0 | { |
3776 | 0 | JSString *p; |
3777 | 0 | JSValue v1; |
3778 | 0 | int res; |
3779 | |
|
3780 | 0 | if (s->error_status) { |
3781 | | /* prevent exception overload */ |
3782 | 0 | return -1; |
3783 | 0 | } |
3784 | 0 | if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { |
3785 | 0 | v1 = JS_ToString(s->ctx, v); |
3786 | 0 | if (JS_IsException(v1)) |
3787 | 0 | return string_buffer_set_error(s); |
3788 | 0 | p = JS_VALUE_GET_STRING(v1); |
3789 | 0 | res = string_buffer_concat(s, p, 0, p->len); |
3790 | 0 | JS_FreeValue(s->ctx, v1); |
3791 | 0 | return res; |
3792 | 0 | } |
3793 | 0 | p = JS_VALUE_GET_STRING(v); |
3794 | 0 | return string_buffer_concat(s, p, 0, p->len); |
3795 | 0 | } |
3796 | | |
3797 | | static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) |
3798 | 152k | { |
3799 | 152k | JSString *p; |
3800 | 152k | int res; |
3801 | | |
3802 | 152k | if (s->error_status) { |
3803 | | /* prevent exception overload */ |
3804 | 0 | JS_FreeValue(s->ctx, v); |
3805 | 0 | return -1; |
3806 | 0 | } |
3807 | 152k | if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { |
3808 | 1 | v = JS_ToStringFree(s->ctx, v); |
3809 | 1 | if (JS_IsException(v)) |
3810 | 0 | return string_buffer_set_error(s); |
3811 | 1 | } |
3812 | 152k | p = JS_VALUE_GET_STRING(v); |
3813 | 152k | res = string_buffer_concat(s, p, 0, p->len); |
3814 | 152k | JS_FreeValue(s->ctx, v); |
3815 | 152k | return res; |
3816 | 152k | } |
3817 | | |
3818 | | static int string_buffer_fill(StringBuffer *s, int c, int count) |
3819 | 0 | { |
3820 | | /* XXX: optimize */ |
3821 | 0 | if (s->len + count > s->size) { |
3822 | 0 | if (string_buffer_realloc(s, s->len + count, c)) |
3823 | 0 | return -1; |
3824 | 0 | } |
3825 | 0 | while (count-- > 0) { |
3826 | 0 | if (string_buffer_putc16(s, c)) |
3827 | 0 | return -1; |
3828 | 0 | } |
3829 | 0 | return 0; |
3830 | 0 | } |
3831 | | |
3832 | | static JSValue string_buffer_end(StringBuffer *s) |
3833 | 231k | { |
3834 | 231k | JSString *str; |
3835 | 231k | str = s->str; |
3836 | 231k | if (s->error_status) |
3837 | 0 | return JS_EXCEPTION; |
3838 | 231k | if (s->len == 0) { |
3839 | 1.06k | js_free(s->ctx, str); |
3840 | 1.06k | s->str = NULL; |
3841 | 1.06k | return JS_AtomToString(s->ctx, JS_ATOM_empty_string); |
3842 | 1.06k | } |
3843 | 230k | if (s->len < s->size) { |
3844 | | /* smaller size so js_realloc should not fail, but OK if it does */ |
3845 | | /* XXX: should add some slack to avoid unnecessary calls */ |
3846 | | /* XXX: might need to use malloc+free to ensure smaller size */ |
3847 | 4.19k | str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + |
3848 | 4.19k | (s->len << s->is_wide_char) + 1 - s->is_wide_char); |
3849 | 4.19k | if (str == NULL) |
3850 | 0 | str = s->str; |
3851 | 4.19k | s->str = str; |
3852 | 4.19k | } |
3853 | 230k | if (!s->is_wide_char) |
3854 | 228k | str->u.str8[s->len] = 0; |
3855 | | #ifdef DUMP_LEAKS |
3856 | | list_add_tail(&str->link, &s->ctx->rt->string_list); |
3857 | | #endif |
3858 | 230k | str->is_wide_char = s->is_wide_char; |
3859 | 230k | str->len = s->len; |
3860 | 230k | s->str = NULL; |
3861 | 230k | return JS_MKPTR(JS_TAG_STRING, str); |
3862 | 231k | } |
3863 | | |
3864 | | /* create a string from a UTF-8 buffer */ |
3865 | | JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) |
3866 | 363k | { |
3867 | 363k | const uint8_t *p, *p_end, *p_start, *p_next; |
3868 | 363k | uint32_t c; |
3869 | 363k | StringBuffer b_s, *b = &b_s; |
3870 | 363k | size_t len1; |
3871 | | |
3872 | 363k | p_start = (const uint8_t *)buf; |
3873 | 363k | p_end = p_start + buf_len; |
3874 | 363k | p = p_start; |
3875 | 9.33M | while (p < p_end && *p < 128) |
3876 | 8.97M | p++; |
3877 | 363k | len1 = p - p_start; |
3878 | 363k | if (len1 > JS_STRING_LEN_MAX) |
3879 | 0 | return JS_ThrowInternalError(ctx, "string too long"); |
3880 | 363k | if (p == p_end) { |
3881 | | /* ASCII string */ |
3882 | 361k | return js_new_string8(ctx, (const uint8_t *)buf, buf_len); |
3883 | 361k | } else { |
3884 | 1.86k | if (string_buffer_init(ctx, b, buf_len)) |
3885 | 0 | goto fail; |
3886 | 1.86k | string_buffer_write8(b, p_start, len1); |
3887 | 2.64M | while (p < p_end) { |
3888 | 2.63M | if (*p < 128) { |
3889 | 2.28M | string_buffer_putc8(b, *p++); |
3890 | 2.28M | } else { |
3891 | | /* parse utf-8 sequence, return 0xFFFFFFFF for error */ |
3892 | 355k | c = unicode_from_utf8(p, p_end - p, &p_next); |
3893 | 355k | if (c < 0x10000) { |
3894 | 4.72k | p = p_next; |
3895 | 350k | } else if (c <= 0x10FFFF) { |
3896 | 124 | p = p_next; |
3897 | | /* surrogate pair */ |
3898 | 124 | c -= 0x10000; |
3899 | 124 | string_buffer_putc16(b, (c >> 10) + 0xd800); |
3900 | 124 | c = (c & 0x3ff) + 0xdc00; |
3901 | 350k | } else { |
3902 | | /* invalid char */ |
3903 | 350k | c = 0xfffd; |
3904 | | /* skip the invalid chars */ |
3905 | | /* XXX: seems incorrect. Why not just use c = *p++; ? */ |
3906 | 408k | while (p < p_end && (*p >= 0x80 && *p < 0xc0)) |
3907 | 58.2k | p++; |
3908 | 350k | if (p < p_end) { |
3909 | 350k | p++; |
3910 | 357k | while (p < p_end && (*p >= 0x80 && *p < 0xc0)) |
3911 | 6.81k | p++; |
3912 | 350k | } |
3913 | 350k | } |
3914 | 355k | string_buffer_putc16(b, c); |
3915 | 355k | } |
3916 | 2.63M | } |
3917 | 1.86k | } |
3918 | 1.86k | return string_buffer_end(b); |
3919 | | |
3920 | 0 | fail: |
3921 | 0 | string_buffer_free(b); |
3922 | 0 | return JS_EXCEPTION; |
3923 | 363k | } |
3924 | | |
3925 | | static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, |
3926 | | JSValue str2, const char *str3) |
3927 | 225k | { |
3928 | 225k | StringBuffer b_s, *b = &b_s; |
3929 | 225k | int len1, len3; |
3930 | 225k | JSString *p; |
3931 | | |
3932 | 225k | if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { |
3933 | 0 | str2 = JS_ToStringFree(ctx, str2); |
3934 | 0 | if (JS_IsException(str2)) |
3935 | 0 | goto fail; |
3936 | 0 | } |
3937 | 225k | p = JS_VALUE_GET_STRING(str2); |
3938 | 225k | len1 = strlen(str1); |
3939 | 225k | len3 = strlen(str3); |
3940 | | |
3941 | 225k | if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) |
3942 | 0 | goto fail; |
3943 | | |
3944 | 225k | string_buffer_write8(b, (const uint8_t *)str1, len1); |
3945 | 225k | string_buffer_concat(b, p, 0, p->len); |
3946 | 225k | string_buffer_write8(b, (const uint8_t *)str3, len3); |
3947 | | |
3948 | 225k | JS_FreeValue(ctx, str2); |
3949 | 225k | return string_buffer_end(b); |
3950 | | |
3951 | 0 | fail: |
3952 | 0 | JS_FreeValue(ctx, str2); |
3953 | 0 | return JS_EXCEPTION; |
3954 | 225k | } |
3955 | | |
3956 | | JSValue JS_NewString(JSContext *ctx, const char *str) |
3957 | 317k | { |
3958 | 317k | return JS_NewStringLen(ctx, str, strlen(str)); |
3959 | 317k | } |
3960 | | |
3961 | | JSValue JS_NewAtomString(JSContext *ctx, const char *str) |
3962 | 24 | { |
3963 | 24 | JSAtom atom = JS_NewAtom(ctx, str); |
3964 | 24 | if (atom == JS_ATOM_NULL) |
3965 | 0 | return JS_EXCEPTION; |
3966 | 24 | JSValue val = JS_AtomToString(ctx, atom); |
3967 | 24 | JS_FreeAtom(ctx, atom); |
3968 | 24 | return val; |
3969 | 24 | } |
3970 | | |
3971 | | /* return (NULL, 0) if exception. */ |
3972 | | /* return pointer into a JSString with a live ref_count */ |
3973 | | /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ |
3974 | | const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8) |
3975 | 400k | { |
3976 | 400k | JSValue val; |
3977 | 400k | JSString *str, *str_new; |
3978 | 400k | int pos, len, c, c1; |
3979 | 400k | uint8_t *q; |
3980 | | |
3981 | 400k | if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { |
3982 | 2 | val = JS_ToString(ctx, val1); |
3983 | 2 | if (JS_IsException(val)) |
3984 | 0 | goto fail; |
3985 | 400k | } else { |
3986 | 400k | val = JS_DupValue(ctx, val1); |
3987 | 400k | } |
3988 | | |
3989 | 400k | str = JS_VALUE_GET_STRING(val); |
3990 | 400k | len = str->len; |
3991 | 400k | if (!str->is_wide_char) { |
3992 | 400k | const uint8_t *src = str->u.str8; |
3993 | 400k | int count; |
3994 | | |
3995 | | /* count the number of non-ASCII characters */ |
3996 | | /* Scanning the whole string is required for ASCII strings, |
3997 | | and computing the number of non-ASCII bytes is less expensive |
3998 | | than testing each byte, hence this method is faster for ASCII |
3999 | | strings, which is the most common case. |
4000 | | */ |
4001 | 400k | count = 0; |
4002 | 7.09M | for (pos = 0; pos < len; pos++) { |
4003 | 6.69M | count += src[pos] >> 7; |
4004 | 6.69M | } |
4005 | 400k | if (count == 0) { |
4006 | 400k | if (plen) |
4007 | 195k | *plen = len; |
4008 | 400k | return (const char *)src; |
4009 | 400k | } |
4010 | 0 | str_new = js_alloc_string(ctx, len + count, 0); |
4011 | 0 | if (!str_new) |
4012 | 0 | goto fail; |
4013 | 0 | q = str_new->u.str8; |
4014 | 0 | for (pos = 0; pos < len; pos++) { |
4015 | 0 | c = src[pos]; |
4016 | 0 | if (c < 0x80) { |
4017 | 0 | *q++ = c; |
4018 | 0 | } else { |
4019 | 0 | *q++ = (c >> 6) | 0xc0; |
4020 | 0 | *q++ = (c & 0x3f) | 0x80; |
4021 | 0 | } |
4022 | 0 | } |
4023 | 44 | } else { |
4024 | 44 | const uint16_t *src = str->u.str16; |
4025 | | /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may |
4026 | | produce 4 bytes but use 2 code points. |
4027 | | */ |
4028 | 44 | str_new = js_alloc_string(ctx, len * 3, 0); |
4029 | 44 | if (!str_new) |
4030 | 0 | goto fail; |
4031 | 44 | q = str_new->u.str8; |
4032 | 44 | pos = 0; |
4033 | 3.21M | while (pos < len) { |
4034 | 3.21M | c = src[pos++]; |
4035 | 3.21M | if (c < 0x80) { |
4036 | 2.86M | *q++ = c; |
4037 | 2.86M | } else { |
4038 | 354k | if (c >= 0xd800 && c < 0xdc00) { |
4039 | 134 | if (pos < len && !cesu8) { |
4040 | 134 | c1 = src[pos]; |
4041 | 134 | if (c1 >= 0xdc00 && c1 < 0xe000) { |
4042 | 129 | pos++; |
4043 | | /* surrogate pair */ |
4044 | 129 | c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; |
4045 | 129 | } else { |
4046 | | /* Keep unmatched surrogate code points */ |
4047 | | /* c = 0xfffd; */ /* error */ |
4048 | 5 | } |
4049 | 134 | } else { |
4050 | | /* Keep unmatched surrogate code points */ |
4051 | | /* c = 0xfffd; */ /* error */ |
4052 | 0 | } |
4053 | 134 | } |
4054 | 354k | q += unicode_to_utf8(q, c); |
4055 | 354k | } |
4056 | 3.21M | } |
4057 | 44 | } |
4058 | | |
4059 | 44 | *q = '\0'; |
4060 | 44 | str_new->len = q - str_new->u.str8; |
4061 | 44 | JS_FreeValue(ctx, val); |
4062 | 44 | if (plen) |
4063 | 44 | *plen = str_new->len; |
4064 | 44 | return (const char *)str_new->u.str8; |
4065 | 0 | fail: |
4066 | 0 | if (plen) |
4067 | 0 | *plen = 0; |
4068 | 0 | return NULL; |
4069 | 400k | } |
4070 | | |
4071 | | void JS_FreeCString(JSContext *ctx, const char *ptr) |
4072 | 522k | { |
4073 | 522k | JSString *p; |
4074 | 522k | if (!ptr) |
4075 | 122k | return; |
4076 | | /* purposely removing constness */ |
4077 | 400k | p = (JSString *)(void *)(ptr - offsetof(JSString, u)); |
4078 | 400k | JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); |
4079 | 400k | } |
4080 | | |
4081 | | static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) |
4082 | 0 | { |
4083 | 0 | int c, i; |
4084 | 0 | for(i = 0; i < len; i++) { |
4085 | 0 | c = src1[i] - src2[i]; |
4086 | 0 | if (c != 0) |
4087 | 0 | return c; |
4088 | 0 | } |
4089 | 0 | return 0; |
4090 | 0 | } |
4091 | | |
4092 | | static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) |
4093 | 1.63k | { |
4094 | 1.63k | int c, i; |
4095 | 10.5k | for(i = 0; i < len; i++) { |
4096 | 8.90k | c = src1[i] - src2[i]; |
4097 | 8.90k | if (c != 0) |
4098 | 0 | return c; |
4099 | 8.90k | } |
4100 | 1.63k | return 0; |
4101 | 1.63k | } |
4102 | | |
4103 | | static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) |
4104 | 50.0k | { |
4105 | 50.0k | int res; |
4106 | | |
4107 | 50.0k | if (likely(!p1->is_wide_char)) { |
4108 | 48.4k | if (likely(!p2->is_wide_char)) |
4109 | 48.4k | res = memcmp(p1->u.str8, p2->u.str8, len); |
4110 | 0 | else |
4111 | 0 | res = -memcmp16_8(p2->u.str16, p1->u.str8, len); |
4112 | 48.4k | } else { |
4113 | 1.63k | if (!p2->is_wide_char) |
4114 | 0 | res = memcmp16_8(p1->u.str16, p2->u.str8, len); |
4115 | 1.63k | else |
4116 | 1.63k | res = memcmp16(p1->u.str16, p2->u.str16, len); |
4117 | 1.63k | } |
4118 | 50.0k | return res; |
4119 | 50.0k | } |
4120 | | |
4121 | | /* return < 0, 0 or > 0 */ |
4122 | | static int js_string_compare(JSContext *ctx, |
4123 | | const JSString *p1, const JSString *p2) |
4124 | 0 | { |
4125 | 0 | int res, len; |
4126 | 0 | len = min_int(p1->len, p2->len); |
4127 | 0 | res = js_string_memcmp(p1, p2, len); |
4128 | 0 | if (res == 0) { |
4129 | 0 | if (p1->len == p2->len) |
4130 | 0 | res = 0; |
4131 | 0 | else if (p1->len < p2->len) |
4132 | 0 | res = -1; |
4133 | 0 | else |
4134 | 0 | res = 1; |
4135 | 0 | } |
4136 | 0 | return res; |
4137 | 0 | } |
4138 | | |
4139 | | static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) |
4140 | 0 | { |
4141 | 0 | if (p->is_wide_char) { |
4142 | 0 | memcpy(dst, p->u.str16 + offset, len * 2); |
4143 | 0 | } else { |
4144 | 0 | const uint8_t *src1 = p->u.str8 + offset; |
4145 | 0 | int i; |
4146 | |
|
4147 | 0 | for(i = 0; i < len; i++) |
4148 | 0 | dst[i] = src1[i]; |
4149 | 0 | } |
4150 | 0 | } |
4151 | | |
4152 | | static JSValue JS_ConcatString1(JSContext *ctx, |
4153 | | const JSString *p1, const JSString *p2) |
4154 | 71.9k | { |
4155 | 71.9k | JSString *p; |
4156 | 71.9k | uint32_t len; |
4157 | 71.9k | int is_wide_char; |
4158 | | |
4159 | 71.9k | len = p1->len + p2->len; |
4160 | 71.9k | if (len > JS_STRING_LEN_MAX) |
4161 | 0 | return JS_ThrowInternalError(ctx, "string too long"); |
4162 | 71.9k | is_wide_char = p1->is_wide_char | p2->is_wide_char; |
4163 | 71.9k | p = js_alloc_string(ctx, len, is_wide_char); |
4164 | 71.9k | if (!p) |
4165 | 0 | return JS_EXCEPTION; |
4166 | 71.9k | if (!is_wide_char) { |
4167 | 71.9k | memcpy(p->u.str8, p1->u.str8, p1->len); |
4168 | 71.9k | memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); |
4169 | 71.9k | p->u.str8[len] = '\0'; |
4170 | 71.9k | } else { |
4171 | 0 | copy_str16(p->u.str16, p1, 0, p1->len); |
4172 | 0 | copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); |
4173 | 0 | } |
4174 | 71.9k | return JS_MKPTR(JS_TAG_STRING, p); |
4175 | 71.9k | } |
4176 | | |
4177 | | /* op1 and op2 are converted to strings. For convience, op1 or op2 = |
4178 | | JS_EXCEPTION are accepted and return JS_EXCEPTION. */ |
4179 | | static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) |
4180 | 71.9k | { |
4181 | 71.9k | JSValue ret; |
4182 | 71.9k | JSString *p1, *p2; |
4183 | | |
4184 | 71.9k | if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { |
4185 | 71.9k | op1 = JS_ToStringFree(ctx, op1); |
4186 | 71.9k | if (JS_IsException(op1)) { |
4187 | 0 | JS_FreeValue(ctx, op2); |
4188 | 0 | return JS_EXCEPTION; |
4189 | 0 | } |
4190 | 71.9k | } |
4191 | 71.9k | if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { |
4192 | 0 | op2 = JS_ToStringFree(ctx, op2); |
4193 | 0 | if (JS_IsException(op2)) { |
4194 | 0 | JS_FreeValue(ctx, op1); |
4195 | 0 | return JS_EXCEPTION; |
4196 | 0 | } |
4197 | 0 | } |
4198 | 71.9k | p1 = JS_VALUE_GET_STRING(op1); |
4199 | 71.9k | p2 = JS_VALUE_GET_STRING(op2); |
4200 | | |
4201 | | /* XXX: could also check if p1 is empty */ |
4202 | 71.9k | if (p2->len == 0) { |
4203 | 0 | goto ret_op1; |
4204 | 0 | } |
4205 | 71.9k | if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char |
4206 | 71.9k | && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { |
4207 | | /* Concatenate in place in available space at the end of p1 */ |
4208 | 0 | if (p1->is_wide_char) { |
4209 | 0 | memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); |
4210 | 0 | p1->len += p2->len; |
4211 | 0 | } else { |
4212 | 0 | memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); |
4213 | 0 | p1->len += p2->len; |
4214 | 0 | p1->u.str8[p1->len] = '\0'; |
4215 | 0 | } |
4216 | 0 | ret_op1: |
4217 | 0 | JS_FreeValue(ctx, op2); |
4218 | 0 | return op1; |
4219 | 0 | } |
4220 | 71.9k | ret = JS_ConcatString1(ctx, p1, p2); |
4221 | 71.9k | JS_FreeValue(ctx, op1); |
4222 | 71.9k | JS_FreeValue(ctx, op2); |
4223 | 71.9k | return ret; |
4224 | 71.9k | } |
4225 | | |
4226 | | /* Shape support */ |
4227 | | |
4228 | | static inline size_t get_shape_size(size_t hash_size, size_t prop_size) |
4229 | 1.26M | { |
4230 | 1.26M | return hash_size * sizeof(uint32_t) + sizeof(JSShape) + |
4231 | 1.26M | prop_size * sizeof(JSShapeProperty); |
4232 | 1.26M | } |
4233 | | |
4234 | | static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) |
4235 | 1.26M | { |
4236 | 1.26M | return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); |
4237 | 1.26M | } |
4238 | | |
4239 | | static inline uint32_t *prop_hash_end(JSShape *sh) |
4240 | 22.3M | { |
4241 | 22.3M | return (uint32_t *)sh; |
4242 | 22.3M | } |
4243 | | |
4244 | | static inline void *get_alloc_from_shape(JSShape *sh) |
4245 | 1.91M | { |
4246 | 1.91M | return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); |
4247 | 1.91M | } |
4248 | | |
4249 | | static inline JSShapeProperty *get_shape_prop(JSShape *sh) |
4250 | 18.3M | { |
4251 | 18.3M | return sh->prop; |
4252 | 18.3M | } |
4253 | | |
4254 | | static int init_shape_hash(JSRuntime *rt) |
4255 | 3 | { |
4256 | 3 | rt->shape_hash_bits = 4; /* 16 shapes */ |
4257 | 3 | rt->shape_hash_size = 1 << rt->shape_hash_bits; |
4258 | 3 | rt->shape_hash_count = 0; |
4259 | 3 | rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * |
4260 | 3 | rt->shape_hash_size); |
4261 | 3 | if (!rt->shape_hash) |
4262 | 0 | return -1; |
4263 | 3 | return 0; |
4264 | 3 | } |
4265 | | |
4266 | | /* same magic hash multiplier as the Linux kernel */ |
4267 | | static uint32_t shape_hash(uint32_t h, uint32_t val) |
4268 | 16.0M | { |
4269 | 16.0M | return (h + val) * 0x9e370001; |
4270 | 16.0M | } |
4271 | | |
4272 | | /* truncate the shape hash to 'hash_bits' bits */ |
4273 | | static uint32_t get_shape_hash(uint32_t h, int hash_bits) |
4274 | 12.4M | { |
4275 | 12.4M | return h >> (32 - hash_bits); |
4276 | 12.4M | } |
4277 | | |
4278 | | static uint32_t shape_initial_hash(JSObject *proto) |
4279 | 1.86M | { |
4280 | 1.86M | uint32_t h; |
4281 | 1.86M | h = shape_hash(1, (uintptr_t)proto); |
4282 | 1.86M | if (sizeof(proto) > 4) |
4283 | 1.86M | h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); |
4284 | 1.86M | return h; |
4285 | 1.86M | } |
4286 | | |
4287 | | static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) |
4288 | 6 | { |
4289 | 6 | int new_shape_hash_size, i; |
4290 | 6 | uint32_t h; |
4291 | 6 | JSShape **new_shape_hash, *sh, *sh_next; |
4292 | | |
4293 | 6 | new_shape_hash_size = 1 << new_shape_hash_bits; |
4294 | 6 | new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * |
4295 | 6 | new_shape_hash_size); |
4296 | 6 | if (!new_shape_hash) |
4297 | 0 | return -1; |
4298 | 230 | for(i = 0; i < rt->shape_hash_size; i++) { |
4299 | 336 | for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { |
4300 | 112 | sh_next = sh->shape_hash_next; |
4301 | 112 | h = get_shape_hash(sh->hash, new_shape_hash_bits); |
4302 | 112 | sh->shape_hash_next = new_shape_hash[h]; |
4303 | 112 | new_shape_hash[h] = sh; |
4304 | 112 | } |
4305 | 224 | } |
4306 | 6 | js_free_rt(rt, rt->shape_hash); |
4307 | 6 | rt->shape_hash_bits = new_shape_hash_bits; |
4308 | 6 | rt->shape_hash_size = new_shape_hash_size; |
4309 | 6 | rt->shape_hash = new_shape_hash; |
4310 | 6 | return 0; |
4311 | 6 | } |
4312 | | |
4313 | | static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) |
4314 | 3.77M | { |
4315 | 3.77M | uint32_t h; |
4316 | 3.77M | h = get_shape_hash(sh->hash, rt->shape_hash_bits); |
4317 | 3.77M | sh->shape_hash_next = rt->shape_hash[h]; |
4318 | 3.77M | rt->shape_hash[h] = sh; |
4319 | 3.77M | rt->shape_hash_count++; |
4320 | 3.77M | } |
4321 | | |
4322 | | static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) |
4323 | 3.77M | { |
4324 | 3.77M | uint32_t h; |
4325 | 3.77M | JSShape **psh; |
4326 | | |
4327 | 3.77M | h = get_shape_hash(sh->hash, rt->shape_hash_bits); |
4328 | 3.77M | psh = &rt->shape_hash[h]; |
4329 | 3.77M | while (*psh != sh) |
4330 | 6 | psh = &(*psh)->shape_hash_next; |
4331 | 3.77M | *psh = sh->shape_hash_next; |
4332 | 3.77M | rt->shape_hash_count--; |
4333 | 3.77M | } |
4334 | | |
4335 | | /* create a new empty shape with prototype 'proto' */ |
4336 | | static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, |
4337 | | int hash_size, int prop_size) |
4338 | 562k | { |
4339 | 562k | JSRuntime *rt = ctx->rt; |
4340 | 562k | void *sh_alloc; |
4341 | 562k | JSShape *sh; |
4342 | | |
4343 | | /* resize the shape hash table if necessary */ |
4344 | 562k | if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { |
4345 | 6 | resize_shape_hash(rt, rt->shape_hash_bits + 1); |
4346 | 6 | } |
4347 | | |
4348 | 562k | sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); |
4349 | 562k | if (!sh_alloc) |
4350 | 5 | return NULL; |
4351 | 562k | sh = get_shape_from_alloc(sh_alloc, hash_size); |
4352 | 562k | sh->header.ref_count = 1; |
4353 | 562k | add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); |
4354 | 562k | if (proto) |
4355 | 562k | JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); |
4356 | 562k | sh->proto = proto; |
4357 | 562k | memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * |
4358 | 562k | hash_size); |
4359 | 562k | sh->prop_hash_mask = hash_size - 1; |
4360 | 562k | sh->prop_size = prop_size; |
4361 | 562k | sh->prop_count = 0; |
4362 | 562k | sh->deleted_prop_count = 0; |
4363 | | |
4364 | | /* insert in the hash table */ |
4365 | 562k | sh->hash = shape_initial_hash(proto); |
4366 | 562k | sh->is_hashed = TRUE; |
4367 | 562k | sh->has_small_array_index = FALSE; |
4368 | 562k | js_shape_hash_link(ctx->rt, sh); |
4369 | 562k | return sh; |
4370 | 562k | } |
4371 | | |
4372 | | static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) |
4373 | 562k | { |
4374 | 562k | return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, |
4375 | 562k | JS_PROP_INITIAL_SIZE); |
4376 | 562k | } |
4377 | | |
4378 | | /* The shape is cloned. The new shape is not inserted in the shape |
4379 | | hash table */ |
4380 | | static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) |
4381 | 656k | { |
4382 | 656k | JSShape *sh; |
4383 | 656k | void *sh_alloc, *sh_alloc1; |
4384 | 656k | size_t size; |
4385 | 656k | JSShapeProperty *pr; |
4386 | 656k | uint32_t i, hash_size; |
4387 | | |
4388 | 656k | hash_size = sh1->prop_hash_mask + 1; |
4389 | 656k | size = get_shape_size(hash_size, sh1->prop_size); |
4390 | 656k | sh_alloc = js_malloc(ctx, size); |
4391 | 656k | if (!sh_alloc) |
4392 | 0 | return NULL; |
4393 | 656k | sh_alloc1 = get_alloc_from_shape(sh1); |
4394 | 656k | memcpy(sh_alloc, sh_alloc1, size); |
4395 | 656k | sh = get_shape_from_alloc(sh_alloc, hash_size); |
4396 | 656k | sh->header.ref_count = 1; |
4397 | 656k | add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); |
4398 | 656k | sh->is_hashed = FALSE; |
4399 | 656k | if (sh->proto) { |
4400 | 656k | JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); |
4401 | 656k | } |
4402 | 1.40M | for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { |
4403 | 748k | JS_DupAtom(ctx, pr->atom); |
4404 | 748k | } |
4405 | 656k | return sh; |
4406 | 656k | } |
4407 | | |
4408 | | static JSShape *js_dup_shape(JSShape *sh) |
4409 | 1.81M | { |
4410 | 1.81M | sh->header.ref_count++; |
4411 | 1.81M | return sh; |
4412 | 1.81M | } |
4413 | | |
4414 | | static void js_free_shape0(JSRuntime *rt, JSShape *sh) |
4415 | 1.21M | { |
4416 | 1.21M | uint32_t i; |
4417 | 1.21M | JSShapeProperty *pr; |
4418 | | |
4419 | 1.21M | assert(sh->header.ref_count == 0); |
4420 | 1.21M | if (sh->is_hashed) |
4421 | 1.21M | js_shape_hash_unlink(rt, sh); |
4422 | 1.21M | if (sh->proto != NULL) { |
4423 | 1.21M | JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); |
4424 | 1.21M | } |
4425 | 1.21M | pr = get_shape_prop(sh); |
4426 | 4.42M | for(i = 0; i < sh->prop_count; i++) { |
4427 | 3.20M | JS_FreeAtomRT(rt, pr->atom); |
4428 | 3.20M | pr++; |
4429 | 3.20M | } |
4430 | 1.21M | remove_gc_object(&sh->header); |
4431 | 1.21M | js_free_rt(rt, get_alloc_from_shape(sh)); |
4432 | 1.21M | } |
4433 | | |
4434 | | static void js_free_shape(JSRuntime *rt, JSShape *sh) |
4435 | 2.75M | { |
4436 | 2.75M | if (unlikely(--sh->header.ref_count <= 0)) { |
4437 | 1.21M | js_free_shape0(rt, sh); |
4438 | 1.21M | } |
4439 | 2.75M | } |
4440 | | |
4441 | | static void js_free_shape_null(JSRuntime *rt, JSShape *sh) |
4442 | 0 | { |
4443 | 0 | if (sh) |
4444 | 0 | js_free_shape(rt, sh); |
4445 | 0 | } |
4446 | | |
4447 | | /* make space to hold at least 'count' properties */ |
4448 | | static no_inline int resize_properties(JSContext *ctx, JSShape **psh, |
4449 | | JSObject *p, uint32_t count) |
4450 | 42.9k | { |
4451 | 42.9k | JSShape *sh; |
4452 | 42.9k | uint32_t new_size, new_hash_size, new_hash_mask, i; |
4453 | 42.9k | JSShapeProperty *pr; |
4454 | 42.9k | void *sh_alloc; |
4455 | 42.9k | intptr_t h; |
4456 | | |
4457 | 42.9k | sh = *psh; |
4458 | 42.9k | new_size = max_int(count, sh->prop_size * 3 / 2); |
4459 | | /* Reallocate prop array first to avoid crash or size inconsistency |
4460 | | in case of memory allocation failure */ |
4461 | 42.9k | if (p) { |
4462 | 42.9k | JSProperty *new_prop; |
4463 | 42.9k | new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); |
4464 | 42.9k | if (unlikely(!new_prop)) |
4465 | 0 | return -1; |
4466 | 42.9k | p->prop = new_prop; |
4467 | 42.9k | } |
4468 | 42.9k | new_hash_size = sh->prop_hash_mask + 1; |
4469 | 43.4k | while (new_hash_size < new_size) |
4470 | 490 | new_hash_size = 2 * new_hash_size; |
4471 | 42.9k | if (new_hash_size != (sh->prop_hash_mask + 1)) { |
4472 | 490 | JSShape *old_sh; |
4473 | | /* resize the hash table and the properties */ |
4474 | 490 | old_sh = sh; |
4475 | 490 | sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); |
4476 | 490 | if (!sh_alloc) |
4477 | 0 | return -1; |
4478 | 490 | sh = get_shape_from_alloc(sh_alloc, new_hash_size); |
4479 | 490 | list_del(&old_sh->header.link); |
4480 | | /* copy all the fields and the properties */ |
4481 | 490 | memcpy(sh, old_sh, |
4482 | 490 | sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); |
4483 | 490 | list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); |
4484 | 490 | new_hash_mask = new_hash_size - 1; |
4485 | 490 | sh->prop_hash_mask = new_hash_mask; |
4486 | 490 | memset(prop_hash_end(sh) - new_hash_size, 0, |
4487 | 490 | sizeof(prop_hash_end(sh)[0]) * new_hash_size); |
4488 | 2.16M | for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { |
4489 | 2.16M | if (pr->atom != JS_ATOM_NULL) { |
4490 | 2.16M | h = ((uintptr_t)pr->atom & new_hash_mask); |
4491 | 2.16M | pr->hash_next = prop_hash_end(sh)[-h - 1]; |
4492 | 2.16M | prop_hash_end(sh)[-h - 1] = i + 1; |
4493 | 2.16M | } |
4494 | 2.16M | } |
4495 | 490 | js_free(ctx, get_alloc_from_shape(old_sh)); |
4496 | 42.4k | } else { |
4497 | | /* only resize the properties */ |
4498 | 42.4k | list_del(&sh->header.link); |
4499 | 42.4k | sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), |
4500 | 42.4k | get_shape_size(new_hash_size, new_size)); |
4501 | 42.4k | if (unlikely(!sh_alloc)) { |
4502 | | /* insert again in the GC list */ |
4503 | 0 | list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); |
4504 | 0 | return -1; |
4505 | 0 | } |
4506 | 42.4k | sh = get_shape_from_alloc(sh_alloc, new_hash_size); |
4507 | 42.4k | list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); |
4508 | 42.4k | } |
4509 | 42.9k | *psh = sh; |
4510 | 42.9k | sh->prop_size = new_size; |
4511 | 42.9k | return 0; |
4512 | 42.9k | } |
4513 | | |
4514 | | /* remove the deleted properties. */ |
4515 | | static int compact_properties(JSContext *ctx, JSObject *p) |
4516 | 0 | { |
4517 | 0 | JSShape *sh, *old_sh; |
4518 | 0 | void *sh_alloc; |
4519 | 0 | intptr_t h; |
4520 | 0 | uint32_t new_hash_size, i, j, new_hash_mask, new_size; |
4521 | 0 | JSShapeProperty *old_pr, *pr; |
4522 | 0 | JSProperty *prop, *new_prop; |
4523 | | |
4524 | 0 | sh = p->shape; |
4525 | 0 | assert(!sh->is_hashed); |
4526 | | |
4527 | 0 | new_size = max_int(JS_PROP_INITIAL_SIZE, |
4528 | 0 | sh->prop_count - sh->deleted_prop_count); |
4529 | 0 | assert(new_size <= sh->prop_size); |
4530 | | |
4531 | 0 | new_hash_size = sh->prop_hash_mask + 1; |
4532 | 0 | while ((new_hash_size / 2) >= new_size) |
4533 | 0 | new_hash_size = new_hash_size / 2; |
4534 | 0 | new_hash_mask = new_hash_size - 1; |
4535 | | |
4536 | | /* resize the hash table and the properties */ |
4537 | 0 | old_sh = sh; |
4538 | 0 | sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); |
4539 | 0 | if (!sh_alloc) |
4540 | 0 | return -1; |
4541 | 0 | sh = get_shape_from_alloc(sh_alloc, new_hash_size); |
4542 | 0 | list_del(&old_sh->header.link); |
4543 | 0 | memcpy(sh, old_sh, sizeof(JSShape)); |
4544 | 0 | list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); |
4545 | | |
4546 | 0 | memset(prop_hash_end(sh) - new_hash_size, 0, |
4547 | 0 | sizeof(prop_hash_end(sh)[0]) * new_hash_size); |
4548 | |
|
4549 | 0 | j = 0; |
4550 | 0 | old_pr = old_sh->prop; |
4551 | 0 | pr = sh->prop; |
4552 | 0 | prop = p->prop; |
4553 | 0 | for(i = 0; i < sh->prop_count; i++) { |
4554 | 0 | if (old_pr->atom != JS_ATOM_NULL) { |
4555 | 0 | pr->atom = old_pr->atom; |
4556 | 0 | pr->flags = old_pr->flags; |
4557 | 0 | h = ((uintptr_t)old_pr->atom & new_hash_mask); |
4558 | 0 | pr->hash_next = prop_hash_end(sh)[-h - 1]; |
4559 | 0 | prop_hash_end(sh)[-h - 1] = j + 1; |
4560 | 0 | prop[j] = prop[i]; |
4561 | 0 | j++; |
4562 | 0 | pr++; |
4563 | 0 | } |
4564 | 0 | old_pr++; |
4565 | 0 | } |
4566 | 0 | assert(j == (sh->prop_count - sh->deleted_prop_count)); |
4567 | 0 | sh->prop_hash_mask = new_hash_mask; |
4568 | 0 | sh->prop_size = new_size; |
4569 | 0 | sh->deleted_prop_count = 0; |
4570 | 0 | sh->prop_count = j; |
4571 | |
|
4572 | 0 | p->shape = sh; |
4573 | 0 | js_free(ctx, get_alloc_from_shape(old_sh)); |
4574 | | |
4575 | | /* reduce the size of the object properties */ |
4576 | 0 | new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); |
4577 | 0 | if (new_prop) |
4578 | 0 | p->prop = new_prop; |
4579 | 0 | return 0; |
4580 | 0 | } |
4581 | | |
4582 | | static int add_shape_property(JSContext *ctx, JSShape **psh, |
4583 | | JSObject *p, JSAtom atom, int prop_flags) |
4584 | 2.55M | { |
4585 | 2.55M | JSRuntime *rt = ctx->rt; |
4586 | 2.55M | JSShape *sh = *psh; |
4587 | 2.55M | JSShapeProperty *pr, *prop; |
4588 | 2.55M | uint32_t hash_mask, new_shape_hash = 0; |
4589 | 2.55M | intptr_t h; |
4590 | | |
4591 | | /* update the shape hash */ |
4592 | 2.55M | if (sh->is_hashed) { |
4593 | 2.55M | js_shape_hash_unlink(rt, sh); |
4594 | 2.55M | new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); |
4595 | 2.55M | } |
4596 | | |
4597 | 2.55M | if (unlikely(sh->prop_count >= sh->prop_size)) { |
4598 | 42.9k | if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { |
4599 | | /* in case of error, reinsert in the hash table. |
4600 | | sh is still valid if resize_properties() failed */ |
4601 | 0 | if (sh->is_hashed) |
4602 | 0 | js_shape_hash_link(rt, sh); |
4603 | 0 | return -1; |
4604 | 0 | } |
4605 | 42.9k | sh = *psh; |
4606 | 42.9k | } |
4607 | 2.55M | if (sh->is_hashed) { |
4608 | 2.55M | sh->hash = new_shape_hash; |
4609 | 2.55M | js_shape_hash_link(rt, sh); |
4610 | 2.55M | } |
4611 | | /* Initialize the new shape property. |
4612 | | The object property at p->prop[sh->prop_count] is uninitialized */ |
4613 | 2.55M | prop = get_shape_prop(sh); |
4614 | 2.55M | pr = &prop[sh->prop_count++]; |
4615 | 2.55M | pr->atom = JS_DupAtom(ctx, atom); |
4616 | 2.55M | pr->flags = prop_flags; |
4617 | 2.55M | sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); |
4618 | | /* add in hash table */ |
4619 | 2.55M | hash_mask = sh->prop_hash_mask; |
4620 | 2.55M | h = atom & hash_mask; |
4621 | 2.55M | pr->hash_next = prop_hash_end(sh)[-h - 1]; |
4622 | 2.55M | prop_hash_end(sh)[-h - 1] = sh->prop_count; |
4623 | 2.55M | return 0; |
4624 | 2.55M | } |
4625 | | |
4626 | | /* find a hashed empty shape matching the prototype. Return NULL if |
4627 | | not found */ |
4628 | | static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) |
4629 | 1.29M | { |
4630 | 1.29M | JSShape *sh1; |
4631 | 1.29M | uint32_t h, h1; |
4632 | | |
4633 | 1.29M | h = shape_initial_hash(proto); |
4634 | 1.29M | h1 = get_shape_hash(h, rt->shape_hash_bits); |
4635 | 1.29M | for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { |
4636 | 737k | if (sh1->hash == h && |
4637 | 737k | sh1->proto == proto && |
4638 | 737k | sh1->prop_count == 0) { |
4639 | 737k | return sh1; |
4640 | 737k | } |
4641 | 737k | } |
4642 | 562k | return NULL; |
4643 | 1.29M | } |
4644 | | |
4645 | | /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if |
4646 | | not found */ |
4647 | | static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, |
4648 | | JSAtom atom, int prop_flags) |
4649 | 3.58M | { |
4650 | 3.58M | JSShape *sh1; |
4651 | 3.58M | uint32_t h, h1, i, n; |
4652 | | |
4653 | 3.58M | h = sh->hash; |
4654 | 3.58M | h = shape_hash(h, atom); |
4655 | 3.58M | h = shape_hash(h, prop_flags); |
4656 | 3.58M | h1 = get_shape_hash(h, rt->shape_hash_bits); |
4657 | 4.97M | for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { |
4658 | | /* we test the hash first so that the rest is done only if the |
4659 | | shapes really match */ |
4660 | 2.42M | if (sh1->hash == h && |
4661 | 2.42M | sh1->proto == sh->proto && |
4662 | 2.42M | sh1->prop_count == ((n = sh->prop_count) + 1)) { |
4663 | 3.95M | for(i = 0; i < n; i++) { |
4664 | 2.91M | if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || |
4665 | 2.91M | unlikely(sh1->prop[i].flags != sh->prop[i].flags)) |
4666 | 0 | goto next; |
4667 | 2.91M | } |
4668 | 1.03M | if (unlikely(sh1->prop[n].atom != atom) || |
4669 | 1.03M | unlikely(sh1->prop[n].flags != prop_flags)) |
4670 | 0 | goto next; |
4671 | 1.03M | return sh1; |
4672 | 1.03M | } |
4673 | 1.38M | next: ; |
4674 | 1.38M | } |
4675 | 2.55M | return NULL; |
4676 | 3.58M | } |
4677 | | |
4678 | | static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) |
4679 | 0 | { |
4680 | 0 | char atom_buf[ATOM_GET_STR_BUF_SIZE]; |
4681 | 0 | int j; |
4682 | 0 |
|
4683 | 0 | /* XXX: should output readable class prototype */ |
4684 | 0 | printf("%5d %3d%c %14p %5d %5d", i, |
4685 | 0 | sh->header.ref_count, " *"[sh->is_hashed], |
4686 | 0 | (void *)sh->proto, sh->prop_size, sh->prop_count); |
4687 | 0 | for(j = 0; j < sh->prop_count; j++) { |
4688 | 0 | printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), |
4689 | 0 | sh->prop[j].atom)); |
4690 | 0 | } |
4691 | 0 | printf("\n"); |
4692 | 0 | } |
4693 | | |
4694 | | static __maybe_unused void JS_DumpShapes(JSRuntime *rt) |
4695 | 0 | { |
4696 | 0 | int i; |
4697 | 0 | JSShape *sh; |
4698 | 0 | struct list_head *el; |
4699 | 0 | JSObject *p; |
4700 | 0 | JSGCObjectHeader *gp; |
4701 | 0 | |
4702 | 0 | printf("JSShapes: {\n"); |
4703 | 0 | printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); |
4704 | 0 | for(i = 0; i < rt->shape_hash_size; i++) { |
4705 | 0 | for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { |
4706 | 0 | JS_DumpShape(rt, i, sh); |
4707 | 0 | assert(sh->is_hashed); |
4708 | 0 | } |
4709 | 0 | } |
4710 | 0 | /* dump non-hashed shapes */ |
4711 | 0 | list_for_each(el, &rt->gc_obj_list) { |
4712 | 0 | gp = list_entry(el, JSGCObjectHeader, link); |
4713 | 0 | if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { |
4714 | 0 | p = (JSObject *)gp; |
4715 | 0 | if (!p->shape->is_hashed) { |
4716 | 0 | JS_DumpShape(rt, -1, p->shape); |
4717 | 0 | } |
4718 | 0 | } |
4719 | 0 | } |
4720 | 0 | printf("}\n"); |
4721 | 0 | } |
4722 | | |
4723 | | static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) |
4724 | 1.34M | { |
4725 | 1.34M | JSObject *p; |
4726 | | |
4727 | 1.34M | js_trigger_gc(ctx->rt, sizeof(JSObject)); |
4728 | 1.34M | p = js_malloc(ctx, sizeof(JSObject)); |
4729 | 1.34M | if (unlikely(!p)) |
4730 | 0 | goto fail; |
4731 | 1.34M | p->class_id = class_id; |
4732 | 1.34M | p->extensible = TRUE; |
4733 | 1.34M | p->free_mark = 0; |
4734 | 1.34M | p->is_exotic = 0; |
4735 | 1.34M | p->fast_array = 0; |
4736 | 1.34M | p->is_constructor = 0; |
4737 | 1.34M | p->is_uncatchable_error = 0; |
4738 | 1.34M | p->tmp_mark = 0; |
4739 | 1.34M | p->is_HTMLDDA = 0; |
4740 | 1.34M | p->first_weak_ref = NULL; |
4741 | 1.34M | p->u.opaque = NULL; |
4742 | 1.34M | p->shape = sh; |
4743 | 1.34M | p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); |
4744 | 1.34M | if (unlikely(!p->prop)) { |
4745 | 4 | js_free(ctx, p); |
4746 | 4 | fail: |
4747 | 4 | js_free_shape(ctx->rt, sh); |
4748 | 4 | return JS_EXCEPTION; |
4749 | 4 | } |
4750 | | |
4751 | 1.34M | switch(class_id) { |
4752 | 226k | case JS_CLASS_OBJECT: |
4753 | 226k | break; |
4754 | 164k | case JS_CLASS_ARRAY: |
4755 | 164k | { |
4756 | 164k | JSProperty *pr; |
4757 | 164k | p->is_exotic = 1; |
4758 | 164k | p->fast_array = 1; |
4759 | 164k | p->u.array.u.values = NULL; |
4760 | 164k | p->u.array.count = 0; |
4761 | 164k | p->u.array.u1.size = 0; |
4762 | | /* the length property is always the first one */ |
4763 | 164k | if (likely(sh == ctx->array_shape)) { |
4764 | 41.5k | pr = &p->prop[0]; |
4765 | 122k | } else { |
4766 | | /* only used for the first array */ |
4767 | | /* cannot fail */ |
4768 | 122k | pr = add_property(ctx, p, JS_ATOM_length, |
4769 | 122k | JS_PROP_WRITABLE | JS_PROP_LENGTH); |
4770 | 122k | } |
4771 | 164k | pr->u.value = JS_NewInt32(ctx, 0); |
4772 | 164k | } |
4773 | 164k | break; |
4774 | 198 | case JS_CLASS_C_FUNCTION: |
4775 | 198 | p->prop[0].u.value = JS_UNDEFINED; |
4776 | 198 | break; |
4777 | 0 | case JS_CLASS_ARGUMENTS: |
4778 | 0 | case JS_CLASS_UINT8C_ARRAY: |
4779 | 0 | case JS_CLASS_INT8_ARRAY: |
4780 | 0 | case JS_CLASS_UINT8_ARRAY: |
4781 | 0 | case JS_CLASS_INT16_ARRAY: |
4782 | 0 | case JS_CLASS_UINT16_ARRAY: |
4783 | 0 | case JS_CLASS_INT32_ARRAY: |
4784 | 0 | case JS_CLASS_UINT32_ARRAY: |
4785 | 0 | #ifdef CONFIG_BIGNUM |
4786 | 0 | case JS_CLASS_BIG_INT64_ARRAY: |
4787 | 0 | case JS_CLASS_BIG_UINT64_ARRAY: |
4788 | 0 | #endif |
4789 | 0 | case JS_CLASS_FLOAT32_ARRAY: |
4790 | 0 | case JS_CLASS_FLOAT64_ARRAY: |
4791 | 0 | p->is_exotic = 1; |
4792 | 0 | p->fast_array = 1; |
4793 | 0 | p->u.array.u.ptr = NULL; |
4794 | 0 | p->u.array.count = 0; |
4795 | 0 | break; |
4796 | 0 | case JS_CLASS_DATAVIEW: |
4797 | 0 | p->u.array.u.ptr = NULL; |
4798 | 0 | p->u.array.count = 0; |
4799 | 0 | break; |
4800 | 2 | case JS_CLASS_NUMBER: |
4801 | 12 | case JS_CLASS_STRING: |
4802 | 34 | case JS_CLASS_BOOLEAN: |
4803 | 34 | case JS_CLASS_SYMBOL: |
4804 | 34 | case JS_CLASS_DATE: |
4805 | 34 | #ifdef CONFIG_BIGNUM |
4806 | 34 | case JS_CLASS_BIG_INT: |
4807 | 34 | case JS_CLASS_BIG_FLOAT: |
4808 | 34 | case JS_CLASS_BIG_DECIMAL: |
4809 | 34 | #endif |
4810 | 34 | p->u.object_data = JS_UNDEFINED; |
4811 | 34 | goto set_exotic; |
4812 | 5 | case JS_CLASS_REGEXP: |
4813 | 5 | p->u.regexp.pattern = NULL; |
4814 | 5 | p->u.regexp.bytecode = NULL; |
4815 | 5 | goto set_exotic; |
4816 | 950k | default: |
4817 | 950k | set_exotic: |
4818 | 950k | if (ctx->rt->class_array[class_id].exotic) { |
4819 | 10 | p->is_exotic = 1; |
4820 | 10 | } |
4821 | 950k | break; |
4822 | 1.34M | } |
4823 | 1.34M | p->header.ref_count = 1; |
4824 | 1.34M | add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); |
4825 | 1.34M | return JS_MKPTR(JS_TAG_OBJECT, p); |
4826 | 1.34M | } |
4827 | | |
4828 | | static JSObject *get_proto_obj(JSValueConst proto_val) |
4829 | 1.29M | { |
4830 | 1.29M | if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) |
4831 | 14 | return NULL; |
4832 | 1.29M | else |
4833 | 1.29M | return JS_VALUE_GET_OBJ(proto_val); |
4834 | 1.29M | } |
4835 | | |
4836 | | /* WARNING: proto must be an object or JS_NULL */ |
4837 | | JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, |
4838 | | JSClassID class_id) |
4839 | 1.29M | { |
4840 | 1.29M | JSShape *sh; |
4841 | 1.29M | JSObject *proto; |
4842 | | |
4843 | 1.29M | proto = get_proto_obj(proto_val); |
4844 | 1.29M | sh = find_hashed_shape_proto(ctx->rt, proto); |
4845 | 1.29M | if (likely(sh)) { |
4846 | 737k | sh = js_dup_shape(sh); |
4847 | 737k | } else { |
4848 | 562k | sh = js_new_shape(ctx, proto); |
4849 | 562k | if (!sh) |
4850 | 5 | return JS_EXCEPTION; |
4851 | 562k | } |
4852 | 1.29M | return JS_NewObjectFromShape(ctx, sh, class_id); |
4853 | 1.29M | } |
4854 | | |
4855 | | #if 0 |
4856 | | static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) |
4857 | | { |
4858 | | JSObject *p; |
4859 | | |
4860 | | if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { |
4861 | | p = JS_VALUE_GET_OBJ(obj); |
4862 | | switch(p->class_id) { |
4863 | | case JS_CLASS_NUMBER: |
4864 | | case JS_CLASS_STRING: |
4865 | | case JS_CLASS_BOOLEAN: |
4866 | | case JS_CLASS_SYMBOL: |
4867 | | case JS_CLASS_DATE: |
4868 | | #ifdef CONFIG_BIGNUM |
4869 | | case JS_CLASS_BIG_INT: |
4870 | | case JS_CLASS_BIG_FLOAT: |
4871 | | case JS_CLASS_BIG_DECIMAL: |
4872 | | #endif |
4873 | | return JS_DupValue(ctx, p->u.object_data); |
4874 | | } |
4875 | | } |
4876 | | return JS_UNDEFINED; |
4877 | | } |
4878 | | #endif |
4879 | | |
4880 | | static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) |
4881 | 34 | { |
4882 | 34 | JSObject *p; |
4883 | | |
4884 | 34 | if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { |
4885 | 34 | p = JS_VALUE_GET_OBJ(obj); |
4886 | 34 | switch(p->class_id) { |
4887 | 2 | case JS_CLASS_NUMBER: |
4888 | 12 | case JS_CLASS_STRING: |
4889 | 34 | case JS_CLASS_BOOLEAN: |
4890 | 34 | case JS_CLASS_SYMBOL: |
4891 | 34 | case JS_CLASS_DATE: |
4892 | 34 | #ifdef CONFIG_BIGNUM |
4893 | 34 | case JS_CLASS_BIG_INT: |
4894 | 34 | case JS_CLASS_BIG_FLOAT: |
4895 | 34 | case JS_CLASS_BIG_DECIMAL: |
4896 | 34 | #endif |
4897 | 34 | JS_FreeValue(ctx, p->u.object_data); |
4898 | 34 | p->u.object_data = val; |
4899 | 34 | return 0; |
4900 | 34 | } |
4901 | 34 | } |
4902 | 0 | JS_FreeValue(ctx, val); |
4903 | 0 | if (!JS_IsException(obj)) |
4904 | 0 | JS_ThrowTypeError(ctx, "invalid object type"); |
4905 | 0 | return -1; |
4906 | 34 | } |
4907 | | |
4908 | | JSValue JS_NewObjectClass(JSContext *ctx, int class_id) |
4909 | 457k | { |
4910 | 457k | return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); |
4911 | 457k | } |
4912 | | |
4913 | | JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) |
4914 | 82.4k | { |
4915 | 82.4k | return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); |
4916 | 82.4k | } |
4917 | | |
4918 | | JSValue JS_NewArray(JSContext *ctx) |
4919 | 41.5k | { |
4920 | 41.5k | return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), |
4921 | 41.5k | JS_CLASS_ARRAY); |
4922 | 41.5k | } |
4923 | | |
4924 | | JSValue JS_NewObject(JSContext *ctx) |
4925 | 144k | { |
4926 | | /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ |
4927 | 144k | return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); |
4928 | 144k | } |
4929 | | |
4930 | | static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, |
4931 | | JSAtom name, int len) |
4932 | 703k | { |
4933 | | /* ES6 feature non compatible with ES5.1: length is configurable */ |
4934 | 703k | JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), |
4935 | 703k | JS_PROP_CONFIGURABLE); |
4936 | 703k | JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, |
4937 | 703k | JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); |
4938 | 703k | } |
4939 | | |
4940 | | static BOOL js_class_has_bytecode(JSClassID class_id) |
4941 | 621k | { |
4942 | 621k | return (class_id == JS_CLASS_BYTECODE_FUNCTION || |
4943 | 621k | class_id == JS_CLASS_GENERATOR_FUNCTION || |
4944 | 621k | class_id == JS_CLASS_ASYNC_FUNCTION || |
4945 | 621k | class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); |
4946 | 621k | } |
4947 | | |
4948 | | /* return NULL without exception if not a function or no bytecode */ |
4949 | | static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) |
4950 | 122k | { |
4951 | 122k | JSObject *p; |
4952 | 122k | if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) |
4953 | 0 | return NULL; |
4954 | 122k | p = JS_VALUE_GET_OBJ(val); |
4955 | 122k | if (!js_class_has_bytecode(p->class_id)) |
4956 | 0 | return NULL; |
4957 | 122k | return p->u.func.function_bytecode; |
4958 | 122k | } |
4959 | | |
4960 | | static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, |
4961 | | JSValueConst home_obj) |
4962 | 129k | { |
4963 | 129k | JSObject *p, *p1; |
4964 | 129k | JSFunctionBytecode *b; |
4965 | | |
4966 | 129k | if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) |
4967 | 0 | return; |
4968 | 129k | p = JS_VALUE_GET_OBJ(func_obj); |
4969 | 129k | if (!js_class_has_bytecode(p->class_id)) |
4970 | 0 | return; |
4971 | 129k | b = p->u.func.function_bytecode; |
4972 | 129k | if (b->need_home_object) { |
4973 | 41.3k | p1 = p->u.func.home_object; |
4974 | 41.3k | if (p1) { |
4975 | 0 | JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); |
4976 | 0 | } |
4977 | 41.3k | if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) |
4978 | 41.3k | p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); |
4979 | 0 | else |
4980 | 0 | p1 = NULL; |
4981 | 41.3k | p->u.func.home_object = p1; |
4982 | 41.3k | } |
4983 | 129k | } |
4984 | | |
4985 | | static JSValue js_get_function_name(JSContext *ctx, JSAtom name) |
4986 | 46.8k | { |
4987 | 46.8k | JSValue name_str; |
4988 | | |
4989 | 46.8k | name_str = JS_AtomToString(ctx, name); |
4990 | 46.8k | if (JS_AtomSymbolHasDescription(ctx, name)) { |
4991 | 0 | name_str = JS_ConcatString3(ctx, "[", name_str, "]"); |
4992 | 0 | } |
4993 | 46.8k | return name_str; |
4994 | 46.8k | } |
4995 | | |
4996 | | /* Modify the name of a method according to the atom and |
4997 | | 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and |
4998 | | JS_PROP_HAS_SET. Also set the home object of the method. |
4999 | | Return < 0 if exception. */ |
5000 | | static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, |
5001 | | JSAtom name, int flags, JSValueConst home_obj) |
5002 | 46.8k | { |
5003 | 46.8k | JSValue name_str; |
5004 | | |
5005 | 46.8k | name_str = js_get_function_name(ctx, name); |
5006 | 46.8k | if (flags & JS_PROP_HAS_GET) { |
5007 | 0 | name_str = JS_ConcatString3(ctx, "get ", name_str, ""); |
5008 | 46.8k | } else if (flags & JS_PROP_HAS_SET) { |
5009 | 0 | name_str = JS_ConcatString3(ctx, "set ", name_str, ""); |
5010 | 0 | } |
5011 | 46.8k | if (JS_IsException(name_str)) |
5012 | 0 | return -1; |
5013 | 46.8k | if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, |
5014 | 46.8k | JS_PROP_CONFIGURABLE) < 0) |
5015 | 0 | return -1; |
5016 | 46.8k | js_method_set_home_object(ctx, func_obj, home_obj); |
5017 | 46.8k | return 0; |
5018 | 46.8k | } |
5019 | | |
5020 | | /* Note: at least 'length' arguments will be readable in 'argv' */ |
5021 | | static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, |
5022 | | const char *name, |
5023 | | int length, JSCFunctionEnum cproto, int magic, |
5024 | | JSValueConst proto_val) |
5025 | 198 | { |
5026 | 198 | JSValue func_obj; |
5027 | 198 | JSObject *p; |
5028 | 198 | JSAtom name_atom; |
5029 | | |
5030 | 198 | func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); |
5031 | 198 | if (JS_IsException(func_obj)) |
5032 | 0 | return func_obj; |
5033 | 198 | p = JS_VALUE_GET_OBJ(func_obj); |
5034 | 198 | p->u.cfunc.realm = JS_DupContext(ctx); |
5035 | 198 | p->u.cfunc.c_function.generic = func; |
5036 | 198 | p->u.cfunc.length = length; |
5037 | 198 | p->u.cfunc.cproto = cproto; |
5038 | 198 | p->u.cfunc.magic = magic; |
5039 | 198 | p->is_constructor = (cproto == JS_CFUNC_constructor || |
5040 | 198 | cproto == JS_CFUNC_constructor_magic || |
5041 | 198 | cproto == JS_CFUNC_constructor_or_func || |
5042 | 198 | cproto == JS_CFUNC_constructor_or_func_magic); |
5043 | 198 | if (!name) |
5044 | 4 | name = ""; |
5045 | 198 | name_atom = JS_NewAtom(ctx, name); |
5046 | 198 | js_function_set_properties(ctx, func_obj, name_atom, length); |
5047 | 198 | JS_FreeAtom(ctx, name_atom); |
5048 | 198 | return func_obj; |
5049 | 198 | } |
5050 | | |
5051 | | /* Note: at least 'length' arguments will be readable in 'argv' */ |
5052 | | JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, |
5053 | | const char *name, |
5054 | | int length, JSCFunctionEnum cproto, int magic) |
5055 | 153 | { |
5056 | 153 | return JS_NewCFunction3(ctx, func, name, length, cproto, magic, |
5057 | 153 | ctx->function_proto); |
5058 | 153 | } |
5059 | | |
5060 | | typedef struct JSCFunctionDataRecord { |
5061 | | JSCFunctionData *func; |
5062 | | uint8_t length; |
5063 | | uint8_t data_len; |
5064 | | uint16_t magic; |
5065 | | JSValue data[0]; |
5066 | | } JSCFunctionDataRecord; |
5067 | | |
5068 | | static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) |
5069 | 81.9k | { |
5070 | 81.9k | JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); |
5071 | 81.9k | int i; |
5072 | | |
5073 | 81.9k | if (s) { |
5074 | 245k | for(i = 0; i < s->data_len; i++) { |
5075 | 163k | JS_FreeValueRT(rt, s->data[i]); |
5076 | 163k | } |
5077 | 81.9k | js_free_rt(rt, s); |
5078 | 81.9k | } |
5079 | 81.9k | } |
5080 | | |
5081 | | static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, |
5082 | | JS_MarkFunc *mark_func) |
5083 | 10 | { |
5084 | 10 | JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); |
5085 | 10 | int i; |
5086 | | |
5087 | 10 | if (s) { |
5088 | 30 | for(i = 0; i < s->data_len; i++) { |
5089 | 20 | JS_MarkValue(rt, s->data[i], mark_func); |
5090 | 20 | } |
5091 | 10 | } |
5092 | 10 | } |
5093 | | |
5094 | | static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, |
5095 | | JSValueConst this_val, |
5096 | | int argc, JSValueConst *argv, int flags) |
5097 | 81.9k | { |
5098 | 81.9k | JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); |
5099 | 81.9k | JSValueConst *arg_buf; |
5100 | 81.9k | int i; |
5101 | | |
5102 | | /* XXX: could add the function on the stack for debug */ |
5103 | 81.9k | if (unlikely(argc < s->length)) { |
5104 | 0 | arg_buf = alloca(sizeof(arg_buf[0]) * s->length); |
5105 | 0 | for(i = 0; i < argc; i++) |
5106 | 0 | arg_buf[i] = argv[i]; |
5107 | 0 | for(i = argc; i < s->length; i++) |
5108 | 0 | arg_buf[i] = JS_UNDEFINED; |
5109 | 81.9k | } else { |
5110 | 81.9k | arg_buf = argv; |
5111 | 81.9k | } |
5112 | | |
5113 | 81.9k | return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); |
5114 | 81.9k | } |
5115 | | |
5116 | | JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, |
5117 | | int length, int magic, int data_len, |
5118 | | JSValueConst *data) |
5119 | 81.9k | { |
5120 | 81.9k | JSCFunctionDataRecord *s; |
5121 | 81.9k | JSValue func_obj; |
5122 | 81.9k | int i; |
5123 | | |
5124 | 81.9k | func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, |
5125 | 81.9k | JS_CLASS_C_FUNCTION_DATA); |
5126 | 81.9k | if (JS_IsException(func_obj)) |
5127 | 0 | return func_obj; |
5128 | 81.9k | s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); |
5129 | 81.9k | if (!s) { |
5130 | 0 | JS_FreeValue(ctx, func_obj); |
5131 | 0 | return JS_EXCEPTION; |
5132 | 0 | } |
5133 | 81.9k | s->func = func; |
5134 | 81.9k | s->length = length; |
5135 | 81.9k | s->data_len = data_len; |
5136 | 81.9k | s->magic = magic; |
5137 | 245k | for(i = 0; i < data_len; i++) |
5138 | 163k | s->data[i] = JS_DupValue(ctx, data[i]); |
5139 | 81.9k | JS_SetOpaque(func_obj, s); |
5140 | 81.9k | js_function_set_properties(ctx, func_obj, |
5141 | 81.9k | JS_ATOM_empty_string, length); |
5142 | 81.9k | return func_obj; |
5143 | 81.9k | } |
5144 | | |
5145 | | static JSContext *js_autoinit_get_realm(JSProperty *pr) |
5146 | 16.2k | { |
5147 | 16.2k | return (JSContext *)(pr->u.init.realm_and_id & ~3); |
5148 | 16.2k | } |
5149 | | |
5150 | | static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) |
5151 | 31 | { |
5152 | 31 | return pr->u.init.realm_and_id & 3; |
5153 | 31 | } |
5154 | | |
5155 | | static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) |
5156 | 812 | { |
5157 | 812 | JS_FreeContext(js_autoinit_get_realm(pr)); |
5158 | 812 | } |
5159 | | |
5160 | | static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, |
5161 | | JS_MarkFunc *mark_func) |
5162 | 15.4k | { |
5163 | 15.4k | mark_func(rt, &js_autoinit_get_realm(pr)->header); |
5164 | 15.4k | } |
5165 | | |
5166 | | static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) |
5167 | 3.05M | { |
5168 | 3.05M | if (unlikely(prop_flags & JS_PROP_TMASK)) { |
5169 | 783 | if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { |
5170 | 0 | if (pr->u.getset.getter) |
5171 | 0 | JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); |
5172 | 0 | if (pr->u.getset.setter) |
5173 | 0 | JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); |
5174 | 783 | } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { |
5175 | 2 | free_var_ref(rt, pr->u.var_ref); |
5176 | 781 | } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { |
5177 | 781 | js_autoinit_free(rt, pr); |
5178 | 781 | } |
5179 | 3.05M | } else { |
5180 | 3.05M | JS_FreeValueRT(rt, pr->u.value); |
5181 | 3.05M | } |
5182 | 3.05M | } |
5183 | | |
5184 | | static force_inline JSShapeProperty *find_own_property1(JSObject *p, |
5185 | | JSAtom atom) |
5186 | 168k | { |
5187 | 168k | JSShape *sh; |
5188 | 168k | JSShapeProperty *pr, *prop; |
5189 | 168k | intptr_t h; |
5190 | 168k | sh = p->shape; |
5191 | 168k | h = (uintptr_t)atom & sh->prop_hash_mask; |
5192 | 168k | h = prop_hash_end(sh)[-h - 1]; |
5193 | 168k | prop = get_shape_prop(sh); |
5194 | 169k | while (h) { |
5195 | 44.6k | pr = &prop[h - 1]; |
5196 | 44.6k | if (likely(pr->atom == atom)) { |
5197 | 43.1k | return pr; |
5198 | 43.1k | } |
5199 | 1.52k | h = pr->hash_next; |
5200 | 1.52k | } |
5201 | 124k | return NULL; |
5202 | 168k | } |
5203 | | |
5204 | | static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, |
5205 | | JSObject *p, |
5206 | | JSAtom atom) |
5207 | 10.2M | { |
5208 | 10.2M | JSShape *sh; |
5209 | 10.2M | JSShapeProperty *pr, *prop; |
5210 | 10.2M | intptr_t h; |
5211 | 10.2M | sh = p->shape; |
5212 | 10.2M | h = (uintptr_t)atom & sh->prop_hash_mask; |
5213 | 10.2M | h = prop_hash_end(sh)[-h - 1]; |
5214 | 10.2M | prop = get_shape_prop(sh); |
5215 | 11.3M | while (h) { |
5216 | 3.96M | pr = &prop[h - 1]; |
5217 | 3.96M | if (likely(pr->atom == atom)) { |
5218 | 2.78M | *ppr = &p->prop[h - 1]; |
5219 | | /* the compiler should be able to assume that pr != NULL here */ |
5220 | 2.78M | return pr; |
5221 | 2.78M | } |
5222 | 1.17M | h = pr->hash_next; |
5223 | 1.17M | } |
5224 | 7.43M | *ppr = NULL; |
5225 | 7.43M | return NULL; |
5226 | 10.2M | } |
5227 | | |
5228 | | /* indicate that the object may be part of a function prototype cycle */ |
5229 | | static void set_cycle_flag(JSContext *ctx, JSValueConst obj) |
5230 | 82.8k | { |
5231 | 82.8k | } |
5232 | | |
5233 | | static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) |
5234 | 8.24M | { |
5235 | 8.24M | if (var_ref) { |
5236 | 8.24M | assert(var_ref->header.ref_count > 0); |
5237 | 8.24M | if (--var_ref->header.ref_count == 0) { |
5238 | 160k | if (var_ref->is_detached) { |
5239 | 160k | JS_FreeValueRT(rt, var_ref->value); |
5240 | 160k | remove_gc_object(&var_ref->header); |
5241 | 160k | } else { |
5242 | 60 | list_del(&var_ref->header.link); /* still on the stack */ |
5243 | 60 | } |
5244 | 160k | js_free_rt(rt, var_ref); |
5245 | 160k | } |
5246 | 8.24M | } |
5247 | 8.24M | } |
5248 | | |
5249 | | static void js_array_finalizer(JSRuntime *rt, JSValue val) |
5250 | 164k | { |
5251 | 164k | JSObject *p = JS_VALUE_GET_OBJ(val); |
5252 | 164k | int i; |
5253 | | |
5254 | 317k | for(i = 0; i < p->u.array.count; i++) { |
5255 | 152k | JS_FreeValueRT(rt, p->u.array.u.values[i]); |
5256 | 152k | } |
5257 | 164k | js_free_rt(rt, p->u.array.u.values); |
5258 | 164k | } |
5259 | | |
5260 | | static void js_array_mark(JSRuntime *rt, JSValueConst val, |
5261 | | JS_MarkFunc *mark_func) |
5262 | 132 | { |
5263 | 132 | JSObject *p = JS_VALUE_GET_OBJ(val); |
5264 | 132 | int i; |
5265 | | |
5266 | 105k | for(i = 0; i < p->u.array.count; i++) { |
5267 | 105k | JS_MarkValue(rt, p->u.array.u.values[i], mark_func); |
5268 | 105k | } |
5269 | 132 | } |
5270 | | |
5271 | | static void js_object_data_finalizer(JSRuntime *rt, JSValue val) |
5272 | 28 | { |
5273 | 28 | JSObject *p = JS_VALUE_GET_OBJ(val); |
5274 | 28 | JS_FreeValueRT(rt, p->u.object_data); |
5275 | 28 | p->u.object_data = JS_UNDEFINED; |
5276 | 28 | } |
5277 | | |
5278 | | static void js_object_data_mark(JSRuntime *rt, JSValueConst val, |
5279 | | JS_MarkFunc *mark_func) |
5280 | 138 | { |
5281 | 138 | JSObject *p = JS_VALUE_GET_OBJ(val); |
5282 | 138 | JS_MarkValue(rt, p->u.object_data, mark_func); |
5283 | 138 | } |
5284 | | |
5285 | | static void js_c_function_finalizer(JSRuntime *rt, JSValue val) |
5286 | 0 | { |
5287 | 0 | JSObject *p = JS_VALUE_GET_OBJ(val); |
5288 | |
|
5289 | 0 | if (p->u.cfunc.realm) |
5290 | 0 | JS_FreeContext(p->u.cfunc.realm); |
5291 | 0 | } |
5292 | | |
5293 | | static void js_c_function_mark(JSRuntime *rt, JSValueConst val, |
5294 | | JS_MarkFunc *mark_func) |
5295 | 4.42k | { |
5296 | 4.42k | JSObject *p = JS_VALUE_GET_OBJ(val); |
5297 | | |
5298 | 4.42k | if (p->u.cfunc.realm) |
5299 | 4.42k | mark_func(rt, &p->u.cfunc.realm->header); |
5300 | 4.42k | } |
5301 | | |
5302 | | static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) |
5303 | 476k | { |
5304 | 476k | JSObject *p1, *p = JS_VALUE_GET_OBJ(val); |
5305 | 476k | JSFunctionBytecode *b; |
5306 | 476k | JSVarRef **var_refs; |
5307 | 476k | int i; |
5308 | | |
5309 | 476k | p1 = p->u.func.home_object; |
5310 | 476k | if (p1) { |
5311 | 33.8k | JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); |
|