quickjs.c (2029740B)
1 /* 2 * QuickJS Javascript Engine 3 * 4 * Copyright (c) 2017-2025 Fabrice Bellard 5 * Copyright (c) 2017-2025 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__) || defined(__GLIBC__) 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 #include "libunicode.h" 48 #include "dtoa.h" 49 50 #define OPTIMIZE 1 51 #define SHORT_OPCODES 1 52 #if defined(__EMSCRIPTEN__) 53 #define DIRECT_DISPATCH 0 54 #else 55 #define DIRECT_DISPATCH 1 56 #endif 57 58 #if defined(__APPLE__) 59 #define MALLOC_OVERHEAD 0 60 #else 61 #define MALLOC_OVERHEAD 8 62 #endif 63 64 #if !defined(_WIN32) 65 /* define it if printf uses the RNDN rounding mode instead of RNDNA */ 66 #define CONFIG_PRINTF_RNDN 67 #endif 68 69 /* define to include Atomics.* operations which depend on the OS 70 threads */ 71 #if !defined(__EMSCRIPTEN__) 72 #define CONFIG_ATOMICS 73 #endif 74 75 #if !defined(__EMSCRIPTEN__) 76 /* enable stack limitation */ 77 #define CONFIG_STACK_CHECK 78 #endif 79 80 81 /* dump object free */ 82 //#define DUMP_FREE 83 //#define DUMP_CLOSURE 84 /* dump the bytecode of the compiled functions: combination of bits 85 1: dump pass 3 final byte code 86 2: dump pass 2 code 87 4: dump pass 1 code 88 8: dump stdlib functions 89 16: dump bytecode in hex 90 32: dump line number table 91 64: dump compute_stack_size 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_MODULE_EXEC 107 //#define DUMP_PROMISE 108 //#define DUMP_READ_OBJECT 109 //#define DUMP_ROPE_REBALANCE 110 /* add asm labels to each opcode so that it is easier to see the generated code */ 111 //#define OPCODE_ASM_LABEL 112 113 /* test the GC by forcing it before each object allocation */ 114 //#define FORCE_GC_AT_MALLOC 115 116 /* use function call trampolines for better output in profiling tools */ 117 //#define PERF_TRAMPOLINE 118 119 #ifdef CONFIG_ATOMICS 120 #include <pthread.h> 121 #include <stdatomic.h> 122 #include <errno.h> 123 #endif 124 125 enum { 126 /* classid tag */ /* union usage | properties */ 127 JS_CLASS_OBJECT = 1, /* must be first */ 128 JS_CLASS_ARRAY, /* u.array | length */ 129 JS_CLASS_ERROR, 130 JS_CLASS_NUMBER, /* u.object_data */ 131 JS_CLASS_STRING, /* u.object_data */ 132 JS_CLASS_BOOLEAN, /* u.object_data */ 133 JS_CLASS_SYMBOL, /* u.object_data */ 134 JS_CLASS_ARGUMENTS, /* u.array | length */ 135 JS_CLASS_MAPPED_ARGUMENTS, /* u.array | length */ 136 JS_CLASS_DATE, /* u.object_data */ 137 JS_CLASS_MODULE_NS, 138 JS_CLASS_C_FUNCTION, /* u.cfunc */ 139 JS_CLASS_BYTECODE_FUNCTION, /* u.func */ 140 JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ 141 JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ 142 JS_CLASS_GENERATOR_FUNCTION, /* u.func */ 143 JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ 144 JS_CLASS_REGEXP, /* u.regexp */ 145 JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ 146 JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ 147 JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ 148 JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ 149 JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ 150 JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ 151 JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ 152 JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ 153 JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ 154 JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ 155 JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ 156 JS_CLASS_FLOAT16_ARRAY, /* u.array (typed_array) */ 157 JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ 158 JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ 159 JS_CLASS_DATAVIEW, /* u.typed_array */ 160 JS_CLASS_BIG_INT, /* u.object_data */ 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_ITERATOR, /* u.map_iterator_data */ 166 JS_CLASS_ITERATOR_CONCAT, /* u.iterator_concat_data */ 167 JS_CLASS_ITERATOR_HELPER, /* u.iterator_helper_data */ 168 JS_CLASS_ITERATOR_WRAP, /* u.iterator_wrap_data */ 169 JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ 170 JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ 171 JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ 172 JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ 173 JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ 174 JS_CLASS_GENERATOR, /* u.generator_data */ 175 JS_CLASS_GLOBAL_OBJECT, /* u.global_object */ 176 JS_CLASS_RAWJSON, 177 JS_CLASS_PROXY, /* u.proxy_data */ 178 JS_CLASS_PROMISE, /* u.promise_data */ 179 JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ 180 JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ 181 JS_CLASS_ASYNC_FUNCTION, /* u.func */ 182 JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ 183 JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ 184 JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ 185 JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ 186 JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ 187 JS_CLASS_WEAK_REF, 188 JS_CLASS_FINALIZATION_REGISTRY, 189 190 JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ 191 }; 192 193 /* number of typed array types */ 194 #define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) 195 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; 196 #define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) 197 198 typedef enum JSErrorEnum { 199 JS_EVAL_ERROR, 200 JS_RANGE_ERROR, 201 JS_REFERENCE_ERROR, 202 JS_SYNTAX_ERROR, 203 JS_TYPE_ERROR, 204 JS_URI_ERROR, 205 JS_INTERNAL_ERROR, 206 JS_AGGREGATE_ERROR, 207 208 JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ 209 } JSErrorEnum; 210 211 /* the variable and scope indexes must fit on 16 bits. The (-1) and 212 ARG_SCOPE_END values are reserved. */ 213 #define JS_MAX_LOCAL_VARS 65534 214 #define JS_STACK_SIZE_MAX 65534 215 #define JS_STRING_LEN_MAX ((1 << 30) - 1) 216 217 /* strings <= this length are not concatenated using ropes. if too 218 small, the rope memory overhead becomes high. */ 219 #define JS_STRING_ROPE_SHORT_LEN 512 220 /* specific threshold for initial rope use */ 221 #define JS_STRING_ROPE_SHORT2_LEN 8192 222 /* rope depth at which we rebalance */ 223 #define JS_STRING_ROPE_MAX_DEPTH 60 224 225 #define __exception __attribute__((warn_unused_result)) 226 227 typedef struct JSShape JSShape; 228 typedef struct JSString JSString; 229 typedef struct JSString JSAtomStruct; 230 typedef struct JSObject JSObject; 231 232 #define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v)) 233 #define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) 234 #define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v)) 235 236 typedef enum { 237 JS_GC_PHASE_NONE, 238 JS_GC_PHASE_DECREF, 239 JS_GC_PHASE_REMOVE_CYCLES, 240 } JSGCPhaseEnum; 241 242 typedef enum OPCodeEnum OPCodeEnum; 243 244 /* JS malloc */ 245 246 #define JS_MALLOC_ALIGN 8 247 #define JS_MALLOC_ARENA_SIZE 4096 248 #define JS_MALLOC_BLOCK_SIZE_COUNT 31 249 #define JS_MALLOC_MIN_SMALL_SIZE 16 250 #define JS_MALLOC_MAX_SMALL_SIZE 512 251 #if defined(__SANITIZE_ADDRESS__) 252 /* use the host malloc() for all allocations */ 253 #define JS_MALLOC_LARGE_BLOCKS_ONLY 1 254 #else 255 #define JS_MALLOC_LARGE_BLOCKS_ONLY 0 256 #endif 257 258 /* allow iteration among the allocated blocks. Currently not used. May 259 be used to suppress the memory overhead of JSGCObjectHeader */ 260 //#define JS_MALLOC_USE_ITER 261 262 #define FREE_NIL 0xffff 263 264 /* 8 byte header */ 265 /* Notes: 266 - the header is necessary at least to recover a pointer to 267 JSMallocArena because we don't want to enforce a page 268 alignment on the system malloc(). 269 - could store the block offset instead of (block_idx, 270 block_size_idx), but it would require a division to recover the block 271 index. 272 */ 273 typedef struct JSMallocBlockHeader { 274 union { 275 uint16_t block_idx; /* FREE_NIL if large block */ 276 uint16_t free_next; /* FREE_NIL if none */ 277 } u; 278 uint8_t block_size_idx; 279 uint8_t gc_obj_type : 7; 280 uint8_t mark : 1; 281 int ref_count; 282 __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t user_data[]; 283 } JSMallocBlockHeader; 284 285 typedef struct JSMallocLargeBlockHeader { 286 #ifdef JS_MALLOC_USE_ITER 287 struct list_head link; 288 #endif 289 JSMallocBlockHeader header; 290 } JSMallocLargeBlockHeader; 291 292 typedef struct { 293 struct list_head free_link; 294 struct list_head link; 295 uint8_t block_size_idx; 296 uint16_t n_used_blocks; /* number of allocated blocks */ 297 uint16_t n_blocks; /* total number of blocks */ 298 uint16_t first_free_block; /* FREE_NIL if none */ 299 #ifdef JS_MALLOC_USE_ITER 300 /* bit set to 1 for allocated block */ 301 uint32_t bitmap[((JS_MALLOC_ARENA_SIZE / JS_MALLOC_MIN_SMALL_SIZE) + 31) / 32]; 302 #endif 303 /* n_blocks memory blocks of identical size */ 304 __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t blocks[]; 305 } JSMallocArena; 306 307 typedef struct { 308 struct list_head arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.link (all arenas) */ 309 struct list_head free_arena_list[JS_MALLOC_BLOCK_SIZE_COUNT]; /* list of JSMallocArena.free_link (arenas where n_used_blocks < n_blocks) */ 310 #ifdef JS_MALLOC_USE_ITER 311 struct list_head large_block_list; /* list of JSMallocLargeBlockHeader.link */ 312 #endif 313 __attribute__((aligned(JS_MALLOC_ALIGN))) uint8_t zero_size_block[sizeof(JSMallocBlockHeader)]; 314 315 /* callbacks to the host malloc */ 316 JSMallocFunctions mf; 317 JSMallocState malloc_state; 318 } JSMallocContext; 319 320 /* end JS Malloc */ 321 322 struct JSRuntime { 323 JSMallocContext malloc_ctx; 324 const char *rt_info; 325 326 int atom_hash_size; /* power of two */ 327 int atom_count; 328 int atom_size; 329 int atom_count_resize; /* resize hash table at this count */ 330 uint32_t *atom_hash; 331 JSAtomStruct **atom_array; 332 int atom_free_index; /* 0 = none */ 333 334 int class_count; /* size of class_array */ 335 JSClass *class_array; 336 337 struct list_head context_list; /* list of JSContext.link */ 338 /* list of JSGCObjectHeader.link. List of allocated GC objects (used 339 by the garbage collector) */ 340 struct list_head gc_obj_list; 341 /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ 342 struct list_head gc_zero_ref_count_list; 343 struct list_head tmp_obj_list; /* used during GC */ 344 JSGCPhaseEnum gc_phase : 8; 345 size_t malloc_gc_threshold; 346 struct list_head weakref_list; /* list of JSWeakRefHeader.link */ 347 #ifdef DUMP_LEAKS 348 struct list_head string_list; /* list of JSString.link */ 349 #endif 350 /* stack limitation */ 351 uintptr_t stack_size; /* in bytes, 0 if no limit */ 352 uintptr_t stack_top; 353 uintptr_t stack_limit; /* lower stack limit */ 354 355 JSValue current_exception; 356 /* true if the current exception cannot be catched */ 357 BOOL current_exception_is_uncatchable : 8; 358 /* true if inside an out of memory error, to avoid recursing */ 359 BOOL in_out_of_memory : 8; 360 361 struct JSStackFrame *current_stack_frame; 362 363 JSInterruptHandler *interrupt_handler; 364 void *interrupt_opaque; 365 366 JSHostPromiseRejectionTracker *host_promise_rejection_tracker; 367 void *host_promise_rejection_tracker_opaque; 368 369 struct list_head job_list; /* list of JSJobEntry.link */ 370 371 JSModuleNormalizeFunc *module_normalize_func; 372 BOOL module_loader_has_attr; 373 union { 374 JSModuleLoaderFunc *module_loader_func; 375 JSModuleLoaderFunc2 *module_loader_func2; 376 } u; 377 JSModuleCheckSupportedImportAttributes *module_check_attrs; 378 void *module_loader_opaque; 379 /* timestamp for internal use in module evaluation */ 380 int64_t module_async_evaluation_next_timestamp; 381 382 BOOL can_block : 8; /* TRUE if Atomics.wait can block */ 383 /* used to allocate, free and clone SharedArrayBuffers */ 384 JSSharedArrayBufferFunctions sab_funcs; 385 /* see JS_SetStripInfo() */ 386 uint8_t strip_flags; 387 388 /* Shape hash table */ 389 int shape_hash_bits; 390 int shape_hash_size; 391 int shape_hash_count; /* number of hashed shapes */ 392 JSShape **shape_hash; 393 void *user_opaque; 394 }; 395 396 struct JSClass { 397 uint32_t class_id; /* 0 means free entry */ 398 JSAtom class_name; 399 JSClassFinalizer *finalizer; 400 JSClassGCMark *gc_mark; 401 JSClassCall *call; 402 /* pointers for exotic behavior, can be NULL if none are present */ 403 const JSClassExoticMethods *exotic; 404 }; 405 406 #define JS_MODE_STRICT (1 << 0) 407 #define JS_MODE_ASYNC (1 << 2) /* async function */ 408 #define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */ 409 410 typedef struct JSStackFrame { 411 struct JSStackFrame *prev_frame; /* NULL if first stack frame */ 412 JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ 413 JSValue *arg_buf; /* arguments */ 414 JSValue *var_buf; /* variables */ 415 struct JSVarRef **var_refs; /* references to arguments or local variables */ 416 const uint8_t *cur_pc; /* only used in bytecode functions : PC of the 417 instruction after the call */ 418 int arg_count; 419 int js_mode; /* not supported for C functions */ 420 /* only used in generators. Current stack pointer value. NULL if 421 the function is running. */ 422 JSValue *cur_sp; 423 } JSStackFrame; 424 425 typedef enum { 426 JS_GC_OBJ_TYPE_JS_OBJECT, 427 JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, 428 JS_GC_OBJ_TYPE_SHAPE, 429 JS_GC_OBJ_TYPE_VAR_REF, 430 JS_GC_OBJ_TYPE_ASYNC_FUNCTION, 431 JS_GC_OBJ_TYPE_JS_CONTEXT, 432 JS_GC_OBJ_TYPE_MODULE, 433 } JSGCObjectTypeEnum; 434 435 /* header for GC objects. GC objects are C data structures with a 436 reference count that can reference other GC objects. JS Objects are 437 a particular type of GC object. */ 438 struct JSGCObjectHeader { 439 struct list_head link; 440 }; 441 442 typedef enum { 443 JS_WEAKREF_TYPE_MAP, 444 JS_WEAKREF_TYPE_WEAKREF, 445 JS_WEAKREF_TYPE_FINREC, 446 } JSWeakRefHeaderTypeEnum; 447 448 typedef struct { 449 struct list_head link; 450 JSWeakRefHeaderTypeEnum weakref_type; 451 } JSWeakRefHeader; 452 453 typedef struct JSVarRef { 454 JSGCObjectHeader header; /* must come first */ 455 uint8_t is_detached; 456 uint8_t is_lexical; /* only used with global variables */ 457 uint8_t is_const; /* only used with global variables */ 458 JSValue *pvalue; /* pointer to the value, either on the stack or 459 to 'value' */ 460 union { 461 JSValue value; /* used when is_detached = TRUE */ 462 struct { 463 uint16_t var_ref_idx; /* index in JSStackFrame.var_refs[] */ 464 JSStackFrame *stack_frame; 465 }; /* used when is_detached = FALSE */ 466 }; 467 } JSVarRef; 468 469 /* bigint */ 470 471 #if JS_LIMB_BITS == 32 472 473 typedef int32_t js_slimb_t; 474 typedef uint32_t js_limb_t; 475 typedef int64_t js_sdlimb_t; 476 typedef uint64_t js_dlimb_t; 477 478 #define JS_LIMB_DIGITS 9 479 480 #else 481 482 typedef __int128 int128_t; 483 typedef unsigned __int128 uint128_t; 484 typedef int64_t js_slimb_t; 485 typedef uint64_t js_limb_t; 486 typedef int128_t js_sdlimb_t; 487 typedef uint128_t js_dlimb_t; 488 489 #define JS_LIMB_DIGITS 19 490 491 #endif 492 493 typedef struct JSBigInt { 494 uint32_t len; /* number of limbs, >= 1 */ 495 js_limb_t tab[]; /* two's complement representation, always 496 normalized so that 'len' is the minimum 497 possible length >= 1 */ 498 } JSBigInt; 499 500 /* this bigint structure can hold a 64 bit integer */ 501 typedef struct { 502 js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */ 503 /* must come just after */ 504 js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS]; 505 } JSBigIntBuf; 506 507 typedef enum { 508 JS_AUTOINIT_ID_PROTOTYPE, 509 JS_AUTOINIT_ID_MODULE_NS, 510 JS_AUTOINIT_ID_PROP, 511 } JSAutoInitIDEnum; 512 513 /* must be large enough to have a negligible runtime cost and small 514 enough to call the interrupt callback often. */ 515 #define JS_INTERRUPT_COUNTER_INIT 10000 516 517 struct JSContext { 518 JSGCObjectHeader header; /* must come first */ 519 JSRuntime *rt; 520 struct list_head link; 521 522 uint16_t binary_object_count; 523 int binary_object_size; 524 525 JSShape *array_shape; /* initial shape for Array objects */ 526 JSShape *arguments_shape; /* shape for arguments objects */ 527 JSShape *mapped_arguments_shape; /* shape for mapped arguments objects */ 528 JSShape *regexp_shape; /* shape for regexp objects */ 529 JSShape *regexp_result_shape; /* shape for regexp result objects */ 530 531 JSValue *class_proto; 532 JSValue function_proto; 533 JSValue function_ctor; 534 JSValue array_ctor; 535 JSValue regexp_ctor; 536 JSValue promise_ctor; 537 JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; 538 JSValue iterator_ctor; 539 JSValue async_iterator_proto; 540 JSValue array_proto_values; 541 JSValue throw_type_error; 542 JSValue eval_obj; 543 544 JSValue global_obj; /* global object */ 545 JSValue global_var_obj; /* contains the global let/const definitions */ 546 547 uint64_t random_state; 548 549 /* when the counter reaches zero, JSRutime.interrupt_handler is called */ 550 int interrupt_counter; 551 552 struct list_head loaded_modules; /* list of JSModuleDef.link */ 553 554 /* if NULL, RegExp compilation is not supported */ 555 JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, 556 JSValueConst flags); 557 /* if NULL, eval is not supported */ 558 JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, 559 const char *input, size_t input_len, 560 const char *filename, int flags, int scope_idx); 561 void *user_opaque; 562 }; 563 564 typedef union JSFloat64Union { 565 double d; 566 uint64_t u64; 567 uint32_t u32[2]; 568 } JSFloat64Union; 569 570 enum { 571 JS_ATOM_TYPE_STRING = 1, 572 JS_ATOM_TYPE_GLOBAL_SYMBOL, 573 JS_ATOM_TYPE_SYMBOL, 574 JS_ATOM_TYPE_PRIVATE, 575 }; 576 577 typedef enum { 578 JS_ATOM_KIND_STRING, 579 JS_ATOM_KIND_SYMBOL, 580 JS_ATOM_KIND_PRIVATE, 581 } JSAtomKindEnum; 582 583 #define JS_ATOM_HASH_MASK ((1 << 30) - 1) 584 #define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK 585 586 struct JSString { 587 uint32_t len : 31; 588 uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ 589 /* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3, 590 for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3 591 XXX: could change encoding to have one more bit in hash */ 592 uint32_t hash : 30; 593 uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ 594 uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ 595 #ifdef DUMP_LEAKS 596 struct list_head link; /* string list */ 597 #endif 598 union { 599 uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ 600 uint16_t str16[0]; 601 } u; 602 }; 603 604 typedef struct JSStringRope { 605 uint32_t len; 606 uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */ 607 uint8_t depth; /* max depth of the rope tree */ 608 /* XXX: could reduce memory usage by using a direct pointer with 609 bit 0 to select rope or string */ 610 JSValue left; 611 JSValue right; /* might be the empty string */ 612 } JSStringRope; 613 614 typedef enum { 615 JS_CLOSURE_LOCAL, /* 'var_idx' is the index of a local variable in the parent function */ 616 JS_CLOSURE_ARG, /* 'var_idx' is the index of a argument variable in the parent function */ 617 JS_CLOSURE_REF, /* 'var_idx' is the index of a closure variable in the parent function */ 618 JS_CLOSURE_GLOBAL_REF, /* 'var_idx' in the index of a closure 619 variable in the parent function 620 referencing a global variable */ 621 JS_CLOSURE_GLOBAL_DECL, /* global variable declaration (eval code only) */ 622 JS_CLOSURE_GLOBAL, /* global variable (eval code only) */ 623 JS_CLOSURE_MODULE_DECL, /* definition of a module variable (eval code only) */ 624 JS_CLOSURE_MODULE_IMPORT, /* definition of a module import (eval code only) */ 625 } JSClosureTypeEnum; 626 627 typedef struct JSClosureVar { 628 JSClosureTypeEnum closure_type : 3; 629 uint8_t is_lexical : 1; /* lexical variable */ 630 uint8_t is_const : 1; /* const variable (is_lexical = 1 if is_const = 1 */ 631 uint8_t var_kind : 4; /* see JSVarKindEnum */ 632 uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the 633 parent function. otherwise: index to a closure 634 variable of the parent function */ 635 JSAtom var_name; 636 } JSClosureVar; 637 638 #define ARG_SCOPE_INDEX 1 639 #define ARG_SCOPE_END (-2) 640 641 typedef enum { 642 /* XXX: add more variable kinds here instead of using bit fields */ 643 JS_VAR_NORMAL, 644 JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ 645 JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator 646 function declaration */ 647 JS_VAR_CATCH, 648 JS_VAR_FUNCTION_NAME, /* function expression name */ 649 JS_VAR_PRIVATE_FIELD, 650 JS_VAR_PRIVATE_METHOD, 651 JS_VAR_PRIVATE_GETTER, 652 JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ 653 JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ 654 JS_VAR_GLOBAL_FUNCTION_DECL, /* global function definition, only in JSVarDef */ 655 } JSVarKindEnum; 656 657 typedef struct JSBytecodeVarDef { 658 JSAtom var_name; 659 /* index into JSFunctionBytecode.vars of the next variable in the same or 660 enclosing lexical scope 661 */ 662 int scope_next; /* XXX: store on 16 bits */ 663 uint8_t is_const : 1; 664 uint8_t is_lexical : 1; 665 uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */ 666 uint8_t has_scope: 1; /* true if JSVarDef.scope_level != 0 */ 667 uint8_t var_kind : 4; /* see JSVarKindEnum */ 668 /* If is_captured = TRUE, provides, the index of the corresponding 669 JSVarRef on stack. It would be more compact to have a separate 670 table with the corresponding inverted table but it requires 671 more modifications in the code. */ 672 uint16_t var_ref_idx; 673 } JSBytecodeVarDef; 674 675 /* for the encoding of the pc2line table */ 676 #define PC2LINE_BASE (-1) 677 #define PC2LINE_RANGE 5 678 #define PC2LINE_OP_FIRST 1 679 #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) 680 681 typedef enum JSFunctionKindEnum { 682 JS_FUNC_NORMAL = 0, 683 JS_FUNC_GENERATOR = (1 << 0), 684 JS_FUNC_ASYNC = (1 << 1), 685 JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), 686 } JSFunctionKindEnum; 687 688 typedef struct JSFunctionBytecode { 689 JSGCObjectHeader header; /* must come first */ 690 uint8_t js_mode; 691 uint8_t has_prototype : 1; /* true if a prototype field is necessary */ 692 uint8_t has_simple_parameter_list : 1; 693 uint8_t is_derived_class_constructor : 1; 694 /* true if home_object needs to be initialized */ 695 uint8_t need_home_object : 1; 696 uint8_t func_kind : 2; 697 uint8_t new_target_allowed : 1; 698 uint8_t super_call_allowed : 1; 699 uint8_t super_allowed : 1; 700 uint8_t arguments_allowed : 1; 701 uint8_t has_debug : 1; 702 uint8_t read_only_bytecode : 1; 703 uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */ 704 /* XXX: 10 bits available */ 705 uint8_t *byte_code_buf; /* (self pointer) */ 706 int byte_code_len; 707 JSAtom func_name; 708 JSBytecodeVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ 709 JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ 710 uint16_t arg_count; 711 uint16_t var_count; 712 uint16_t defined_arg_count; /* for length function property */ 713 uint16_t stack_size; /* maximum stack size */ 714 uint16_t var_ref_count; /* number of local variable references */ 715 JSContext *realm; /* function realm */ 716 JSValue *cpool; /* constant pool (self pointer) */ 717 int cpool_count; 718 int closure_var_count; 719 void *perf_trampoline; 720 struct { 721 /* debug info, move to separate structure to save memory? */ 722 JSAtom filename; 723 int source_len; 724 int pc2line_len; 725 uint8_t *pc2line_buf; 726 char *source; 727 } debug; 728 } JSFunctionBytecode; 729 730 typedef struct JSBoundFunction { 731 JSValue func_obj; 732 JSValue this_val; 733 int argc; 734 JSValue argv[0]; 735 } JSBoundFunction; 736 737 typedef enum JSIteratorKindEnum { 738 JS_ITERATOR_KIND_KEY, 739 JS_ITERATOR_KIND_VALUE, 740 JS_ITERATOR_KIND_KEY_AND_VALUE, 741 } JSIteratorKindEnum; 742 743 typedef struct JSForInIterator { 744 JSValue obj; 745 uint32_t idx; 746 uint32_t atom_count; 747 uint8_t in_prototype_chain; 748 uint8_t is_array; 749 JSPropertyEnum *tab_atom; /* is_array = FALSE */ 750 } JSForInIterator; 751 752 typedef struct JSRegExp { 753 JSString *pattern; 754 JSString *bytecode; /* also contains the flags */ 755 } JSRegExp; 756 757 typedef struct JSProxyData { 758 JSValue target; 759 JSValue handler; 760 uint8_t is_func; 761 uint8_t is_revoked; 762 } JSProxyData; 763 764 typedef struct JSArrayBuffer { 765 int byte_length; /* 0 if detached */ 766 int max_byte_length; /* -1 if not resizable; >= byte_length otherwise */ 767 uint8_t detached; 768 uint8_t shared; /* if shared, the array buffer cannot be detached */ 769 uint8_t *data; /* NULL if detached */ 770 struct list_head array_list; 771 void *opaque; 772 JSFreeArrayBufferDataFunc *free_func; 773 } JSArrayBuffer; 774 775 typedef struct JSTypedArray { 776 struct list_head link; /* link to arraybuffer */ 777 JSObject *obj; /* back pointer to the TypedArray/DataView object */ 778 JSObject *buffer; /* based array buffer */ 779 uint32_t offset; /* byte offset in the array buffer */ 780 uint32_t length; /* byte length in the array buffer */ 781 BOOL track_rab; /* auto-track length of backing array buffer */ 782 } JSTypedArray; 783 784 typedef struct JSGlobalObject { 785 JSValue uninitialized_vars; /* hidden object containing the list of uninitialized variables */ 786 } JSGlobalObject; 787 788 typedef struct JSAsyncFunctionState { 789 JSGCObjectHeader header; 790 JSValue this_val; /* 'this' argument */ 791 int argc; /* number of function arguments */ 792 BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ 793 BOOL is_completed; /* TRUE if the function has returned. The stack 794 frame is no longer valid */ 795 JSValue resolving_funcs[2]; /* only used in JS async functions */ 796 JSStackFrame frame; 797 /* arg_buf, var_buf, stack_buf and var_refs follow */ 798 } JSAsyncFunctionState; 799 800 typedef enum { 801 /* binary operators */ 802 JS_OVOP_ADD, 803 JS_OVOP_SUB, 804 JS_OVOP_MUL, 805 JS_OVOP_DIV, 806 JS_OVOP_MOD, 807 JS_OVOP_POW, 808 JS_OVOP_OR, 809 JS_OVOP_AND, 810 JS_OVOP_XOR, 811 JS_OVOP_SHL, 812 JS_OVOP_SAR, 813 JS_OVOP_SHR, 814 JS_OVOP_EQ, 815 JS_OVOP_LESS, 816 817 JS_OVOP_BINARY_COUNT, 818 /* unary operators */ 819 JS_OVOP_POS = JS_OVOP_BINARY_COUNT, 820 JS_OVOP_NEG, 821 JS_OVOP_INC, 822 JS_OVOP_DEC, 823 JS_OVOP_NOT, 824 825 JS_OVOP_COUNT, 826 } JSOverloadableOperatorEnum; 827 828 typedef struct { 829 uint32_t operator_index; 830 JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ 831 } JSBinaryOperatorDefEntry; 832 833 typedef struct { 834 int count; 835 JSBinaryOperatorDefEntry *tab; 836 } JSBinaryOperatorDef; 837 838 typedef struct { 839 uint32_t operator_counter; 840 BOOL is_primitive; /* OperatorSet for a primitive type */ 841 /* NULL if no operator is defined */ 842 JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ 843 JSBinaryOperatorDef left; 844 JSBinaryOperatorDef right; 845 } JSOperatorSetData; 846 847 typedef struct JSReqModuleEntry { 848 JSAtom module_name; 849 JSModuleDef *module; /* used using resolution */ 850 JSValue attributes; /* JS_UNDEFINED or an object contains the attributes as key/value */ 851 } JSReqModuleEntry; 852 853 typedef enum JSExportTypeEnum { 854 JS_EXPORT_TYPE_LOCAL, 855 JS_EXPORT_TYPE_INDIRECT, 856 } JSExportTypeEnum; 857 858 typedef struct JSExportEntry { 859 union { 860 struct { 861 int var_idx; /* closure variable index */ 862 JSVarRef *var_ref; /* if != NULL, reference to the variable */ 863 } local; /* for local export */ 864 int req_module_idx; /* module for indirect export */ 865 } u; 866 JSExportTypeEnum export_type; 867 JSAtom local_name; /* '*' if export ns from. not used for local 868 export after compilation */ 869 JSAtom export_name; /* exported variable name */ 870 } JSExportEntry; 871 872 typedef struct JSStarExportEntry { 873 int req_module_idx; /* in req_module_entries */ 874 } JSStarExportEntry; 875 876 typedef struct JSImportEntry { 877 int var_idx; /* closure variable index */ 878 BOOL is_star; /* import_name = '*' is a valid import name, so need a flag */ 879 JSAtom import_name; 880 int req_module_idx; /* in req_module_entries */ 881 } JSImportEntry; 882 883 typedef enum { 884 JS_MODULE_STATUS_UNLINKED, 885 JS_MODULE_STATUS_LINKING, 886 JS_MODULE_STATUS_LINKED, 887 JS_MODULE_STATUS_EVALUATING, 888 JS_MODULE_STATUS_EVALUATING_ASYNC, 889 JS_MODULE_STATUS_EVALUATED, 890 } JSModuleStatus; 891 892 struct JSModuleDef { 893 JSGCObjectHeader header; /* must come first */ 894 JSAtom module_name; 895 struct list_head link; 896 897 JSReqModuleEntry *req_module_entries; 898 int req_module_entries_count; 899 int req_module_entries_size; 900 901 JSExportEntry *export_entries; 902 int export_entries_count; 903 int export_entries_size; 904 905 JSStarExportEntry *star_export_entries; 906 int star_export_entries_count; 907 int star_export_entries_size; 908 909 JSImportEntry *import_entries; 910 int import_entries_count; 911 int import_entries_size; 912 913 JSValue module_ns; 914 JSValue func_obj; /* only used for JS modules */ 915 JSModuleInitFunc *init_func; /* only used for C modules */ 916 BOOL has_tla : 8; /* true if func_obj contains await */ 917 BOOL resolved : 8; 918 BOOL func_created : 8; 919 JSModuleStatus status : 8; 920 /* temp use during js_module_link() & js_module_evaluate() */ 921 int dfs_index, dfs_ancestor_index; 922 JSModuleDef *stack_prev; 923 /* temp use during js_module_evaluate() */ 924 JSModuleDef **async_parent_modules; 925 int async_parent_modules_count; 926 int async_parent_modules_size; 927 int pending_async_dependencies; 928 BOOL async_evaluation; /* true: async_evaluation_timestamp corresponds to [[AsyncEvaluationOrder]] 929 false: [[AsyncEvaluationOrder]] is UNSET or DONE */ 930 int64_t async_evaluation_timestamp; 931 JSModuleDef *cycle_root; 932 JSValue promise; /* corresponds to spec field: capability */ 933 JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ 934 935 /* true if evaluation yielded an exception. It is saved in 936 eval_exception */ 937 BOOL eval_has_exception : 8; 938 JSValue eval_exception; 939 JSValue meta_obj; /* for import.meta */ 940 JSValue private_value; /* private value for C modules */ 941 }; 942 943 typedef struct JSJobEntry { 944 struct list_head link; 945 JSContext *realm; 946 JSJobFunc *job_func; 947 int argc; 948 JSValue argv[0]; 949 } JSJobEntry; 950 951 typedef struct JSProperty { 952 union { 953 JSValue value; /* JS_PROP_NORMAL */ 954 struct { /* JS_PROP_GETSET */ 955 JSObject *getter; /* NULL if undefined */ 956 JSObject *setter; /* NULL if undefined */ 957 } getset; 958 JSVarRef *var_ref; /* JS_PROP_VARREF */ 959 struct { /* JS_PROP_AUTOINIT */ 960 /* in order to use only 2 pointers, we compress the realm 961 and the init function pointer */ 962 uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) 963 in the 2 low bits */ 964 void *opaque; 965 } init; 966 } u; 967 } JSProperty; 968 969 #define JS_PROP_INITIAL_SIZE 2 970 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ 971 972 typedef struct JSShapeProperty { 973 uint32_t hash_next : 26; /* 0 if last in list */ 974 uint32_t flags : 6; /* JS_PROP_XXX */ 975 JSAtom atom; /* JS_ATOM_NULL = free property entry */ 976 } JSShapeProperty; 977 978 struct JSShape { 979 JSGCObjectHeader header; 980 /* true if the shape is inserted in the shape hash table. If not, 981 JSShape.hash is not valid */ 982 uint8_t is_hashed; 983 uint32_t hash; /* current hash value */ 984 uint32_t prop_hash_mask; /* >= 2 */ 985 int prop_size; /* allocated properties */ 986 int prop_count; /* include deleted properties */ 987 int deleted_prop_count; 988 JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ 989 JSObject *proto; 990 uint32_t hash_table[]; /* prop_hash_mask + 1 elements */ 991 /* followed by JSShapeProperty prop[prop_size]; */ 992 }; 993 994 struct JSObject { 995 JSGCObjectHeader header; 996 /* TRUE if the array prototype is "normal": 997 - no small index properties which are get/set or non writable 998 - its prototype is Object.prototype 999 - Object.prototype has no small index properties which are get/set or non writable 1000 - the prototype of Object.prototype is null (always true as it is immutable) 1001 */ 1002 uint8_t is_std_array_prototype : 1; 1003 1004 uint8_t extensible : 1; 1005 uint8_t free_mark : 1; /* only used when freeing objects with cycles */ 1006 uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ 1007 uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS and typed arrays) */ 1008 uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ 1009 uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */ 1010 uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ 1011 uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ 1012 uint16_t class_id; /* see JS_CLASS_x */ 1013 /* count the number of weak references to this object. The object 1014 structure is freed only if header.ref_count = 0 and 1015 weakref_count = 0 */ 1016 uint32_t weakref_count; 1017 JSShape *shape; /* prototype and property names + flag */ 1018 JSProperty *prop; /* array of properties */ 1019 union { 1020 void *opaque; 1021 struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ 1022 struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ 1023 struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ 1024 struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ 1025 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ 1026 struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ 1027 struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ 1028 struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ 1029 struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ 1030 struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ 1031 struct JSIteratorConcatData *iterator_concat_data; /* JS_CLASS_ITERATOR_CONCAT */ 1032 struct JSIteratorHelperData *iterator_helper_data; /* JS_CLASS_ITERATOR_HELPER */ 1033 struct JSIteratorWrapData *iterator_wrap_data; /* JS_CLASS_ITERATOR_WRAP */ 1034 struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ 1035 struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ 1036 struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ 1037 struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ 1038 struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ 1039 struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ 1040 struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ 1041 /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ 1042 struct JSFunctionBytecode *function_bytecode; 1043 JSVarRef **var_refs; 1044 JSObject *home_object; /* for 'super' access */ 1045 } func; 1046 struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ 1047 JSContext *realm; 1048 JSCFunctionType c_function; 1049 uint8_t length; 1050 uint8_t cproto; 1051 int16_t magic; 1052 } cfunc; 1053 /* array part for fast arrays and typed arrays */ 1054 struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_MAPPED_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 1055 union { 1056 uint32_t size; /* JS_CLASS_ARRAY */ 1057 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 1058 } u1; 1059 union { 1060 JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ 1061 JSVarRef **var_refs; /* JS_CLASS_MAPPED_ARGUMENTS */ 1062 void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 1063 int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ 1064 uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ 1065 int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ 1066 uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ 1067 int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ 1068 uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ 1069 int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ 1070 uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ 1071 uint16_t *fp16_ptr; /* JS_CLASS_FLOAT16_ARRAY */ 1072 float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ 1073 double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ 1074 } u; 1075 uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ 1076 } array; /* 12/20 bytes */ 1077 JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ 1078 JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ 1079 JSGlobalObject global_object; 1080 } u; 1081 }; 1082 1083 typedef struct JSMapRecord { 1084 int ref_count; /* used during enumeration to avoid freeing the record */ 1085 BOOL empty : 8; /* TRUE if the record is deleted */ 1086 struct list_head link; 1087 struct JSMapRecord *hash_next; 1088 JSValue key; 1089 JSValue value; 1090 } JSMapRecord; 1091 1092 typedef struct JSMapState { 1093 BOOL is_weak; /* TRUE if WeakSet/WeakMap */ 1094 struct list_head records; /* list of JSMapRecord.link */ 1095 uint32_t record_count; 1096 JSMapRecord **hash_table; 1097 int hash_bits; 1098 uint32_t hash_size; /* = 2 ^ hash_bits */ 1099 uint32_t record_count_threshold; /* count at which a hash table 1100 resize is needed */ 1101 JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */ 1102 } JSMapState; 1103 1104 enum { 1105 __JS_ATOM_NULL = JS_ATOM_NULL, 1106 #define DEF(name, str) JS_ATOM_ ## name, 1107 #include "quickjs-atom.h" 1108 #undef DEF 1109 JS_ATOM_END, 1110 }; 1111 #define JS_ATOM_LAST_KEYWORD JS_ATOM_super 1112 #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield 1113 1114 static const char js_atom_init[] = 1115 #define DEF(name, str) str "\0" 1116 #include "quickjs-atom.h" 1117 #undef DEF 1118 ; 1119 1120 typedef enum OPCodeFormat { 1121 #define FMT(f) OP_FMT_ ## f, 1122 #define DEF(id, size, n_pop, n_push, f) 1123 #include "quickjs-opcode.h" 1124 #undef DEF 1125 #undef FMT 1126 } OPCodeFormat; 1127 1128 enum OPCodeEnum { 1129 #define FMT(f) 1130 #define DEF(id, size, n_pop, n_push, f) OP_ ## id, 1131 #define def(id, size, n_pop, n_push, f) 1132 #include "quickjs-opcode.h" 1133 #undef def 1134 #undef DEF 1135 #undef FMT 1136 OP_COUNT, /* excluding temporary opcodes */ 1137 /* temporary opcodes : overlap with the short opcodes */ 1138 OP_TEMP_START = OP_nop + 1, 1139 OP___dummy = OP_TEMP_START - 1, 1140 #define FMT(f) 1141 #define DEF(id, size, n_pop, n_push, f) 1142 #define def(id, size, n_pop, n_push, f) OP_ ## id, 1143 #include "quickjs-opcode.h" 1144 #undef def 1145 #undef DEF 1146 #undef FMT 1147 OP_TEMP_END, 1148 }; 1149 1150 static int JS_InitAtoms(JSRuntime *rt); 1151 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, 1152 int atom_type); 1153 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); 1154 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); 1155 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, 1156 JSValueConst this_obj, 1157 int argc, JSValueConst *argv, int flags); 1158 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, 1159 JSValueConst this_obj, 1160 int argc, JSValueConst *argv, int flags); 1161 static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 1162 JSValueConst this_obj, JSValueConst new_target, 1163 int argc, JSValue *argv, int flags); 1164 static JSValue JS_CallConstructorInternal(JSContext *ctx, 1165 JSValueConst func_obj, 1166 JSValueConst new_target, 1167 int argc, JSValue *argv, int flags); 1168 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, 1169 int argc, JSValueConst *argv); 1170 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, 1171 int argc, JSValueConst *argv); 1172 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, 1173 JSValue val, BOOL is_array_ctor); 1174 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, 1175 JSValueConst val, int flags, int scope_idx); 1176 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); 1177 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); 1178 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); 1179 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); 1180 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); 1181 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); 1182 static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom atom); 1183 static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val); 1184 static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val); 1185 static __maybe_unused void JS_DumpShapes(JSRuntime *rt); 1186 static void js_dump_value_write(void *opaque, const char *buf, size_t len); 1187 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, 1188 int argc, JSValueConst *argv, int magic); 1189 static void js_array_finalizer(JSRuntime *rt, JSValue val); 1190 static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1191 static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValue val); 1192 static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1193 static void js_object_data_finalizer(JSRuntime *rt, JSValue val); 1194 static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1195 static void js_c_function_finalizer(JSRuntime *rt, JSValue val); 1196 static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1197 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); 1198 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, 1199 JS_MarkFunc *mark_func); 1200 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); 1201 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, 1202 JS_MarkFunc *mark_func); 1203 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); 1204 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, 1205 JS_MarkFunc *mark_func); 1206 static void js_regexp_finalizer(JSRuntime *rt, JSValue val); 1207 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); 1208 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); 1209 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, 1210 JS_MarkFunc *mark_func); 1211 static void js_proxy_finalizer(JSRuntime *rt, JSValue val); 1212 static void js_proxy_mark(JSRuntime *rt, JSValueConst val, 1213 JS_MarkFunc *mark_func); 1214 static void js_map_finalizer(JSRuntime *rt, JSValue val); 1215 static void js_map_mark(JSRuntime *rt, JSValueConst val, 1216 JS_MarkFunc *mark_func); 1217 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); 1218 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, 1219 JS_MarkFunc *mark_func); 1220 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); 1221 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, 1222 JS_MarkFunc *mark_func); 1223 static void js_iterator_concat_finalizer(JSRuntime *rt, JSValue val); 1224 static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, 1225 JS_MarkFunc *mark_func); 1226 static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val); 1227 static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, 1228 JS_MarkFunc *mark_func); 1229 static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val); 1230 static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, 1231 JS_MarkFunc *mark_func); 1232 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); 1233 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, 1234 JS_MarkFunc *mark_func); 1235 static void js_generator_finalizer(JSRuntime *rt, JSValue obj); 1236 static void js_generator_mark(JSRuntime *rt, JSValueConst val, 1237 JS_MarkFunc *mark_func); 1238 static void js_global_object_finalizer(JSRuntime *rt, JSValue obj); 1239 static void js_global_object_mark(JSRuntime *rt, JSValueConst val, 1240 JS_MarkFunc *mark_func); 1241 static void js_promise_finalizer(JSRuntime *rt, JSValue val); 1242 static void js_promise_mark(JSRuntime *rt, JSValueConst val, 1243 JS_MarkFunc *mark_func); 1244 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); 1245 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, 1246 JS_MarkFunc *mark_func); 1247 1248 #define HINT_STRING 0 1249 #define HINT_NUMBER 1 1250 #define HINT_NONE 2 1251 #define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive 1252 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); 1253 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); 1254 static int JS_ToBoolFree(JSContext *ctx, JSValue val); 1255 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); 1256 static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); 1257 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); 1258 static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); 1259 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, 1260 JSValueConst flags); 1261 static JSValue JS_NewRegexp(JSContext *ctx, JSValue pattern, JSValue bc); 1262 static void gc_decref(JSRuntime *rt); 1263 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, 1264 const JSClassDef *class_def, JSAtom name); 1265 1266 typedef enum JSStrictEqModeEnum { 1267 JS_EQ_STRICT, 1268 JS_EQ_SAME_VALUE, 1269 JS_EQ_SAME_VALUE_ZERO, 1270 } JSStrictEqModeEnum; 1271 1272 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 1273 JSStrictEqModeEnum eq_mode); 1274 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1275 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1276 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1277 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); 1278 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); 1279 static JSProperty *add_property(JSContext *ctx, 1280 JSObject *p, JSAtom prop, int prop_flags); 1281 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags); 1282 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); 1283 JSValue JS_ThrowOutOfMemory(JSContext *ctx); 1284 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); 1285 1286 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); 1287 static int JS_CreateProperty(JSContext *ctx, JSObject *p, 1288 JSAtom prop, JSValueConst val, 1289 JSValueConst getter, JSValueConst setter, 1290 int flags); 1291 static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, 1292 int pos2, int len); 1293 static JSValue js_array_buffer_constructor3(JSContext *ctx, 1294 JSValueConst new_target, 1295 uint64_t len, uint64_t *max_len, 1296 JSClassID class_id, 1297 uint8_t *buf, 1298 JSFreeArrayBufferDataFunc *free_func, 1299 void *opaque, BOOL alloc_flag); 1300 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr); 1301 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); 1302 static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf); 1303 static JSValue js_typed_array_constructor(JSContext *ctx, 1304 JSValueConst this_val, 1305 int argc, JSValueConst *argv, 1306 int classid); 1307 static JSValue js_typed_array_constructor_ta(JSContext *ctx, 1308 JSValueConst new_target, 1309 JSValueConst src_obj, 1310 int classid, uint32_t len); 1311 static BOOL typed_array_is_oob(JSObject *p); 1312 static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj); 1313 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); 1314 static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx); 1315 static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical); 1316 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, 1317 BOOL is_arg); 1318 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); 1319 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); 1320 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, 1321 JSValueConst this_obj, 1322 int argc, JSValueConst *argv, 1323 int flags); 1324 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); 1325 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, 1326 JS_MarkFunc *mark_func); 1327 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 1328 const char *input, size_t input_len, 1329 const char *filename, int flags, int scope_idx); 1330 static void js_free_module_def(JSRuntime *rt, JSModuleDef *m); 1331 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, 1332 JS_MarkFunc *mark_func); 1333 static JSValue js_import_meta(JSContext *ctx); 1334 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options); 1335 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); 1336 static JSValue js_new_promise_capability(JSContext *ctx, 1337 JSValue *resolving_funcs, 1338 JSValueConst ctor); 1339 static __exception int perform_promise_then(JSContext *ctx, 1340 JSValueConst promise, 1341 JSValueConst *resolve_reject, 1342 JSValueConst *cap_resolving_funcs); 1343 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, 1344 int argc, JSValueConst *argv, int magic); 1345 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, 1346 int argc, JSValueConst *argv); 1347 static BOOL js_string_eq(JSContext *ctx, 1348 const JSString *p1, const JSString *p2); 1349 static int js_string_compare(JSContext *ctx, 1350 const JSString *p1, const JSString *p2); 1351 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); 1352 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 1353 JSValue prop, JSValue val, int flags); 1354 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); 1355 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); 1356 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); 1357 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, 1358 JSObject *p, JSAtom prop); 1359 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); 1360 static int JS_AddIntrinsicBasicObjects(JSContext *ctx); 1361 static void js_free_shape(JSRuntime *rt, JSShape *sh); 1362 static void js_free_shape_null(JSRuntime *rt, JSShape *sh); 1363 static int js_shape_prepare_update(JSContext *ctx, JSObject *p, 1364 JSShapeProperty **pprs); 1365 static int init_shape_hash(JSRuntime *rt); 1366 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, 1367 JSValueConst obj); 1368 static __exception int js_get_length64(JSContext *ctx, int64_t *pres, 1369 JSValueConst obj); 1370 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); 1371 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, 1372 JSValueConst array_arg); 1373 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, 1374 JSValue **arrpp, uint32_t *countp); 1375 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, 1376 JSValueConst sync_iter); 1377 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); 1378 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, 1379 JS_MarkFunc *mark_func); 1380 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, 1381 JSValueConst this_val, 1382 int argc, JSValueConst *argv, int flags); 1383 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); 1384 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, 1385 JSGCObjectTypeEnum type); 1386 static void remove_gc_object(JSGCObjectHeader *h); 1387 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); 1388 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, 1389 void *opaque); 1390 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, 1391 JSAtom atom, void *opaque); 1392 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, 1393 int argc, JSValueConst *argv, int is_map); 1394 static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh); 1395 static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); 1396 static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh); 1397 static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects); 1398 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, 1399 JSValueConst obj, JSValueConst method); 1400 static int js_string_find_invalid_codepoint(JSString *p); 1401 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, 1402 int argc, JSValueConst *argv); 1403 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, 1404 int argc, JSValueConst *argv, int magic); 1405 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, 1406 int argc, JSValueConst *argv); 1407 static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p, 1408 JSAtom atom, BOOL is_lexical); 1409 static int typed_array_init(JSContext *ctx, JSValueConst obj, 1410 JSValue buffer, uint64_t offset, uint64_t len, 1411 BOOL track_rab); 1412 1413 1414 static const JSClassExoticMethods js_arguments_exotic_methods; 1415 static const JSClassExoticMethods js_string_exotic_methods; 1416 static const JSClassExoticMethods js_proxy_exotic_methods; 1417 static const JSClassExoticMethods js_module_ns_exotic_methods; 1418 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; 1419 1420 /* JS malloc */ 1421 1422 /* max overhead for size >= 64: 12.5% */ 1423 static const uint16_t js_malloc_block_sizes[JS_MALLOC_BLOCK_SIZE_COUNT] = { 1424 16, 1425 24, 1426 32, 1427 40, 1428 48, 1429 56, 1430 64, 1431 72, 1432 80, 1433 88, 1434 96, 1435 104, 1436 112, 1437 120, 1438 128, 1439 144, 1440 160, 1441 176, 1442 192, 1443 208, 1444 224, 1445 240, 1446 256, 1447 288, 1448 320, 1449 352, 1450 384, 1451 416, 1452 448, 1453 480, 1454 512, 1455 }; 1456 1457 static int get_block_size_index(size_t size) 1458 { 1459 if (size <= 16) { 1460 return 0; 1461 } else if (size <= 128) { 1462 return (size + 7) / 8 - 2; 1463 } else if (size <= 256) { 1464 return (size + 15) / 16 + 6; 1465 } else if (size <= 512) { 1466 return (size + 31) / 32 + 14; 1467 } else { 1468 return JS_MALLOC_BLOCK_SIZE_COUNT; 1469 } 1470 } 1471 1472 static JSMallocBlockHeader *get_zero_size_block(JSMallocContext *s) 1473 { 1474 return (JSMallocBlockHeader *)s->zero_size_block; 1475 } 1476 1477 static void js_malloc_init(JSMallocContext *s) 1478 { 1479 int i; 1480 memset(s, 0, sizeof(*s)); 1481 get_zero_size_block(s)->u.block_idx = FREE_NIL; 1482 for(i = 0; i < JS_MALLOC_BLOCK_SIZE_COUNT; i++) { 1483 init_list_head(&s->arena_list[i]); 1484 init_list_head(&s->free_arena_list[i]); 1485 } 1486 #ifdef JS_MALLOC_USE_ITER 1487 init_list_head(&s->large_block_list); 1488 #endif 1489 } 1490 1491 static void *get_arena_block(JSMallocArena *ar, unsigned int idx, unsigned int block_size) 1492 { 1493 return ar->blocks + idx * block_size; 1494 } 1495 1496 static inline JSMallocBlockHeader *js_rc(void *ptr) 1497 { 1498 return container_of(ptr, JSMallocBlockHeader, user_data); 1499 } 1500 1501 static no_inline JSMallocArena *js_malloc_new_arena(JSMallocContext *s, int block_size_idx) 1502 { 1503 JSMallocBlockHeader *b; 1504 JSMallocArena *ar; 1505 int n_blocks, block_size, i; 1506 1507 block_size = js_malloc_block_sizes[block_size_idx]; 1508 n_blocks = (JS_MALLOC_ARENA_SIZE - sizeof(JSMallocArena)) / block_size; 1509 ar = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocArena) + n_blocks * block_size); 1510 if (!ar) 1511 return NULL; 1512 1513 ar->block_size_idx = block_size_idx; 1514 ar->n_blocks = n_blocks; 1515 ar->n_used_blocks = 0; 1516 ar->first_free_block = 0; 1517 #ifdef JS_MALLOC_USE_ITER 1518 { 1519 int n_bitmap_words = (n_blocks + 31) / 32; 1520 for(i = 0; i < n_bitmap_words; i++) 1521 ar->bitmap[i] = 0; 1522 } 1523 #endif 1524 for(i = 0; i < n_blocks - 1; i++) { 1525 b = get_arena_block(ar, i, block_size); 1526 b->u.free_next = i + 1; 1527 b->block_size_idx = block_size_idx; 1528 } 1529 b = get_arena_block(ar, n_blocks - 1, block_size); 1530 b->u.free_next = FREE_NIL; 1531 b->block_size_idx = block_size_idx; 1532 1533 /* add to the head */ 1534 list_add(&ar->link, &s->arena_list[block_size_idx]); 1535 list_add(&ar->free_link, &s->free_arena_list[block_size_idx]); 1536 return ar; 1537 } 1538 1539 static no_inline void *js_malloc_large(JSMallocContext *s, size_t size) 1540 { 1541 JSMallocLargeBlockHeader *b; 1542 b = s->mf.js_malloc(&s->malloc_state, sizeof(JSMallocLargeBlockHeader) + size); 1543 if (!b) 1544 return NULL; 1545 b->header.u.block_idx = FREE_NIL; 1546 b->header.block_size_idx = 0xff; /* fail safe */ 1547 #ifdef JS_MALLOC_USE_ITER 1548 list_add_tail(&b->link, &s->large_block_list); 1549 #endif 1550 return b->header.user_data; 1551 } 1552 1553 static void *__js_malloc(JSMallocContext *s, size_t size) 1554 { 1555 size_t total_size; 1556 if (unlikely(size == 0)) { 1557 JSMallocBlockHeader *b = get_zero_size_block(s); 1558 return b->user_data; 1559 } else { 1560 total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) + 1561 sizeof(JSMallocBlockHeader); 1562 if (!JS_MALLOC_LARGE_BLOCKS_ONLY && 1563 total_size <= JS_MALLOC_MAX_SMALL_SIZE) { 1564 int block_size_idx; 1565 unsigned int block_idx, block_size; 1566 JSMallocBlockHeader *b; 1567 JSMallocArena *ar; 1568 struct list_head *el, *head; 1569 1570 block_size_idx = get_block_size_index(total_size); 1571 block_size = js_malloc_block_sizes[block_size_idx]; 1572 head = &s->free_arena_list[block_size_idx]; 1573 el = head->next; 1574 if (unlikely(el == head)) { 1575 ar = js_malloc_new_arena(s, block_size_idx); 1576 if (!ar) 1577 return NULL; 1578 } else { 1579 ar = list_entry(el, JSMallocArena, free_link); 1580 } 1581 block_idx = ar->first_free_block; 1582 b = get_arena_block(ar, ar->first_free_block, block_size); 1583 ar->first_free_block = b->u.free_next; 1584 b->u.block_idx = block_idx; 1585 ar->n_used_blocks++; 1586 if (unlikely(ar->n_used_blocks == ar->n_blocks)) { 1587 list_del(&ar->free_link); 1588 } 1589 #ifdef JS_MALLOC_USE_ITER 1590 ar->bitmap[block_idx / 32] |= 1 << (block_idx % 32); 1591 #endif 1592 return b->user_data; 1593 } else { 1594 return js_malloc_large(s, size); 1595 } 1596 } 1597 } 1598 1599 static void __js_free(JSMallocContext *s, void *ptr) 1600 { 1601 JSMallocBlockHeader *b; 1602 1603 if (!ptr) 1604 return; 1605 b = container_of(ptr, JSMallocBlockHeader, user_data); 1606 if (unlikely(b->u.block_idx == FREE_NIL)) { 1607 /* large or zero size block */ 1608 if (b == get_zero_size_block(s)) { 1609 /* nothing to do */ 1610 } else { 1611 JSMallocLargeBlockHeader *lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data); 1612 #ifdef JS_MALLOC_USE_ITER 1613 list_del(&lb->link); 1614 #endif 1615 s->mf.js_free(&s->malloc_state, lb); 1616 } 1617 } else { 1618 unsigned int block_idx = b->u.block_idx; 1619 unsigned int block_size_idx = b->block_size_idx; 1620 unsigned int block_size = js_malloc_block_sizes[block_size_idx]; 1621 JSMallocArena *ar = (JSMallocArena *)((uint8_t *)b - block_size * block_idx - sizeof(JSMallocArena)); 1622 b->u.free_next = ar->first_free_block; 1623 ar->first_free_block = block_idx; 1624 #ifdef JS_MALLOC_USE_ITER 1625 ar->bitmap[block_idx / 32] &= ~(1 << (block_idx % 32)); 1626 #endif 1627 /* add back to the free list if needed */ 1628 if (unlikely(ar->n_used_blocks == ar->n_blocks)) { 1629 list_add(&ar->free_link, &s->free_arena_list[block_size_idx]); 1630 } 1631 ar->n_used_blocks--; 1632 if (unlikely(ar->n_used_blocks == 0)) { 1633 list_del(&ar->link); 1634 list_del(&ar->free_link); 1635 s->mf.js_free(&s->malloc_state, ar); 1636 } 1637 } 1638 } 1639 1640 static void *__js_realloc(JSMallocContext *s, void *ptr, size_t size) 1641 { 1642 JSMallocBlockHeader *b; 1643 if (ptr == NULL) { 1644 return __js_malloc(s, size); 1645 } else if (size == 0) { 1646 __js_free(s, ptr); 1647 return NULL; 1648 } 1649 b = container_of(ptr, JSMallocBlockHeader, user_data); 1650 if (b->u.block_idx == FREE_NIL) { 1651 if (b == get_zero_size_block(s)) { 1652 return __js_malloc(s, size); 1653 } else { 1654 JSMallocLargeBlockHeader *lb, *new_lb; 1655 lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data); 1656 #ifdef JS_MALLOC_USE_ITER 1657 list_del(&lb->link); 1658 #endif 1659 new_lb = s->mf.js_realloc(&s->malloc_state, lb, sizeof(JSMallocLargeBlockHeader) + size); 1660 if (!new_lb) { 1661 #ifdef JS_MALLOC_USE_ITER 1662 /* add again in the list */ 1663 list_add_tail(&lb->link, &s->large_block_list); 1664 #endif 1665 return NULL; 1666 } 1667 new_lb->header.u.block_idx = FREE_NIL; 1668 new_lb->header.block_size_idx = 0xff; /* fail safe */ 1669 #ifdef JS_MALLOC_USE_ITER 1670 list_add_tail(&new_lb->link, &s->large_block_list); 1671 #endif 1672 return new_lb->header.user_data; 1673 } 1674 } else { 1675 unsigned int block_size_idx = b->block_size_idx; 1676 size_t block_size = js_malloc_block_sizes[block_size_idx]; 1677 size_t total_size, old_size; 1678 void *new_ptr; 1679 JSMallocBlockHeader *new_b; 1680 1681 total_size = ((size + JS_MALLOC_ALIGN - 1) & ~(JS_MALLOC_ALIGN - 1)) + 1682 sizeof(JSMallocBlockHeader); 1683 if (total_size <= block_size) 1684 return ptr; 1685 new_ptr = __js_malloc(s, size); 1686 if (!new_ptr) 1687 return NULL; 1688 new_b = container_of(new_ptr, JSMallocBlockHeader, user_data); 1689 /* copy the GC data */ 1690 new_b->gc_obj_type = b->gc_obj_type; 1691 new_b->mark = b->mark; 1692 new_b->ref_count = b->ref_count; 1693 /* copy the data */ 1694 old_size = block_size - sizeof(JSMallocBlockHeader); 1695 if (size > old_size) 1696 size = old_size; 1697 memcpy(new_ptr, ptr, size); 1698 __js_free(s, ptr); 1699 return new_ptr; 1700 } 1701 } 1702 1703 static size_t __js_malloc_usable_size(JSMallocContext *s, const char *ptr) 1704 { 1705 JSMallocBlockHeader *b; 1706 if (!ptr) 1707 return 0; 1708 b = container_of(ptr, JSMallocBlockHeader, user_data); 1709 if (b->u.block_idx == FREE_NIL) { 1710 if (b == get_zero_size_block(s)) { 1711 return 0; 1712 } else { 1713 JSMallocLargeBlockHeader *lb; 1714 size_t size; 1715 lb = container_of(ptr, JSMallocLargeBlockHeader, header.user_data); 1716 if (s->mf.js_malloc_usable_size) { 1717 size = s->mf.js_malloc_usable_size(lb); 1718 if (size != 0) 1719 size -= sizeof(JSMallocLargeBlockHeader); 1720 return size; 1721 } else { 1722 return 0; 1723 } 1724 } 1725 } else { 1726 size_t block_size = js_malloc_block_sizes[b->block_size_idx]; 1727 return block_size - sizeof(*b); 1728 } 1729 } 1730 1731 static __maybe_unused void js_malloc_dump_arenas(JSMallocContext *s) 1732 { 1733 struct list_head *el; 1734 int block_size_idx; 1735 1736 printf("%20s %10s %10s\n", "PTR", "BLK_SIZE", "ALLOC"); 1737 for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) { 1738 int block_size = js_malloc_block_sizes[block_size_idx]; 1739 list_for_each(el, &s->arena_list[block_size_idx]) { 1740 JSMallocArena *ar = list_entry(el, JSMallocArena, link); 1741 printf("%20p %10u %9.1f%%\n", 1742 ar, block_size, 1743 (double)ar->n_used_blocks / ar->n_blocks * 100); 1744 } 1745 } 1746 } 1747 1748 #ifdef JS_MALLOC_USE_ITER 1749 typedef void JSMallocIterFunc(void *opaque, void *ptr); 1750 1751 /* iterate thru allocated blocks. The allocated block list should not 1752 be modified while iterating. */ 1753 static __maybe_unused void js_malloc_iter(JSMallocContext *s, JSMallocIterFunc *iter_func, void *iter_opaque) 1754 { 1755 struct list_head *el; 1756 int block_size_idx; 1757 int i, j, n_words; 1758 uint32_t bmp; 1759 1760 for(block_size_idx = 0; block_size_idx < JS_MALLOC_BLOCK_SIZE_COUNT; block_size_idx++) { 1761 unsigned int block_size = js_malloc_block_sizes[block_size_idx]; 1762 list_for_each(el, &s->arena_list[block_size_idx]) { 1763 JSMallocArena *ar = list_entry(el, JSMallocArena, link); 1764 n_words = (ar->n_blocks + 31) / 32; 1765 for(i = 0; i < n_words; i++) { 1766 bmp = ar->bitmap[i]; 1767 while (bmp != 0) { 1768 j = ctz32(bmp); 1769 bmp &= ~(1 << j); 1770 iter_func(iter_opaque, get_arena_block(ar, i * 32+ j, block_size)); 1771 } 1772 } 1773 } 1774 } 1775 list_for_each(el, &s->large_block_list) { 1776 JSMallocLargeBlockHeader *lb = list_entry(el, JSMallocLargeBlockHeader, link); 1777 iter_func(iter_opaque, lb->header.user_data); 1778 } 1779 } 1780 #endif 1781 1782 /* end JS malloc */ 1783 1784 static void js_trigger_gc(JSRuntime *rt, size_t size) 1785 { 1786 BOOL force_gc; 1787 #ifdef FORCE_GC_AT_MALLOC 1788 force_gc = TRUE; 1789 #else 1790 force_gc = ((rt->malloc_ctx.malloc_state.malloc_size + size) > 1791 rt->malloc_gc_threshold); 1792 #endif 1793 if (force_gc) { 1794 #ifdef DUMP_GC 1795 printf("GC: size=%" PRIu64 "\n", 1796 (uint64_t)rt->malloc_ctx.malloc_state.malloc_size); 1797 #endif 1798 JS_RunGC(rt); 1799 rt->malloc_gc_threshold = rt->malloc_ctx.malloc_state.malloc_size + 1800 (rt->malloc_ctx.malloc_state.malloc_size >> 1); 1801 } 1802 } 1803 1804 void *js_malloc_rt(JSRuntime *rt, size_t size) 1805 { 1806 return __js_malloc(&rt->malloc_ctx, size); 1807 } 1808 1809 void js_free_rt(JSRuntime *rt, void *ptr) 1810 { 1811 __js_free(&rt->malloc_ctx, ptr); 1812 } 1813 1814 void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) 1815 { 1816 return __js_realloc(&rt->malloc_ctx, ptr, size); 1817 } 1818 1819 size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) 1820 { 1821 return __js_malloc_usable_size(&rt->malloc_ctx, ptr); 1822 } 1823 1824 void *js_mallocz_rt(JSRuntime *rt, size_t size) 1825 { 1826 void *ptr; 1827 ptr = js_malloc_rt(rt, size); 1828 if (unlikely(!ptr)) 1829 return NULL; 1830 return memset(ptr, 0, size); 1831 } 1832 1833 /* Throw out of memory in case of error */ 1834 void *js_malloc(JSContext *ctx, size_t size) 1835 { 1836 void *ptr; 1837 ptr = js_malloc_rt(ctx->rt, size); 1838 if (unlikely(!ptr)) { 1839 JS_ThrowOutOfMemory(ctx); 1840 return NULL; 1841 } 1842 return ptr; 1843 } 1844 1845 /* Throw out of memory in case of error */ 1846 void *js_mallocz(JSContext *ctx, size_t size) 1847 { 1848 void *ptr; 1849 ptr = js_mallocz_rt(ctx->rt, size); 1850 if (unlikely(!ptr)) { 1851 JS_ThrowOutOfMemory(ctx); 1852 return NULL; 1853 } 1854 return ptr; 1855 } 1856 1857 void js_free(JSContext *ctx, void *ptr) 1858 { 1859 js_free_rt(ctx->rt, ptr); 1860 } 1861 1862 /* Throw out of memory in case of error */ 1863 void *js_realloc(JSContext *ctx, void *ptr, size_t size) 1864 { 1865 void *ret; 1866 ret = js_realloc_rt(ctx->rt, ptr, size); 1867 if (unlikely(!ret && size != 0)) { 1868 JS_ThrowOutOfMemory(ctx); 1869 return NULL; 1870 } 1871 return ret; 1872 } 1873 1874 /* store extra allocated size in *pslack if successful */ 1875 void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) 1876 { 1877 void *ret; 1878 ret = js_realloc_rt(ctx->rt, ptr, size); 1879 if (unlikely(!ret && size != 0)) { 1880 JS_ThrowOutOfMemory(ctx); 1881 return NULL; 1882 } 1883 if (pslack) { 1884 size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); 1885 *pslack = (new_size > size) ? new_size - size : 0; 1886 } 1887 return ret; 1888 } 1889 1890 size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) 1891 { 1892 return js_malloc_usable_size_rt(ctx->rt, ptr); 1893 } 1894 1895 /* Throw out of memory exception in case of error */ 1896 char *js_strndup(JSContext *ctx, const char *s, size_t n) 1897 { 1898 char *ptr; 1899 ptr = js_malloc(ctx, n + 1); 1900 if (ptr) { 1901 memcpy(ptr, s, n); 1902 ptr[n] = '\0'; 1903 } 1904 return ptr; 1905 } 1906 1907 char *js_strdup(JSContext *ctx, const char *str) 1908 { 1909 return js_strndup(ctx, str, strlen(str)); 1910 } 1911 1912 static no_inline int js_realloc_array(JSContext *ctx, void **parray, 1913 int elem_size, int *psize, int req_size) 1914 { 1915 int new_size; 1916 size_t slack; 1917 void *new_array; 1918 /* XXX: potential arithmetic overflow */ 1919 new_size = max_int(req_size, *psize * 3 / 2); 1920 new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); 1921 if (!new_array) 1922 return -1; 1923 new_size += slack / elem_size; 1924 *psize = new_size; 1925 *parray = new_array; 1926 return 0; 1927 } 1928 1929 /* resize the array and update its size if req_size > *psize */ 1930 static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, 1931 int *psize, int req_size) 1932 { 1933 if (unlikely(req_size > *psize)) 1934 return js_realloc_array(ctx, parray, elem_size, psize, req_size); 1935 else 1936 return 0; 1937 } 1938 1939 static void *js_realloc_rt_opaque(void *opaque, void *ptr, size_t size) 1940 { 1941 return js_realloc_rt(opaque, ptr, size); 1942 } 1943 1944 static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) 1945 { 1946 dbuf_init2(s, ctx->rt, js_realloc_rt_opaque); 1947 } 1948 1949 static void *js_realloc_bytecode_rt(void *opaque, void *ptr, size_t size) 1950 { 1951 JSRuntime *rt = opaque; 1952 if (size > (INT32_MAX / 2)) { 1953 /* the bytecode cannot be larger than 2G. Leave some slack to 1954 avoid some overflows. */ 1955 return NULL; 1956 } else { 1957 return js_realloc_rt(rt, ptr, size); 1958 } 1959 } 1960 1961 static inline void js_dbuf_bytecode_init(JSContext *ctx, DynBuf *s) 1962 { 1963 dbuf_init2(s, ctx->rt, js_realloc_bytecode_rt); 1964 } 1965 1966 static inline int is_digit(int c) { 1967 return c >= '0' && c <= '9'; 1968 } 1969 1970 static inline int string_get(const JSString *p, int idx) { 1971 return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; 1972 } 1973 1974 typedef struct JSClassShortDef { 1975 JSAtom class_name; 1976 JSClassFinalizer *finalizer; 1977 JSClassGCMark *gc_mark; 1978 } JSClassShortDef; 1979 1980 static JSClassShortDef const js_std_class_def[] = { 1981 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ 1982 { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ 1983 { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ 1984 { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ 1985 { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ 1986 { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ 1987 { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ 1988 { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ 1989 { JS_ATOM_Arguments, js_mapped_arguments_finalizer, js_mapped_arguments_mark }, /* JS_CLASS_MAPPED_ARGUMENTS */ 1990 { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ 1991 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ 1992 { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ 1993 { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ 1994 { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ 1995 { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ 1996 { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ 1997 { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ 1998 { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ 1999 { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ 2000 { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ 2001 { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ 2002 { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ 2003 { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ 2004 { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ 2005 { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ 2006 { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ 2007 { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ 2008 { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ 2009 { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ 2010 { JS_ATOM_Float16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT16_ARRAY */ 2011 { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ 2012 { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ 2013 { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ 2014 { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ 2015 { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ 2016 { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ 2017 { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ 2018 { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ 2019 { JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */ 2020 { JS_ATOM_IteratorConcat, js_iterator_concat_finalizer, js_iterator_concat_mark }, /* JS_CLASS_ITERATOR_CONCAT */ 2021 { JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */ 2022 { JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */ 2023 { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ 2024 { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ 2025 { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ 2026 { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ 2027 { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ 2028 { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ 2029 { JS_ATOM_Object, js_global_object_finalizer, js_global_object_mark }, /* JS_CLASS_GLOBAL_OBJECT */ 2030 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_RAWJSON */ 2031 }; 2032 2033 static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, 2034 int start, int count) 2035 { 2036 JSClassDef cm_s, *cm = &cm_s; 2037 int i, class_id; 2038 2039 for(i = 0; i < count; i++) { 2040 class_id = i + start; 2041 memset(cm, 0, sizeof(*cm)); 2042 cm->finalizer = tab[i].finalizer; 2043 cm->gc_mark = tab[i].gc_mark; 2044 if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) 2045 return -1; 2046 } 2047 return 0; 2048 } 2049 2050 #if !defined(CONFIG_STACK_CHECK) 2051 /* no stack limitation */ 2052 static inline uintptr_t js_get_stack_pointer(void) 2053 { 2054 return 0; 2055 } 2056 2057 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) 2058 { 2059 return FALSE; 2060 } 2061 #else 2062 /* Note: OS and CPU dependent */ 2063 static inline uintptr_t js_get_stack_pointer(void) 2064 { 2065 return (uintptr_t)__builtin_frame_address(0); 2066 } 2067 2068 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) 2069 { 2070 uintptr_t sp; 2071 sp = js_get_stack_pointer() - alloca_size; 2072 return unlikely(sp < rt->stack_limit); 2073 } 2074 #endif 2075 2076 JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) 2077 { 2078 JSRuntime *rt; 2079 JSMallocState ms; 2080 2081 memset(&ms, 0, sizeof(ms)); 2082 ms.opaque = opaque; 2083 ms.malloc_limit = -1; 2084 2085 rt = mf->js_malloc(&ms, sizeof(JSRuntime)); 2086 if (!rt) 2087 return NULL; 2088 memset(rt, 0, sizeof(*rt)); 2089 js_malloc_init(&rt->malloc_ctx); 2090 rt->malloc_ctx.mf = *mf; 2091 rt->malloc_ctx.malloc_state = ms; 2092 rt->malloc_gc_threshold = 256 * 1024; 2093 2094 init_list_head(&rt->context_list); 2095 init_list_head(&rt->gc_obj_list); 2096 init_list_head(&rt->gc_zero_ref_count_list); 2097 rt->gc_phase = JS_GC_PHASE_NONE; 2098 init_list_head(&rt->weakref_list); 2099 2100 #ifdef DUMP_LEAKS 2101 init_list_head(&rt->string_list); 2102 #endif 2103 init_list_head(&rt->job_list); 2104 2105 if (JS_InitAtoms(rt)) 2106 goto fail; 2107 2108 /* create the object, array and function classes */ 2109 if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, 2110 countof(js_std_class_def)) < 0) 2111 goto fail; 2112 rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; 2113 rt->class_array[JS_CLASS_MAPPED_ARGUMENTS].exotic = &js_arguments_exotic_methods; 2114 rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; 2115 rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; 2116 2117 rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; 2118 rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; 2119 rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; 2120 rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; 2121 if (init_shape_hash(rt)) 2122 goto fail; 2123 2124 rt->stack_size = JS_DEFAULT_STACK_SIZE; 2125 JS_UpdateStackTop(rt); 2126 2127 rt->current_exception = JS_UNINITIALIZED; 2128 2129 return rt; 2130 fail: 2131 JS_FreeRuntime(rt); 2132 return NULL; 2133 } 2134 2135 void *JS_GetRuntimeOpaque(JSRuntime *rt) 2136 { 2137 return rt->user_opaque; 2138 } 2139 2140 void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) 2141 { 2142 rt->user_opaque = opaque; 2143 } 2144 2145 /* default memory allocation functions with memory limitation */ 2146 static size_t js_def_malloc_usable_size(const void *ptr) 2147 { 2148 #if defined(__APPLE__) 2149 return malloc_size(ptr); 2150 #elif defined(_WIN32) 2151 return _msize((void *)ptr); 2152 #elif defined(__EMSCRIPTEN__) 2153 return 0; 2154 #elif defined(__linux__) || defined(__GLIBC__) 2155 return malloc_usable_size((void *)ptr); 2156 #else 2157 /* change this to `return 0;` if compilation fails */ 2158 return malloc_usable_size((void *)ptr); 2159 #endif 2160 } 2161 2162 static void *js_def_malloc(JSMallocState *s, size_t size) 2163 { 2164 void *ptr; 2165 2166 /* Do not allocate zero bytes: behavior is platform dependent */ 2167 assert(size != 0); 2168 2169 if (unlikely(s->malloc_size + size > s->malloc_limit)) 2170 return NULL; 2171 2172 ptr = malloc(size); 2173 if (!ptr) 2174 return NULL; 2175 2176 s->malloc_count++; 2177 s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 2178 return ptr; 2179 } 2180 2181 static void js_def_free(JSMallocState *s, void *ptr) 2182 { 2183 if (!ptr) 2184 return; 2185 2186 s->malloc_count--; 2187 s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 2188 free(ptr); 2189 } 2190 2191 static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) 2192 { 2193 size_t old_size; 2194 2195 if (!ptr) { 2196 if (size == 0) 2197 return NULL; 2198 return js_def_malloc(s, size); 2199 } 2200 old_size = js_def_malloc_usable_size(ptr); 2201 if (size == 0) { 2202 s->malloc_count--; 2203 s->malloc_size -= old_size + MALLOC_OVERHEAD; 2204 free(ptr); 2205 return NULL; 2206 } 2207 if (s->malloc_size + size - old_size > s->malloc_limit) 2208 return NULL; 2209 2210 ptr = realloc(ptr, size); 2211 if (!ptr) 2212 return NULL; 2213 2214 s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; 2215 return ptr; 2216 } 2217 2218 static const JSMallocFunctions def_malloc_funcs = { 2219 js_def_malloc, 2220 js_def_free, 2221 js_def_realloc, 2222 js_def_malloc_usable_size, 2223 }; 2224 2225 JSRuntime *JS_NewRuntime(void) 2226 { 2227 return JS_NewRuntime2(&def_malloc_funcs, NULL); 2228 } 2229 2230 void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) 2231 { 2232 rt->malloc_ctx.malloc_state.malloc_limit = limit; 2233 } 2234 2235 /* use -1 to disable automatic GC */ 2236 void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) 2237 { 2238 rt->malloc_gc_threshold = gc_threshold; 2239 } 2240 2241 #define malloc(s) malloc_is_forbidden(s) 2242 #define free(p) free_is_forbidden(p) 2243 #define realloc(p,s) realloc_is_forbidden(p,s) 2244 2245 void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) 2246 { 2247 rt->interrupt_handler = cb; 2248 rt->interrupt_opaque = opaque; 2249 } 2250 2251 void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) 2252 { 2253 rt->can_block = can_block; 2254 } 2255 2256 void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, 2257 const JSSharedArrayBufferFunctions *sf) 2258 { 2259 rt->sab_funcs = *sf; 2260 } 2261 2262 void JS_SetStripInfo(JSRuntime *rt, int flags) 2263 { 2264 rt->strip_flags = flags; 2265 } 2266 2267 int JS_GetStripInfo(JSRuntime *rt) 2268 { 2269 return rt->strip_flags; 2270 } 2271 2272 static int JS_EnqueueJob2(JSContext *ctx, JSJobFunc *job_func, 2273 int argc, JSValueConst *argv, BOOL no_exception) 2274 { 2275 JSRuntime *rt = ctx->rt; 2276 JSJobEntry *e; 2277 int i; 2278 2279 if (no_exception) 2280 e = js_malloc_rt(ctx->rt, sizeof(*e) + argc * sizeof(JSValue)); 2281 else 2282 e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); 2283 if (!e) 2284 return -1; 2285 e->realm = JS_DupContext(ctx); 2286 e->job_func = job_func; 2287 e->argc = argc; 2288 for(i = 0; i < argc; i++) { 2289 e->argv[i] = JS_DupValue(ctx, argv[i]); 2290 } 2291 list_add_tail(&e->link, &rt->job_list); 2292 return 0; 2293 } 2294 2295 /* return 0 if OK, < 0 if exception */ 2296 int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, 2297 int argc, JSValueConst *argv) 2298 { 2299 return JS_EnqueueJob2(ctx, job_func, argc, argv, FALSE); 2300 } 2301 2302 BOOL JS_IsJobPending(JSRuntime *rt) 2303 { 2304 return !list_empty(&rt->job_list); 2305 } 2306 2307 /* return < 0 if exception, 0 if no job pending, 1 if a job was 2308 executed successfully. The context of the job is stored in '*pctx' 2309 if pctx != NULL. It may be NULL if the context was already 2310 destroyed or if no job was pending. The 'pctx' parameter is now 2311 absolete. */ 2312 int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) 2313 { 2314 JSContext *ctx; 2315 JSJobEntry *e; 2316 JSValue res; 2317 int i, ret; 2318 2319 if (list_empty(&rt->job_list)) { 2320 if (pctx) 2321 *pctx = NULL; 2322 return 0; 2323 } 2324 2325 /* get the first pending job and execute it */ 2326 e = list_entry(rt->job_list.next, JSJobEntry, link); 2327 list_del(&e->link); 2328 ctx = e->realm; 2329 res = e->job_func(ctx, e->argc, (JSValueConst *)e->argv); 2330 for(i = 0; i < e->argc; i++) 2331 JS_FreeValue(ctx, e->argv[i]); 2332 if (JS_IsException(res)) 2333 ret = -1; 2334 else 2335 ret = 1; 2336 JS_FreeValue(ctx, res); 2337 js_free(ctx, e); 2338 if (pctx) { 2339 if (js_rc(ctx)->ref_count > 1) 2340 *pctx = ctx; 2341 else 2342 *pctx = NULL; 2343 } 2344 JS_FreeContext(ctx); 2345 return ret; 2346 } 2347 2348 static inline uint32_t atom_get_free(const JSAtomStruct *p) 2349 { 2350 return (uintptr_t)p >> 1; 2351 } 2352 2353 static inline BOOL atom_is_free(const JSAtomStruct *p) 2354 { 2355 return (uintptr_t)p & 1; 2356 } 2357 2358 static inline JSAtomStruct *atom_set_free(uint32_t v) 2359 { 2360 return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); 2361 } 2362 2363 /* Note: the string contents are uninitialized */ 2364 static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) 2365 { 2366 JSString *str; 2367 str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); 2368 if (unlikely(!str)) 2369 return NULL; 2370 js_rc(str)->ref_count = 1; 2371 str->is_wide_char = is_wide_char; 2372 str->len = max_len; 2373 str->atom_type = 0; 2374 str->hash = 0; /* optional but costless */ 2375 str->hash_next = 0; /* optional */ 2376 #ifdef DUMP_LEAKS 2377 list_add_tail(&str->link, &rt->string_list); 2378 #endif 2379 return str; 2380 } 2381 2382 static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) 2383 { 2384 JSString *p; 2385 p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); 2386 if (unlikely(!p)) { 2387 JS_ThrowOutOfMemory(ctx); 2388 return NULL; 2389 } 2390 return p; 2391 } 2392 2393 /* same as JS_FreeValueRT() but faster */ 2394 static inline void js_free_string(JSRuntime *rt, JSString *str) 2395 { 2396 if (--js_rc(str)->ref_count <= 0) { 2397 if (str->atom_type) { 2398 JS_FreeAtomStruct(rt, str); 2399 } else { 2400 #ifdef DUMP_LEAKS 2401 list_del(&str->link); 2402 #endif 2403 js_free_rt(rt, str); 2404 } 2405 } 2406 } 2407 2408 void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) 2409 { 2410 if (rt) 2411 rt->rt_info = s; 2412 } 2413 2414 void JS_FreeRuntime(JSRuntime *rt) 2415 { 2416 struct list_head *el, *el1; 2417 int i; 2418 2419 JS_FreeValueRT(rt, rt->current_exception); 2420 2421 list_for_each_safe(el, el1, &rt->job_list) { 2422 JSJobEntry *e = list_entry(el, JSJobEntry, link); 2423 for(i = 0; i < e->argc; i++) 2424 JS_FreeValueRT(rt, e->argv[i]); 2425 JS_FreeContext(e->realm); 2426 js_free_rt(rt, e); 2427 } 2428 init_list_head(&rt->job_list); 2429 2430 /* don't remove the weak objects to avoid create new jobs with 2431 FinalizationRegistry */ 2432 JS_RunGCInternal(rt, FALSE); 2433 2434 #ifdef DUMP_LEAKS 2435 /* leaking objects */ 2436 { 2437 BOOL header_done; 2438 JSGCObjectHeader *p; 2439 int count; 2440 2441 /* remove the internal refcounts to display only the object 2442 referenced externally */ 2443 list_for_each(el, &rt->gc_obj_list) { 2444 p = list_entry(el, JSGCObjectHeader, link); 2445 js_rc(p)->mark = 0; 2446 } 2447 gc_decref(rt); 2448 2449 header_done = FALSE; 2450 list_for_each(el, &rt->gc_obj_list) { 2451 p = list_entry(el, JSGCObjectHeader, link); 2452 if (js_rc(p)->ref_count != 0) { 2453 if (!header_done) { 2454 printf("Object leaks:\n"); 2455 JS_DumpObjectHeader(rt); 2456 header_done = TRUE; 2457 } 2458 JS_DumpGCObject(rt, p); 2459 } 2460 } 2461 2462 count = 0; 2463 list_for_each(el, &rt->gc_obj_list) { 2464 p = list_entry(el, JSGCObjectHeader, link); 2465 if (js_rc(p)->ref_count == 0) { 2466 count++; 2467 } 2468 } 2469 if (count != 0) 2470 printf("Secondary object leaks: %d\n", count); 2471 } 2472 #endif 2473 assert(list_empty(&rt->gc_obj_list)); 2474 assert(list_empty(&rt->weakref_list)); 2475 2476 /* free the classes */ 2477 for(i = 0; i < rt->class_count; i++) { 2478 JSClass *cl = &rt->class_array[i]; 2479 if (cl->class_id != 0) { 2480 JS_FreeAtomRT(rt, cl->class_name); 2481 } 2482 } 2483 js_free_rt(rt, rt->class_array); 2484 2485 #ifdef DUMP_LEAKS 2486 /* only the atoms defined in JS_InitAtoms() should be left */ 2487 { 2488 BOOL header_done = FALSE; 2489 2490 for(i = 0; i < rt->atom_size; i++) { 2491 JSAtomStruct *p = rt->atom_array[i]; 2492 if (!atom_is_free(p) /* && p->str*/) { 2493 if (i >= JS_ATOM_END || js_rc(p)->ref_count != 1) { 2494 if (!header_done) { 2495 header_done = TRUE; 2496 if (rt->rt_info) { 2497 printf("%s:1: atom leakage:", rt->rt_info); 2498 } else { 2499 printf("Atom leaks:\n" 2500 " %6s %6s %s\n", 2501 "ID", "REFCNT", "NAME"); 2502 } 2503 } 2504 if (rt->rt_info) { 2505 printf(" "); 2506 } else { 2507 printf(" %6u %6u ", i, js_rc(p)->ref_count); 2508 } 2509 switch (p->atom_type) { 2510 case JS_ATOM_TYPE_STRING: 2511 JS_DumpString(rt, p); 2512 break; 2513 case JS_ATOM_TYPE_GLOBAL_SYMBOL: 2514 printf("Symbol.for("); 2515 JS_DumpString(rt, p); 2516 printf(")"); 2517 break; 2518 case JS_ATOM_TYPE_SYMBOL: 2519 if (p->hash != JS_ATOM_HASH_PRIVATE) { 2520 printf("Symbol("); 2521 JS_DumpString(rt, p); 2522 printf(")"); 2523 } else { 2524 printf("Private("); 2525 JS_DumpString(rt, p); 2526 printf(")"); 2527 } 2528 break; 2529 } 2530 if (rt->rt_info) { 2531 printf(":%u", js_rc(p)->ref_count); 2532 } else { 2533 printf("\n"); 2534 } 2535 } 2536 } 2537 } 2538 if (rt->rt_info && header_done) 2539 printf("\n"); 2540 } 2541 #endif 2542 2543 /* free the atoms */ 2544 for(i = 0; i < rt->atom_size; i++) { 2545 JSAtomStruct *p = rt->atom_array[i]; 2546 if (!atom_is_free(p)) { 2547 #ifdef DUMP_LEAKS 2548 list_del(&p->link); 2549 #endif 2550 js_free_rt(rt, p); 2551 } 2552 } 2553 js_free_rt(rt, rt->atom_array); 2554 js_free_rt(rt, rt->atom_hash); 2555 js_free_rt(rt, rt->shape_hash); 2556 #ifdef DUMP_LEAKS 2557 if (!list_empty(&rt->string_list)) { 2558 if (rt->rt_info) { 2559 printf("%s:1: string leakage:", rt->rt_info); 2560 } else { 2561 printf("String leaks:\n" 2562 " %6s %s\n", 2563 "REFCNT", "VALUE"); 2564 } 2565 list_for_each_safe(el, el1, &rt->string_list) { 2566 JSString *str = list_entry(el, JSString, link); 2567 if (rt->rt_info) { 2568 printf(" "); 2569 } else { 2570 printf(" %6u ", js_rc(str)->ref_count); 2571 } 2572 JS_DumpString(rt, str); 2573 if (rt->rt_info) { 2574 printf(":%u", js_rc(str)->ref_count); 2575 } else { 2576 printf("\n"); 2577 } 2578 list_del(&str->link); 2579 js_free_rt(rt, str); 2580 } 2581 if (rt->rt_info) 2582 printf("\n"); 2583 } 2584 { 2585 JSMallocState *s = &rt->malloc_ctx.malloc_state; 2586 if (s->malloc_count > 1) { 2587 if (rt->rt_info) 2588 printf("%s:1: ", rt->rt_info); 2589 printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", 2590 (uint64_t)(s->malloc_size - sizeof(JSRuntime)), 2591 (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); 2592 } 2593 } 2594 #endif 2595 2596 { 2597 JSMallocState ms = rt->malloc_ctx.malloc_state; 2598 rt->malloc_ctx.mf.js_free(&ms, rt); 2599 } 2600 } 2601 2602 JSContext *JS_NewContextRaw(JSRuntime *rt) 2603 { 2604 JSContext *ctx; 2605 int i; 2606 2607 ctx = js_mallocz_rt(rt, sizeof(JSContext)); 2608 if (!ctx) 2609 return NULL; 2610 js_rc(ctx)->ref_count = 1; 2611 add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); 2612 2613 ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * 2614 rt->class_count); 2615 if (!ctx->class_proto) { 2616 js_free_rt(rt, ctx); 2617 return NULL; 2618 } 2619 ctx->rt = rt; 2620 list_add_tail(&ctx->link, &rt->context_list); 2621 for(i = 0; i < rt->class_count; i++) 2622 ctx->class_proto[i] = JS_NULL; 2623 ctx->array_ctor = JS_NULL; 2624 ctx->iterator_ctor = JS_NULL; 2625 ctx->regexp_ctor = JS_NULL; 2626 ctx->promise_ctor = JS_NULL; 2627 init_list_head(&ctx->loaded_modules); 2628 2629 if (JS_AddIntrinsicBasicObjects(ctx)) { 2630 JS_FreeContext(ctx); 2631 return NULL; 2632 } 2633 return ctx; 2634 } 2635 2636 JSContext *JS_NewContext(JSRuntime *rt) 2637 { 2638 JSContext *ctx; 2639 2640 ctx = JS_NewContextRaw(rt); 2641 if (!ctx) 2642 return NULL; 2643 2644 if (JS_AddIntrinsicBaseObjects(ctx) || 2645 JS_AddIntrinsicDate(ctx) || 2646 JS_AddIntrinsicEval(ctx) || 2647 JS_AddIntrinsicStringNormalize(ctx) || 2648 JS_AddIntrinsicRegExp(ctx) || 2649 JS_AddIntrinsicJSON(ctx) || 2650 JS_AddIntrinsicProxy(ctx) || 2651 JS_AddIntrinsicMapSet(ctx) || 2652 JS_AddIntrinsicTypedArrays(ctx) || 2653 JS_AddIntrinsicPromise(ctx) || 2654 JS_AddIntrinsicWeakRef(ctx)) { 2655 JS_FreeContext(ctx); 2656 return NULL; 2657 } 2658 return ctx; 2659 } 2660 2661 void *JS_GetContextOpaque(JSContext *ctx) 2662 { 2663 return ctx->user_opaque; 2664 } 2665 2666 void JS_SetContextOpaque(JSContext *ctx, void *opaque) 2667 { 2668 ctx->user_opaque = opaque; 2669 } 2670 2671 /* set the new value and free the old value after (freeing the value 2672 can reallocate the object data) */ 2673 static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) 2674 { 2675 JSValue old_val; 2676 old_val = *pval; 2677 *pval = new_val; 2678 JS_FreeValue(ctx, old_val); 2679 } 2680 2681 void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) 2682 { 2683 JSRuntime *rt = ctx->rt; 2684 assert(class_id < rt->class_count); 2685 set_value(ctx, &ctx->class_proto[class_id], obj); 2686 } 2687 2688 JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) 2689 { 2690 JSRuntime *rt = ctx->rt; 2691 assert(class_id < rt->class_count); 2692 return JS_DupValue(ctx, ctx->class_proto[class_id]); 2693 } 2694 2695 typedef enum JSFreeModuleEnum { 2696 JS_FREE_MODULE_ALL, 2697 JS_FREE_MODULE_NOT_RESOLVED, 2698 } JSFreeModuleEnum; 2699 2700 /* XXX: would be more efficient with separate module lists */ 2701 static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) 2702 { 2703 struct list_head *el, *el1; 2704 list_for_each_safe(el, el1, &ctx->loaded_modules) { 2705 JSModuleDef *m = list_entry(el, JSModuleDef, link); 2706 if (flag == JS_FREE_MODULE_ALL || 2707 (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { 2708 /* warning: the module may be referenced elsewhere. It 2709 could be simpler to use an array instead of a list for 2710 'ctx->loaded_modules' */ 2711 list_del(&m->link); 2712 m->link.prev = NULL; 2713 m->link.next = NULL; 2714 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); 2715 } 2716 } 2717 } 2718 2719 JSContext *JS_DupContext(JSContext *ctx) 2720 { 2721 js_rc(ctx)->ref_count++; 2722 return ctx; 2723 } 2724 2725 /* used by the GC */ 2726 static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, 2727 JS_MarkFunc *mark_func) 2728 { 2729 int i; 2730 struct list_head *el; 2731 2732 list_for_each(el, &ctx->loaded_modules) { 2733 JSModuleDef *m = list_entry(el, JSModuleDef, link); 2734 JS_MarkValue(rt, JS_MKPTR(JS_TAG_MODULE, m), mark_func); 2735 } 2736 2737 JS_MarkValue(rt, ctx->global_obj, mark_func); 2738 JS_MarkValue(rt, ctx->global_var_obj, mark_func); 2739 2740 JS_MarkValue(rt, ctx->throw_type_error, mark_func); 2741 JS_MarkValue(rt, ctx->eval_obj, mark_func); 2742 2743 JS_MarkValue(rt, ctx->array_proto_values, mark_func); 2744 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 2745 JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); 2746 } 2747 for(i = 0; i < rt->class_count; i++) { 2748 JS_MarkValue(rt, ctx->class_proto[i], mark_func); 2749 } 2750 JS_MarkValue(rt, ctx->iterator_ctor, mark_func); 2751 JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); 2752 JS_MarkValue(rt, ctx->promise_ctor, mark_func); 2753 JS_MarkValue(rt, ctx->array_ctor, mark_func); 2754 JS_MarkValue(rt, ctx->regexp_ctor, mark_func); 2755 JS_MarkValue(rt, ctx->function_ctor, mark_func); 2756 JS_MarkValue(rt, ctx->function_proto, mark_func); 2757 2758 if (ctx->array_shape) 2759 mark_func(rt, &ctx->array_shape->header); 2760 2761 if (ctx->arguments_shape) 2762 mark_func(rt, &ctx->arguments_shape->header); 2763 2764 if (ctx->mapped_arguments_shape) 2765 mark_func(rt, &ctx->mapped_arguments_shape->header); 2766 2767 if (ctx->regexp_shape) 2768 mark_func(rt, &ctx->regexp_shape->header); 2769 2770 if (ctx->regexp_result_shape) 2771 mark_func(rt, &ctx->regexp_result_shape->header); 2772 } 2773 2774 void JS_FreeContext(JSContext *ctx) 2775 { 2776 JSRuntime *rt = ctx->rt; 2777 int i; 2778 2779 if (--js_rc(ctx)->ref_count > 0) 2780 return; 2781 assert(js_rc(ctx)->ref_count == 0); 2782 2783 #ifdef DUMP_ATOMS 2784 JS_DumpAtoms(ctx->rt); 2785 #endif 2786 #ifdef DUMP_SHAPES 2787 JS_DumpShapes(ctx->rt); 2788 #endif 2789 #ifdef DUMP_OBJECTS 2790 { 2791 struct list_head *el; 2792 JSGCObjectHeader *p; 2793 printf("JSObjects: {\n"); 2794 JS_DumpObjectHeader(ctx->rt); 2795 list_for_each(el, &rt->gc_obj_list) { 2796 p = list_entry(el, JSGCObjectHeader, link); 2797 JS_DumpGCObject(rt, p); 2798 } 2799 printf("}\n"); 2800 } 2801 #endif 2802 #ifdef DUMP_MEM 2803 { 2804 JSMemoryUsage stats; 2805 JS_ComputeMemoryUsage(rt, &stats); 2806 JS_DumpMemoryUsage(stdout, &stats, rt); 2807 } 2808 #endif 2809 2810 js_free_modules(ctx, JS_FREE_MODULE_ALL); 2811 2812 JS_FreeValue(ctx, ctx->global_obj); 2813 JS_FreeValue(ctx, ctx->global_var_obj); 2814 2815 JS_FreeValue(ctx, ctx->throw_type_error); 2816 JS_FreeValue(ctx, ctx->eval_obj); 2817 2818 JS_FreeValue(ctx, ctx->array_proto_values); 2819 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 2820 JS_FreeValue(ctx, ctx->native_error_proto[i]); 2821 } 2822 for(i = 0; i < rt->class_count; i++) { 2823 JS_FreeValue(ctx, ctx->class_proto[i]); 2824 } 2825 js_free_rt(rt, ctx->class_proto); 2826 JS_FreeValue(ctx, ctx->iterator_ctor); 2827 JS_FreeValue(ctx, ctx->async_iterator_proto); 2828 JS_FreeValue(ctx, ctx->promise_ctor); 2829 JS_FreeValue(ctx, ctx->array_ctor); 2830 JS_FreeValue(ctx, ctx->regexp_ctor); 2831 JS_FreeValue(ctx, ctx->function_ctor); 2832 JS_FreeValue(ctx, ctx->function_proto); 2833 2834 js_free_shape_null(ctx->rt, ctx->array_shape); 2835 js_free_shape_null(ctx->rt, ctx->arguments_shape); 2836 js_free_shape_null(ctx->rt, ctx->mapped_arguments_shape); 2837 js_free_shape_null(ctx->rt, ctx->regexp_shape); 2838 js_free_shape_null(ctx->rt, ctx->regexp_result_shape); 2839 2840 list_del(&ctx->link); 2841 remove_gc_object(&ctx->header); 2842 js_free_rt(ctx->rt, ctx); 2843 } 2844 2845 JSRuntime *JS_GetRuntime(JSContext *ctx) 2846 { 2847 return ctx->rt; 2848 } 2849 2850 static void update_stack_limit(JSRuntime *rt) 2851 { 2852 if (rt->stack_size == 0) { 2853 rt->stack_limit = 0; /* no limit */ 2854 } else { 2855 rt->stack_limit = rt->stack_top - rt->stack_size; 2856 } 2857 } 2858 2859 void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) 2860 { 2861 rt->stack_size = stack_size; 2862 update_stack_limit(rt); 2863 } 2864 2865 void JS_UpdateStackTop(JSRuntime *rt) 2866 { 2867 rt->stack_top = js_get_stack_pointer(); 2868 update_stack_limit(rt); 2869 } 2870 2871 static inline BOOL is_strict_mode(JSContext *ctx) 2872 { 2873 JSStackFrame *sf = ctx->rt->current_stack_frame; 2874 return (sf && (sf->js_mode & JS_MODE_STRICT)); 2875 } 2876 2877 /* JSAtom support */ 2878 2879 #define JS_ATOM_TAG_INT (1U << 31) 2880 #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) 2881 #define JS_ATOM_MAX ((1U << 30) - 1) 2882 2883 /* return the max count from the hash size */ 2884 #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) 2885 2886 static inline BOOL __JS_AtomIsConst(JSAtom v) 2887 { 2888 #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 2889 return (int32_t)v <= 0; 2890 #else 2891 return (int32_t)v < JS_ATOM_END; 2892 #endif 2893 } 2894 2895 static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) 2896 { 2897 return (v & JS_ATOM_TAG_INT) != 0; 2898 } 2899 2900 static inline JSAtom __JS_AtomFromUInt32(uint32_t v) 2901 { 2902 return v | JS_ATOM_TAG_INT; 2903 } 2904 2905 static inline uint32_t __JS_AtomToUInt32(JSAtom atom) 2906 { 2907 return atom & ~JS_ATOM_TAG_INT; 2908 } 2909 2910 static inline int is_num(int c) 2911 { 2912 return c >= '0' && c <= '9'; 2913 } 2914 2915 /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ 2916 static inline BOOL is_num_string(uint32_t *pval, const JSString *p) 2917 { 2918 uint32_t n; 2919 uint64_t n64; 2920 int c, i, len; 2921 2922 len = p->len; 2923 if (len == 0 || len > 10) 2924 return FALSE; 2925 c = string_get(p, 0); 2926 if (is_num(c)) { 2927 if (c == '0') { 2928 if (len != 1) 2929 return FALSE; 2930 n = 0; 2931 } else { 2932 n = c - '0'; 2933 for(i = 1; i < len; i++) { 2934 c = string_get(p, i); 2935 if (!is_num(c)) 2936 return FALSE; 2937 n64 = (uint64_t)n * 10 + (c - '0'); 2938 if ((n64 >> 32) != 0) 2939 return FALSE; 2940 n = n64; 2941 } 2942 } 2943 *pval = n; 2944 return TRUE; 2945 } else { 2946 return FALSE; 2947 } 2948 } 2949 2950 /* XXX: could use faster version ? */ 2951 static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) 2952 { 2953 size_t i; 2954 2955 for(i = 0; i < len; i++) 2956 h = h * 263 + str[i]; 2957 return h; 2958 } 2959 2960 static inline uint32_t hash_string16(const uint16_t *str, 2961 size_t len, uint32_t h) 2962 { 2963 size_t i; 2964 2965 for(i = 0; i < len; i++) 2966 h = h * 263 + str[i]; 2967 return h; 2968 } 2969 2970 static uint32_t hash_string(const JSString *str, uint32_t h) 2971 { 2972 if (str->is_wide_char) 2973 h = hash_string16(str->u.str16, str->len, h); 2974 else 2975 h = hash_string8(str->u.str8, str->len, h); 2976 return h; 2977 } 2978 2979 static uint32_t hash_string_rope(JSValueConst val, uint32_t h) 2980 { 2981 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 2982 return hash_string(JS_VALUE_GET_STRING(val), h); 2983 } else { 2984 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); 2985 h = hash_string_rope(r->left, h); 2986 return hash_string_rope(r->right, h); 2987 } 2988 } 2989 2990 static __maybe_unused void JS_DumpChar(FILE *fo, int c, int sep) 2991 { 2992 if (c == sep || c == '\\') { 2993 fputc('\\', fo); 2994 fputc(c, fo); 2995 } else if (c >= ' ' && c <= 126) { 2996 fputc(c, fo); 2997 } else if (c == '\n') { 2998 fputc('\\', fo); 2999 fputc('n', fo); 3000 } else { 3001 fprintf(fo, "\\u%04x", c); 3002 } 3003 } 3004 3005 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) 3006 { 3007 int i, sep; 3008 3009 if (p == NULL) { 3010 printf("<null>"); 3011 return; 3012 } 3013 printf("%d", js_rc((void *)p)->ref_count); 3014 sep = (js_rc((void *)p)->ref_count == 1) ? '\"' : '\''; 3015 putchar(sep); 3016 for(i = 0; i < p->len; i++) { 3017 JS_DumpChar(stdout, string_get(p, i), sep); 3018 } 3019 putchar(sep); 3020 } 3021 3022 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) 3023 { 3024 JSAtomStruct *p; 3025 int h, i; 3026 /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ 3027 printf("JSAtom count=%d size=%d hash_size=%d:\n", 3028 rt->atom_count, rt->atom_size, rt->atom_hash_size); 3029 printf("JSAtom hash table: {\n"); 3030 for(i = 0; i < rt->atom_hash_size; i++) { 3031 h = rt->atom_hash[i]; 3032 if (h) { 3033 printf(" %d:", i); 3034 while (h) { 3035 p = rt->atom_array[h]; 3036 printf(" "); 3037 JS_DumpString(rt, p); 3038 h = p->hash_next; 3039 } 3040 printf("\n"); 3041 } 3042 } 3043 printf("}\n"); 3044 printf("JSAtom table: {\n"); 3045 for(i = 0; i < rt->atom_size; i++) { 3046 p = rt->atom_array[i]; 3047 if (!atom_is_free(p)) { 3048 printf(" %d: { %d %08x ", i, p->atom_type, p->hash); 3049 if (!(p->len == 0 && p->is_wide_char != 0)) 3050 JS_DumpString(rt, p); 3051 printf(" %d }\n", p->hash_next); 3052 } 3053 } 3054 printf("}\n"); 3055 } 3056 3057 static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) 3058 { 3059 JSAtomStruct *p; 3060 uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; 3061 3062 assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ 3063 new_hash_mask = new_hash_size - 1; 3064 new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); 3065 if (!new_hash) 3066 return -1; 3067 for(i = 0; i < rt->atom_hash_size; i++) { 3068 h = rt->atom_hash[i]; 3069 while (h != 0) { 3070 p = rt->atom_array[h]; 3071 hash_next1 = p->hash_next; 3072 /* add in new hash table */ 3073 j = p->hash & new_hash_mask; 3074 p->hash_next = new_hash[j]; 3075 new_hash[j] = h; 3076 h = hash_next1; 3077 } 3078 } 3079 js_free_rt(rt, rt->atom_hash); 3080 rt->atom_hash = new_hash; 3081 rt->atom_hash_size = new_hash_size; 3082 rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); 3083 // JS_DumpAtoms(rt); 3084 return 0; 3085 } 3086 3087 static int JS_InitAtoms(JSRuntime *rt) 3088 { 3089 int i, len, atom_type; 3090 const char *p; 3091 3092 rt->atom_hash_size = 0; 3093 rt->atom_hash = NULL; 3094 rt->atom_count = 0; 3095 rt->atom_size = 0; 3096 rt->atom_free_index = 0; 3097 if (JS_ResizeAtomHash(rt, 512)) /* there are at least 504 predefined atoms */ 3098 return -1; 3099 3100 p = js_atom_init; 3101 for(i = 1; i < JS_ATOM_END; i++) { 3102 if (i == JS_ATOM_Private_brand) 3103 atom_type = JS_ATOM_TYPE_PRIVATE; 3104 else if (i >= JS_ATOM_Symbol_toPrimitive) 3105 atom_type = JS_ATOM_TYPE_SYMBOL; 3106 else 3107 atom_type = JS_ATOM_TYPE_STRING; 3108 len = strlen(p); 3109 if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) 3110 return -1; 3111 p = p + len + 1; 3112 } 3113 return 0; 3114 } 3115 3116 static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) 3117 { 3118 JSAtomStruct *p; 3119 3120 if (!__JS_AtomIsConst(v)) { 3121 p = rt->atom_array[v]; 3122 js_rc(p)->ref_count++; 3123 } 3124 return v; 3125 } 3126 3127 JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) 3128 { 3129 JSRuntime *rt; 3130 JSAtomStruct *p; 3131 3132 if (!__JS_AtomIsConst(v)) { 3133 rt = ctx->rt; 3134 p = rt->atom_array[v]; 3135 js_rc(p)->ref_count++; 3136 } 3137 return v; 3138 } 3139 3140 static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) 3141 { 3142 JSRuntime *rt; 3143 JSAtomStruct *p; 3144 3145 rt = ctx->rt; 3146 if (__JS_AtomIsTaggedInt(v)) 3147 return JS_ATOM_KIND_STRING; 3148 p = rt->atom_array[v]; 3149 switch(p->atom_type) { 3150 case JS_ATOM_TYPE_STRING: 3151 return JS_ATOM_KIND_STRING; 3152 case JS_ATOM_TYPE_GLOBAL_SYMBOL: 3153 return JS_ATOM_KIND_SYMBOL; 3154 case JS_ATOM_TYPE_SYMBOL: 3155 if (p->hash == JS_ATOM_HASH_PRIVATE) 3156 return JS_ATOM_KIND_PRIVATE; 3157 else 3158 return JS_ATOM_KIND_SYMBOL; 3159 default: 3160 abort(); 3161 } 3162 } 3163 3164 static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) 3165 { 3166 return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; 3167 } 3168 3169 static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) 3170 { 3171 uint32_t i = p->hash_next; /* atom_index */ 3172 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { 3173 JSAtomStruct *p1; 3174 3175 i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; 3176 p1 = rt->atom_array[i]; 3177 while (p1 != p) { 3178 assert(i != 0); 3179 i = p1->hash_next; 3180 p1 = rt->atom_array[i]; 3181 } 3182 } 3183 return i; 3184 } 3185 3186 /* string case (internal). Return JS_ATOM_NULL if error. 'str' is 3187 freed. */ 3188 static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) 3189 { 3190 uint32_t h, h1, i; 3191 JSAtomStruct *p; 3192 int len; 3193 3194 #if 0 3195 printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); 3196 #endif 3197 if (atom_type < JS_ATOM_TYPE_SYMBOL) { 3198 /* str is not NULL */ 3199 if (str->atom_type == atom_type) { 3200 /* str is the atom, return its index */ 3201 i = js_get_atom_index(rt, str); 3202 /* reduce string refcount and increase atom's unless constant */ 3203 if (__JS_AtomIsConst(i)) 3204 js_rc(str)->ref_count--; 3205 return i; 3206 } 3207 /* try and locate an already registered atom */ 3208 len = str->len; 3209 h = hash_string(str, atom_type); 3210 h &= JS_ATOM_HASH_MASK; 3211 h1 = h & (rt->atom_hash_size - 1); 3212 i = rt->atom_hash[h1]; 3213 while (i != 0) { 3214 p = rt->atom_array[i]; 3215 if (p->hash == h && 3216 p->atom_type == atom_type && 3217 p->len == len && 3218 js_string_memcmp(p, 0, str, 0, len) == 0) { 3219 if (!__JS_AtomIsConst(i)) 3220 js_rc(p)->ref_count++; 3221 goto done; 3222 } 3223 i = p->hash_next; 3224 } 3225 } else { 3226 h1 = 0; /* avoid warning */ 3227 if (atom_type == JS_ATOM_TYPE_SYMBOL) { 3228 h = 0; 3229 } else { 3230 h = JS_ATOM_HASH_PRIVATE; 3231 atom_type = JS_ATOM_TYPE_SYMBOL; 3232 } 3233 } 3234 3235 if (rt->atom_free_index == 0) { 3236 /* allow new atom entries */ 3237 uint32_t new_size, start; 3238 JSAtomStruct **new_array; 3239 3240 /* alloc new with size progression 3/2: 3241 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 3242 preallocating space for predefined atoms (at least 504). 3243 */ 3244 new_size = max_int(711, rt->atom_size * 3 / 2); 3245 if (new_size > JS_ATOM_MAX) 3246 goto fail; 3247 /* XXX: should use realloc2 to use slack space */ 3248 new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); 3249 if (!new_array) 3250 goto fail; 3251 /* Note: the atom 0 is not used */ 3252 start = rt->atom_size; 3253 if (start == 0) { 3254 /* JS_ATOM_NULL entry */ 3255 p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); 3256 if (!p) { 3257 js_free_rt(rt, new_array); 3258 goto fail; 3259 } 3260 js_rc(p)->ref_count = 1; /* not refcounted */ 3261 p->atom_type = JS_ATOM_TYPE_SYMBOL; 3262 #ifdef DUMP_LEAKS 3263 list_add_tail(&p->link, &rt->string_list); 3264 #endif 3265 new_array[0] = p; 3266 rt->atom_count++; 3267 start = 1; 3268 } 3269 rt->atom_size = new_size; 3270 rt->atom_array = new_array; 3271 rt->atom_free_index = start; 3272 for(i = start; i < new_size; i++) { 3273 uint32_t next; 3274 if (i == (new_size - 1)) 3275 next = 0; 3276 else 3277 next = i + 1; 3278 rt->atom_array[i] = atom_set_free(next); 3279 } 3280 } 3281 3282 if (str) { 3283 if (str->atom_type == 0) { 3284 p = str; 3285 p->atom_type = atom_type; 3286 } else { 3287 p = js_malloc_rt(rt, sizeof(JSString) + 3288 (str->len << str->is_wide_char) + 3289 1 - str->is_wide_char); 3290 if (unlikely(!p)) 3291 goto fail; 3292 js_rc(p)->ref_count = 1; 3293 p->is_wide_char = str->is_wide_char; 3294 p->len = str->len; 3295 #ifdef DUMP_LEAKS 3296 list_add_tail(&p->link, &rt->string_list); 3297 #endif 3298 memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + 3299 1 - str->is_wide_char); 3300 js_free_string(rt, str); 3301 } 3302 } else { 3303 p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ 3304 if (!p) 3305 return JS_ATOM_NULL; 3306 js_rc(p)->ref_count = 1; 3307 p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ 3308 p->len = 0; 3309 #ifdef DUMP_LEAKS 3310 list_add_tail(&p->link, &rt->string_list); 3311 #endif 3312 } 3313 3314 /* use an already free entry */ 3315 i = rt->atom_free_index; 3316 rt->atom_free_index = atom_get_free(rt->atom_array[i]); 3317 rt->atom_array[i] = p; 3318 3319 p->hash = h; 3320 p->hash_next = i; /* atom_index */ 3321 p->atom_type = atom_type; 3322 3323 rt->atom_count++; 3324 3325 if (atom_type != JS_ATOM_TYPE_SYMBOL) { 3326 p->hash_next = rt->atom_hash[h1]; 3327 rt->atom_hash[h1] = i; 3328 if (unlikely(rt->atom_count >= rt->atom_count_resize)) 3329 JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); 3330 } 3331 3332 // JS_DumpAtoms(rt); 3333 return i; 3334 3335 fail: 3336 i = JS_ATOM_NULL; 3337 done: 3338 if (str) 3339 js_free_string(rt, str); 3340 return i; 3341 } 3342 3343 /* only works with zero terminated 8 bit strings */ 3344 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, 3345 int atom_type) 3346 { 3347 JSString *p; 3348 p = js_alloc_string_rt(rt, len, 0); 3349 if (!p) 3350 return JS_ATOM_NULL; 3351 memcpy(p->u.str8, str, len); 3352 p->u.str8[len] = '\0'; 3353 return __JS_NewAtom(rt, p, atom_type); 3354 } 3355 3356 /* Warning: str must be ASCII only */ 3357 static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, 3358 int atom_type) 3359 { 3360 uint32_t h, h1, i; 3361 JSAtomStruct *p; 3362 3363 h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); 3364 h &= JS_ATOM_HASH_MASK; 3365 h1 = h & (rt->atom_hash_size - 1); 3366 i = rt->atom_hash[h1]; 3367 while (i != 0) { 3368 p = rt->atom_array[i]; 3369 if (p->hash == h && 3370 p->atom_type == JS_ATOM_TYPE_STRING && 3371 p->len == len && 3372 p->is_wide_char == 0 && 3373 memcmp(p->u.str8, str, len) == 0) { 3374 if (!__JS_AtomIsConst(i)) 3375 js_rc(p)->ref_count++; 3376 return i; 3377 } 3378 i = p->hash_next; 3379 } 3380 return JS_ATOM_NULL; 3381 } 3382 3383 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) 3384 { 3385 #if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ 3386 if (unlikely(i == JS_ATOM_NULL)) { 3387 js_rc(p)->ref_count = INT32_MAX / 2; 3388 return; 3389 } 3390 #endif 3391 uint32_t i = p->hash_next; /* atom_index */ 3392 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { 3393 JSAtomStruct *p0, *p1; 3394 uint32_t h0; 3395 3396 h0 = p->hash & (rt->atom_hash_size - 1); 3397 i = rt->atom_hash[h0]; 3398 p1 = rt->atom_array[i]; 3399 if (p1 == p) { 3400 rt->atom_hash[h0] = p1->hash_next; 3401 } else { 3402 for(;;) { 3403 assert(i != 0); 3404 p0 = p1; 3405 i = p1->hash_next; 3406 p1 = rt->atom_array[i]; 3407 if (p1 == p) { 3408 p0->hash_next = p1->hash_next; 3409 break; 3410 } 3411 } 3412 } 3413 } 3414 /* insert in free atom list */ 3415 rt->atom_array[i] = atom_set_free(rt->atom_free_index); 3416 rt->atom_free_index = i; 3417 /* free the string structure */ 3418 #ifdef DUMP_LEAKS 3419 list_del(&p->link); 3420 #endif 3421 if (p->atom_type == JS_ATOM_TYPE_SYMBOL && 3422 p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) { 3423 /* live weak references are still present on this object: keep 3424 it */ 3425 } else { 3426 js_free_rt(rt, p); 3427 } 3428 rt->atom_count--; 3429 assert(rt->atom_count >= 0); 3430 } 3431 3432 static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) 3433 { 3434 JSAtomStruct *p; 3435 3436 p = rt->atom_array[i]; 3437 if (--js_rc(p)->ref_count > 0) 3438 return; 3439 JS_FreeAtomStruct(rt, p); 3440 } 3441 3442 /* Warning: 'p' is freed */ 3443 static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) 3444 { 3445 JSRuntime *rt = ctx->rt; 3446 uint32_t n; 3447 if (is_num_string(&n, p)) { 3448 if (n <= JS_ATOM_MAX_INT) { 3449 js_free_string(rt, p); 3450 return __JS_AtomFromUInt32(n); 3451 } 3452 } 3453 /* XXX: should generate an exception */ 3454 return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); 3455 } 3456 3457 /* XXX: optimize */ 3458 static size_t count_ascii(const uint8_t *buf, size_t len) 3459 { 3460 const uint8_t *p, *p_end; 3461 p = buf; 3462 p_end = buf + len; 3463 while (p < p_end && *p < 128) 3464 p++; 3465 return p - buf; 3466 } 3467 3468 /* str is UTF-8 encoded */ 3469 JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) 3470 { 3471 JSValue val; 3472 3473 if (len == 0 || 3474 (!is_digit(*str) && 3475 count_ascii((const uint8_t *)str, len) == len)) { 3476 JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); 3477 if (atom) 3478 return atom; 3479 } 3480 val = JS_NewStringLen(ctx, str, len); 3481 if (JS_IsException(val)) 3482 return JS_ATOM_NULL; 3483 return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); 3484 } 3485 3486 JSAtom JS_NewAtom(JSContext *ctx, const char *str) 3487 { 3488 return JS_NewAtomLen(ctx, str, strlen(str)); 3489 } 3490 3491 JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) 3492 { 3493 if (n <= JS_ATOM_MAX_INT) { 3494 return __JS_AtomFromUInt32(n); 3495 } else { 3496 char buf[11]; 3497 JSValue val; 3498 size_t len; 3499 len = u32toa(buf, n); 3500 val = js_new_string8_len(ctx, buf, len); 3501 if (JS_IsException(val)) 3502 return JS_ATOM_NULL; 3503 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), 3504 JS_ATOM_TYPE_STRING); 3505 } 3506 } 3507 3508 static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) 3509 { 3510 if ((uint64_t)n <= JS_ATOM_MAX_INT) { 3511 return __JS_AtomFromUInt32((uint32_t)n); 3512 } else { 3513 char buf[24]; 3514 JSValue val; 3515 size_t len; 3516 len = i64toa(buf, n); 3517 val = js_new_string8_len(ctx, buf, len); 3518 if (JS_IsException(val)) 3519 return JS_ATOM_NULL; 3520 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), 3521 JS_ATOM_TYPE_STRING); 3522 } 3523 } 3524 3525 /* 'p' is freed */ 3526 static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) 3527 { 3528 JSRuntime *rt = ctx->rt; 3529 JSAtom atom; 3530 atom = __JS_NewAtom(rt, p, atom_type); 3531 if (atom == JS_ATOM_NULL) 3532 return JS_ThrowOutOfMemory(ctx); 3533 return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); 3534 } 3535 3536 /* descr must be a non-numeric string atom */ 3537 static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, 3538 int atom_type) 3539 { 3540 JSRuntime *rt = ctx->rt; 3541 JSString *p; 3542 3543 assert(!__JS_AtomIsTaggedInt(descr)); 3544 assert(descr < rt->atom_size); 3545 p = rt->atom_array[descr]; 3546 JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3547 return JS_NewSymbol(ctx, p, atom_type); 3548 } 3549 3550 #define ATOM_GET_STR_BUF_SIZE 64 3551 3552 /* Should only be used for debug. */ 3553 static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, 3554 JSAtom atom) 3555 { 3556 if (__JS_AtomIsTaggedInt(atom)) { 3557 snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); 3558 } else { 3559 JSAtomStruct *p; 3560 assert(atom < rt->atom_size); 3561 if (atom == JS_ATOM_NULL) { 3562 snprintf(buf, buf_size, "<null>"); 3563 } else { 3564 int i, c; 3565 char *q; 3566 JSString *str; 3567 3568 q = buf; 3569 p = rt->atom_array[atom]; 3570 assert(!atom_is_free(p)); 3571 str = p; 3572 if (str) { 3573 if (!str->is_wide_char) { 3574 /* special case ASCII strings */ 3575 c = 0; 3576 for(i = 0; i < str->len; i++) { 3577 c |= str->u.str8[i]; 3578 } 3579 if (c < 0x80) 3580 return (const char *)str->u.str8; 3581 } 3582 for(i = 0; i < str->len; i++) { 3583 c = string_get(str, i); 3584 if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) 3585 break; 3586 if (c < 128) { 3587 *q++ = c; 3588 } else { 3589 q += unicode_to_utf8((uint8_t *)q, c); 3590 } 3591 } 3592 } 3593 *q = '\0'; 3594 } 3595 } 3596 return buf; 3597 } 3598 3599 static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) 3600 { 3601 return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); 3602 } 3603 3604 static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) 3605 { 3606 char buf[ATOM_GET_STR_BUF_SIZE]; 3607 3608 if (__JS_AtomIsTaggedInt(atom)) { 3609 size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); 3610 return js_new_string8_len(ctx, buf, len); 3611 } else { 3612 JSRuntime *rt = ctx->rt; 3613 JSAtomStruct *p; 3614 assert(atom < rt->atom_size); 3615 p = rt->atom_array[atom]; 3616 if (p->atom_type == JS_ATOM_TYPE_STRING) { 3617 goto ret_string; 3618 } else if (force_string) { 3619 if (p->len == 0 && p->is_wide_char != 0) { 3620 /* no description string */ 3621 p = rt->atom_array[JS_ATOM_empty_string]; 3622 } 3623 ret_string: 3624 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3625 } else { 3626 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p)); 3627 } 3628 } 3629 } 3630 3631 JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) 3632 { 3633 return __JS_AtomToValue(ctx, atom, FALSE); 3634 } 3635 3636 JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) 3637 { 3638 return __JS_AtomToValue(ctx, atom, TRUE); 3639 } 3640 3641 /* return TRUE if the atom is an array index (i.e. 0 <= index <= 3642 2^32-2 and return its value */ 3643 static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) 3644 { 3645 if (__JS_AtomIsTaggedInt(atom)) { 3646 *pval = __JS_AtomToUInt32(atom); 3647 return TRUE; 3648 } else { 3649 JSRuntime *rt = ctx->rt; 3650 JSAtomStruct *p; 3651 uint32_t val; 3652 3653 assert(atom < rt->atom_size); 3654 p = rt->atom_array[atom]; 3655 if (p->atom_type == JS_ATOM_TYPE_STRING && 3656 is_num_string(&val, p) && val != -1) { 3657 *pval = val; 3658 return TRUE; 3659 } else { 3660 *pval = 0; 3661 return FALSE; 3662 } 3663 } 3664 } 3665 3666 /* This test must be fast if atom is not a numeric index (e.g. a 3667 method name). Return JS_UNDEFINED if not a numeric 3668 index. JS_EXCEPTION can also be returned. */ 3669 static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) 3670 { 3671 JSRuntime *rt = ctx->rt; 3672 JSAtomStruct *p1; 3673 JSString *p; 3674 int c, ret; 3675 JSValue num, str; 3676 3677 if (__JS_AtomIsTaggedInt(atom)) 3678 return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); 3679 assert(atom < rt->atom_size); 3680 p1 = rt->atom_array[atom]; 3681 if (p1->atom_type != JS_ATOM_TYPE_STRING) 3682 return JS_UNDEFINED; 3683 switch(atom) { 3684 case JS_ATOM_minus_zero: 3685 return __JS_NewFloat64(ctx, -0.0); 3686 case JS_ATOM_Infinity: 3687 return __JS_NewFloat64(ctx, INFINITY); 3688 case JS_ATOM_minus_Infinity: 3689 return __JS_NewFloat64(ctx, -INFINITY); 3690 case JS_ATOM_NaN: 3691 return __JS_NewFloat64(ctx, NAN); 3692 default: 3693 break; 3694 } 3695 p = p1; 3696 if (p->len == 0) 3697 return JS_UNDEFINED; 3698 c = string_get(p, 0); 3699 if (!is_num(c) && c != '-') 3700 return JS_UNDEFINED; 3701 /* this is ECMA CanonicalNumericIndexString primitive */ 3702 num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3703 if (JS_IsException(num)) 3704 return num; 3705 str = JS_ToString(ctx, num); 3706 if (JS_IsException(str)) { 3707 JS_FreeValue(ctx, num); 3708 return str; 3709 } 3710 ret = js_string_eq(ctx, p, JS_VALUE_GET_STRING(str)); 3711 JS_FreeValue(ctx, str); 3712 if (ret) { 3713 return num; 3714 } else { 3715 JS_FreeValue(ctx, num); 3716 return JS_UNDEFINED; 3717 } 3718 } 3719 3720 /* return -1 if exception or TRUE/FALSE */ 3721 static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) 3722 { 3723 JSValue num; 3724 num = JS_AtomIsNumericIndex1(ctx, atom); 3725 if (likely(JS_IsUndefined(num))) 3726 return FALSE; 3727 if (JS_IsException(num)) 3728 return -1; 3729 JS_FreeValue(ctx, num); 3730 return TRUE; 3731 } 3732 3733 void JS_FreeAtom(JSContext *ctx, JSAtom v) 3734 { 3735 if (!__JS_AtomIsConst(v)) 3736 __JS_FreeAtom(ctx->rt, v); 3737 } 3738 3739 void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) 3740 { 3741 if (!__JS_AtomIsConst(v)) 3742 __JS_FreeAtom(rt, v); 3743 } 3744 3745 /* return TRUE if 'v' is a symbol with a string description */ 3746 static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) 3747 { 3748 JSRuntime *rt; 3749 JSAtomStruct *p; 3750 3751 rt = ctx->rt; 3752 if (__JS_AtomIsTaggedInt(v)) 3753 return FALSE; 3754 p = rt->atom_array[v]; 3755 return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && 3756 p->hash != JS_ATOM_HASH_PRIVATE) || 3757 p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && 3758 !(p->len == 0 && p->is_wide_char != 0)); 3759 } 3760 3761 /* free with JS_FreeCString() */ 3762 const char *JS_AtomToCStringLen(JSContext *ctx, size_t *plen, JSAtom atom) 3763 { 3764 JSValue str; 3765 const char *cstr; 3766 3767 str = JS_AtomToString(ctx, atom); 3768 if (JS_IsException(str)) { 3769 if (plen) 3770 *plen = 0; 3771 return NULL; 3772 } 3773 cstr = JS_ToCStringLen(ctx, plen, str); 3774 JS_FreeValue(ctx, str); 3775 return cstr; 3776 } 3777 3778 /* return a string atom containing name concatenated with str1 */ 3779 static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) 3780 { 3781 JSValue str; 3782 JSAtom atom; 3783 const char *cstr; 3784 char *cstr2; 3785 size_t len, len1; 3786 3787 str = JS_AtomToString(ctx, name); 3788 if (JS_IsException(str)) 3789 return JS_ATOM_NULL; 3790 cstr = JS_ToCStringLen(ctx, &len, str); 3791 if (!cstr) 3792 goto fail; 3793 len1 = strlen(str1); 3794 cstr2 = js_malloc(ctx, len + len1 + 1); 3795 if (!cstr2) 3796 goto fail; 3797 memcpy(cstr2, cstr, len); 3798 memcpy(cstr2 + len, str1, len1); 3799 cstr2[len + len1] = '\0'; 3800 atom = JS_NewAtomLen(ctx, cstr2, len + len1); 3801 js_free(ctx, cstr2); 3802 JS_FreeCString(ctx, cstr); 3803 JS_FreeValue(ctx, str); 3804 return atom; 3805 fail: 3806 JS_FreeCString(ctx, cstr); 3807 JS_FreeValue(ctx, str); 3808 return JS_ATOM_NULL; 3809 } 3810 3811 static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) 3812 { 3813 char buf[16]; 3814 size_t len; 3815 len = u32toa(buf, n); 3816 buf[len] = '\0'; 3817 return js_atom_concat_str(ctx, name, buf); 3818 } 3819 3820 static inline BOOL JS_IsEmptyString(JSValueConst v) 3821 { 3822 return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; 3823 } 3824 3825 /* JSClass support */ 3826 3827 #ifdef CONFIG_ATOMICS 3828 static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER; 3829 #endif 3830 3831 /* a new class ID is allocated if *pclass_id != 0 */ 3832 JSClassID JS_NewClassID(JSClassID *pclass_id) 3833 { 3834 JSClassID class_id; 3835 #ifdef CONFIG_ATOMICS 3836 pthread_mutex_lock(&js_class_id_mutex); 3837 #endif 3838 class_id = *pclass_id; 3839 if (class_id == 0) { 3840 class_id = js_class_id_alloc++; 3841 *pclass_id = class_id; 3842 } 3843 #ifdef CONFIG_ATOMICS 3844 pthread_mutex_unlock(&js_class_id_mutex); 3845 #endif 3846 return class_id; 3847 } 3848 3849 JSClassID JS_GetClassID(JSValue v) 3850 { 3851 JSObject *p; 3852 if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) 3853 return JS_INVALID_CLASS_ID; 3854 p = JS_VALUE_GET_OBJ(v); 3855 return p->class_id; 3856 } 3857 3858 BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) 3859 { 3860 return (class_id < rt->class_count && 3861 rt->class_array[class_id].class_id != 0); 3862 } 3863 3864 /* create a new object internal class. Return -1 if error, 0 if 3865 OK. The finalizer can be NULL if none is needed. */ 3866 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, 3867 const JSClassDef *class_def, JSAtom name) 3868 { 3869 int new_size, i; 3870 JSClass *cl, *new_class_array; 3871 struct list_head *el; 3872 3873 if (class_id >= (1 << 16)) 3874 return -1; 3875 if (class_id < rt->class_count && 3876 rt->class_array[class_id].class_id != 0) 3877 return -1; 3878 3879 if (class_id >= rt->class_count) { 3880 new_size = max_int(JS_CLASS_INIT_COUNT, 3881 max_int(class_id + 1, rt->class_count * 3 / 2)); 3882 3883 /* reallocate the context class prototype array, if any */ 3884 list_for_each(el, &rt->context_list) { 3885 JSContext *ctx = list_entry(el, JSContext, link); 3886 JSValue *new_tab; 3887 new_tab = js_realloc_rt(rt, ctx->class_proto, 3888 sizeof(ctx->class_proto[0]) * new_size); 3889 if (!new_tab) 3890 return -1; 3891 for(i = rt->class_count; i < new_size; i++) 3892 new_tab[i] = JS_NULL; 3893 ctx->class_proto = new_tab; 3894 } 3895 /* reallocate the class array */ 3896 new_class_array = js_realloc_rt(rt, rt->class_array, 3897 sizeof(JSClass) * new_size); 3898 if (!new_class_array) 3899 return -1; 3900 memset(new_class_array + rt->class_count, 0, 3901 (new_size - rt->class_count) * sizeof(JSClass)); 3902 rt->class_array = new_class_array; 3903 rt->class_count = new_size; 3904 } 3905 cl = &rt->class_array[class_id]; 3906 cl->class_id = class_id; 3907 cl->class_name = JS_DupAtomRT(rt, name); 3908 cl->finalizer = class_def->finalizer; 3909 cl->gc_mark = class_def->gc_mark; 3910 cl->call = class_def->call; 3911 cl->exotic = class_def->exotic; 3912 return 0; 3913 } 3914 3915 int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) 3916 { 3917 int ret, len; 3918 JSAtom name; 3919 3920 len = strlen(class_def->class_name); 3921 name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); 3922 if (name == JS_ATOM_NULL) { 3923 name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); 3924 if (name == JS_ATOM_NULL) 3925 return -1; 3926 } 3927 ret = JS_NewClass1(rt, class_id, class_def, name); 3928 JS_FreeAtomRT(rt, name); 3929 return ret; 3930 } 3931 3932 static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) 3933 { 3934 JSString *str; 3935 3936 if (len <= 0) { 3937 return JS_AtomToString(ctx, JS_ATOM_empty_string); 3938 } 3939 str = js_alloc_string(ctx, len, 0); 3940 if (!str) 3941 return JS_EXCEPTION; 3942 memcpy(str->u.str8, buf, len); 3943 str->u.str8[len] = '\0'; 3944 return JS_MKPTR(JS_TAG_STRING, str); 3945 } 3946 3947 static JSValue js_new_string8(JSContext *ctx, const char *buf) 3948 { 3949 return js_new_string8_len(ctx, buf, strlen(buf)); 3950 } 3951 3952 static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) 3953 { 3954 JSString *str; 3955 str = js_alloc_string(ctx, len, 1); 3956 if (!str) 3957 return JS_EXCEPTION; 3958 memcpy(str->u.str16, buf, len * 2); 3959 return JS_MKPTR(JS_TAG_STRING, str); 3960 } 3961 3962 static JSValue js_new_string_char(JSContext *ctx, uint16_t c) 3963 { 3964 if (c < 0x100) { 3965 uint8_t ch8 = c; 3966 return js_new_string8_len(ctx, (const char *)&ch8, 1); 3967 } else { 3968 uint16_t ch16 = c; 3969 return js_new_string16_len(ctx, &ch16, 1); 3970 } 3971 } 3972 3973 static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) 3974 { 3975 int len = end - start; 3976 if (start == 0 && end == p->len) { 3977 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3978 } 3979 if (p->is_wide_char && len > 0) { 3980 JSString *str; 3981 int i; 3982 uint16_t c = 0; 3983 for (i = start; i < end; i++) { 3984 c |= p->u.str16[i]; 3985 } 3986 if (c > 0xFF) 3987 return js_new_string16_len(ctx, p->u.str16 + start, len); 3988 3989 str = js_alloc_string(ctx, len, 0); 3990 if (!str) 3991 return JS_EXCEPTION; 3992 for (i = 0; i < len; i++) { 3993 str->u.str8[i] = p->u.str16[start + i]; 3994 } 3995 str->u.str8[len] = '\0'; 3996 return JS_MKPTR(JS_TAG_STRING, str); 3997 } else { 3998 return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len); 3999 } 4000 } 4001 4002 typedef struct StringBuffer { 4003 JSContext *ctx; 4004 JSString *str; 4005 int len; 4006 int size; 4007 int is_wide_char; 4008 int error_status; 4009 } StringBuffer; 4010 4011 /* It is valid to call string_buffer_end() and all string_buffer functions even 4012 if string_buffer_init() or another string_buffer function returns an error. 4013 If the error_status is set, string_buffer_end() returns JS_EXCEPTION. 4014 */ 4015 static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, 4016 int is_wide) 4017 { 4018 s->ctx = ctx; 4019 s->size = size; 4020 s->len = 0; 4021 s->is_wide_char = is_wide; 4022 s->error_status = 0; 4023 s->str = js_alloc_string(ctx, size, is_wide); 4024 if (unlikely(!s->str)) { 4025 s->size = 0; 4026 return s->error_status = -1; 4027 } 4028 #ifdef DUMP_LEAKS 4029 /* the StringBuffer may reallocate the JSString, only link it at the end */ 4030 list_del(&s->str->link); 4031 #endif 4032 return 0; 4033 } 4034 4035 static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) 4036 { 4037 return string_buffer_init2(ctx, s, size, 0); 4038 } 4039 4040 static void string_buffer_free(StringBuffer *s) 4041 { 4042 js_free(s->ctx, s->str); 4043 s->str = NULL; 4044 } 4045 4046 static int string_buffer_set_error(StringBuffer *s) 4047 { 4048 js_free(s->ctx, s->str); 4049 s->str = NULL; 4050 s->size = 0; 4051 s->len = 0; 4052 return s->error_status = -1; 4053 } 4054 4055 static no_inline int string_buffer_widen(StringBuffer *s, int size) 4056 { 4057 JSString *str; 4058 size_t slack; 4059 int i; 4060 4061 if (s->error_status) 4062 return -1; 4063 4064 str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); 4065 if (!str) 4066 return string_buffer_set_error(s); 4067 size += slack >> 1; 4068 for(i = s->len; i-- > 0;) { 4069 str->u.str16[i] = str->u.str8[i]; 4070 } 4071 s->is_wide_char = 1; 4072 s->size = size; 4073 s->str = str; 4074 return 0; 4075 } 4076 4077 static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) 4078 { 4079 JSString *new_str; 4080 int new_size; 4081 size_t new_size_bytes, slack; 4082 4083 if (s->error_status) 4084 return -1; 4085 4086 if (new_len > JS_STRING_LEN_MAX) { 4087 JS_ThrowInternalError(s->ctx, "string too long"); 4088 return string_buffer_set_error(s); 4089 } 4090 new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); 4091 if (!s->is_wide_char && c >= 0x100) { 4092 return string_buffer_widen(s, new_size); 4093 } 4094 new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; 4095 new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); 4096 if (!new_str) 4097 return string_buffer_set_error(s); 4098 new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); 4099 s->size = new_size; 4100 s->str = new_str; 4101 return 0; 4102 } 4103 4104 static no_inline int string_buffer_putc16_slow(StringBuffer *s, uint32_t c) 4105 { 4106 if (unlikely(s->len >= s->size)) { 4107 if (string_buffer_realloc(s, s->len + 1, c)) 4108 return -1; 4109 } 4110 if (s->is_wide_char) { 4111 s->str->u.str16[s->len++] = c; 4112 } else if (c < 0x100) { 4113 s->str->u.str8[s->len++] = c; 4114 } else { 4115 if (string_buffer_widen(s, s->size)) 4116 return -1; 4117 s->str->u.str16[s->len++] = c; 4118 } 4119 return 0; 4120 } 4121 4122 /* 0 <= c <= 0xff */ 4123 static int string_buffer_putc8(StringBuffer *s, uint32_t c) 4124 { 4125 if (unlikely(s->len >= s->size)) { 4126 if (string_buffer_realloc(s, s->len + 1, c)) 4127 return -1; 4128 } 4129 if (s->is_wide_char) { 4130 s->str->u.str16[s->len++] = c; 4131 } else { 4132 s->str->u.str8[s->len++] = c; 4133 } 4134 return 0; 4135 } 4136 4137 /* 0 <= c <= 0xffff */ 4138 static int string_buffer_putc16(StringBuffer *s, uint32_t c) 4139 { 4140 if (likely(s->len < s->size)) { 4141 if (s->is_wide_char) { 4142 s->str->u.str16[s->len++] = c; 4143 return 0; 4144 } else if (c < 0x100) { 4145 s->str->u.str8[s->len++] = c; 4146 return 0; 4147 } 4148 } 4149 return string_buffer_putc16_slow(s, c); 4150 } 4151 4152 static int string_buffer_putc_slow(StringBuffer *s, uint32_t c) 4153 { 4154 if (unlikely(c >= 0x10000)) { 4155 /* surrogate pair */ 4156 if (string_buffer_putc16(s, get_hi_surrogate(c))) 4157 return -1; 4158 c = get_lo_surrogate(c); 4159 } 4160 return string_buffer_putc16(s, c); 4161 } 4162 4163 /* 0 <= c <= 0x10ffff */ 4164 static inline int string_buffer_putc(StringBuffer *s, uint32_t c) 4165 { 4166 if (likely(s->len < s->size)) { 4167 if (s->is_wide_char) { 4168 if (c < 0x10000) { 4169 s->str->u.str16[s->len++] = c; 4170 return 0; 4171 } else if (likely((s->len + 1) < s->size)) { 4172 s->str->u.str16[s->len++] = get_hi_surrogate(c); 4173 s->str->u.str16[s->len++] = get_lo_surrogate(c); 4174 return 0; 4175 } 4176 } else if (c < 0x100) { 4177 s->str->u.str8[s->len++] = c; 4178 return 0; 4179 } 4180 } 4181 return string_buffer_putc_slow(s, c); 4182 } 4183 4184 static int string_getc(const JSString *p, int *pidx) 4185 { 4186 int idx, c, c1; 4187 idx = *pidx; 4188 if (p->is_wide_char) { 4189 c = p->u.str16[idx++]; 4190 if (is_hi_surrogate(c) && idx < p->len) { 4191 c1 = p->u.str16[idx]; 4192 if (is_lo_surrogate(c1)) { 4193 c = from_surrogate(c, c1); 4194 idx++; 4195 } 4196 } 4197 } else { 4198 c = p->u.str8[idx++]; 4199 } 4200 *pidx = idx; 4201 return c; 4202 } 4203 4204 static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) 4205 { 4206 int i; 4207 4208 if (s->len + len > s->size) { 4209 if (string_buffer_realloc(s, s->len + len, 0)) 4210 return -1; 4211 } 4212 if (s->is_wide_char) { 4213 for (i = 0; i < len; i++) { 4214 s->str->u.str16[s->len + i] = p[i]; 4215 } 4216 s->len += len; 4217 } else { 4218 memcpy(&s->str->u.str8[s->len], p, len); 4219 s->len += len; 4220 } 4221 return 0; 4222 } 4223 4224 static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) 4225 { 4226 int c = 0, i; 4227 4228 for (i = 0; i < len; i++) { 4229 c |= p[i]; 4230 } 4231 if (s->len + len > s->size) { 4232 if (string_buffer_realloc(s, s->len + len, c)) 4233 return -1; 4234 } else if (!s->is_wide_char && c >= 0x100) { 4235 if (string_buffer_widen(s, s->size)) 4236 return -1; 4237 } 4238 if (s->is_wide_char) { 4239 memcpy(&s->str->u.str16[s->len], p, len << 1); 4240 s->len += len; 4241 } else { 4242 for (i = 0; i < len; i++) { 4243 s->str->u.str8[s->len + i] = p[i]; 4244 } 4245 s->len += len; 4246 } 4247 return 0; 4248 } 4249 4250 /* appending an ASCII string */ 4251 static int string_buffer_puts8(StringBuffer *s, const char *str) 4252 { 4253 return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); 4254 } 4255 4256 static int string_buffer_concat(StringBuffer *s, const JSString *p, 4257 uint32_t from, uint32_t to) 4258 { 4259 if (to <= from) 4260 return 0; 4261 if (p->is_wide_char) 4262 return string_buffer_write16(s, p->u.str16 + from, to - from); 4263 else 4264 return string_buffer_write8(s, p->u.str8 + from, to - from); 4265 } 4266 4267 static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) 4268 { 4269 JSString *p; 4270 JSValue v1; 4271 int res; 4272 4273 if (s->error_status) { 4274 /* prevent exception overload */ 4275 return -1; 4276 } 4277 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { 4278 if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) { 4279 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v); 4280 /* recursion is acceptable because the rope depth is bounded */ 4281 if (string_buffer_concat_value(s, r->left)) 4282 return -1; 4283 return string_buffer_concat_value(s, r->right); 4284 } else { 4285 v1 = JS_ToString(s->ctx, v); 4286 if (JS_IsException(v1)) 4287 return string_buffer_set_error(s); 4288 p = JS_VALUE_GET_STRING(v1); 4289 res = string_buffer_concat(s, p, 0, p->len); 4290 JS_FreeValue(s->ctx, v1); 4291 return res; 4292 } 4293 } 4294 p = JS_VALUE_GET_STRING(v); 4295 return string_buffer_concat(s, p, 0, p->len); 4296 } 4297 4298 static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) 4299 { 4300 JSString *p; 4301 int res; 4302 4303 if (s->error_status) { 4304 /* prevent exception overload */ 4305 JS_FreeValue(s->ctx, v); 4306 return -1; 4307 } 4308 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { 4309 v = JS_ToStringFree(s->ctx, v); 4310 if (JS_IsException(v)) 4311 return string_buffer_set_error(s); 4312 } 4313 p = JS_VALUE_GET_STRING(v); 4314 res = string_buffer_concat(s, p, 0, p->len); 4315 JS_FreeValue(s->ctx, v); 4316 return res; 4317 } 4318 4319 static int string_buffer_fill(StringBuffer *s, int c, int count) 4320 { 4321 /* XXX: optimize */ 4322 if (s->len + count > s->size) { 4323 if (string_buffer_realloc(s, s->len + count, c)) 4324 return -1; 4325 } 4326 while (count-- > 0) { 4327 if (string_buffer_putc16(s, c)) 4328 return -1; 4329 } 4330 return 0; 4331 } 4332 4333 static JSValue string_buffer_end(StringBuffer *s) 4334 { 4335 JSString *str; 4336 str = s->str; 4337 if (s->error_status) 4338 return JS_EXCEPTION; 4339 if (s->len == 0) { 4340 js_free(s->ctx, str); 4341 s->str = NULL; 4342 return JS_AtomToString(s->ctx, JS_ATOM_empty_string); 4343 } 4344 if (s->len < s->size) { 4345 /* smaller size so js_realloc should not fail, but OK if it does */ 4346 /* XXX: should add some slack to avoid unnecessary calls */ 4347 /* XXX: might need to use malloc+free to ensure smaller size */ 4348 str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + 4349 (s->len << s->is_wide_char) + 1 - s->is_wide_char); 4350 if (str == NULL) 4351 str = s->str; 4352 s->str = str; 4353 } 4354 if (!s->is_wide_char) 4355 str->u.str8[s->len] = 0; 4356 #ifdef DUMP_LEAKS 4357 list_add_tail(&str->link, &s->ctx->rt->string_list); 4358 #endif 4359 str->is_wide_char = s->is_wide_char; 4360 str->len = s->len; 4361 s->str = NULL; 4362 return JS_MKPTR(JS_TAG_STRING, str); 4363 } 4364 4365 /* create a string from a UTF-8 buffer */ 4366 JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) 4367 { 4368 const uint8_t *p, *p_end, *p_start, *p_next; 4369 uint32_t c; 4370 StringBuffer b_s, *b = &b_s; 4371 size_t len1; 4372 4373 p_start = (const uint8_t *)buf; 4374 p_end = p_start + buf_len; 4375 len1 = count_ascii(p_start, buf_len); 4376 p = p_start + len1; 4377 if (len1 > JS_STRING_LEN_MAX) 4378 return JS_ThrowInternalError(ctx, "string too long"); 4379 if (p == p_end) { 4380 /* ASCII string */ 4381 return js_new_string8_len(ctx, buf, buf_len); 4382 } else { 4383 if (string_buffer_init(ctx, b, buf_len)) 4384 goto fail; 4385 string_buffer_write8(b, p_start, len1); 4386 while (p < p_end) { 4387 if (*p < 128) { 4388 string_buffer_putc8(b, *p++); 4389 } else { 4390 /* parse utf-8 sequence, return 0xFFFFFFFF for error */ 4391 c = unicode_from_utf8(p, p_end - p, &p_next); 4392 if (c < 0x10000) { 4393 p = p_next; 4394 } else if (c <= 0x10FFFF) { 4395 p = p_next; 4396 /* surrogate pair */ 4397 string_buffer_putc16(b, get_hi_surrogate(c)); 4398 c = get_lo_surrogate(c); 4399 } else { 4400 /* invalid char */ 4401 c = 0xfffd; 4402 /* skip the invalid chars */ 4403 /* XXX: seems incorrect. Why not just use c = *p++; ? */ 4404 while (p < p_end && (*p >= 0x80 && *p < 0xc0)) 4405 p++; 4406 if (p < p_end) { 4407 p++; 4408 while (p < p_end && (*p >= 0x80 && *p < 0xc0)) 4409 p++; 4410 } 4411 } 4412 string_buffer_putc16(b, c); 4413 } 4414 } 4415 } 4416 return string_buffer_end(b); 4417 4418 fail: 4419 string_buffer_free(b); 4420 return JS_EXCEPTION; 4421 } 4422 4423 static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, 4424 JSValue str2, const char *str3) 4425 { 4426 StringBuffer b_s, *b = &b_s; 4427 int len1, len3; 4428 JSString *p; 4429 4430 if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { 4431 str2 = JS_ToStringFree(ctx, str2); 4432 if (JS_IsException(str2)) 4433 goto fail; 4434 } 4435 p = JS_VALUE_GET_STRING(str2); 4436 len1 = strlen(str1); 4437 len3 = strlen(str3); 4438 4439 if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) 4440 goto fail; 4441 4442 string_buffer_write8(b, (const uint8_t *)str1, len1); 4443 string_buffer_concat(b, p, 0, p->len); 4444 string_buffer_write8(b, (const uint8_t *)str3, len3); 4445 4446 JS_FreeValue(ctx, str2); 4447 return string_buffer_end(b); 4448 4449 fail: 4450 JS_FreeValue(ctx, str2); 4451 return JS_EXCEPTION; 4452 } 4453 4454 JSValue JS_NewAtomString(JSContext *ctx, const char *str) 4455 { 4456 JSAtom atom = JS_NewAtom(ctx, str); 4457 if (atom == JS_ATOM_NULL) 4458 return JS_EXCEPTION; 4459 JSValue val = JS_AtomToString(ctx, atom); 4460 JS_FreeAtom(ctx, atom); 4461 return val; 4462 } 4463 4464 /* return (NULL, 0) if exception. */ 4465 /* return pointer into a JSString with a live ref_count */ 4466 /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ 4467 const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8) 4468 { 4469 JSValue val; 4470 JSString *str, *str_new; 4471 int pos, len, c, c1; 4472 uint8_t *q; 4473 4474 if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { 4475 val = JS_ToString(ctx, val1); 4476 if (JS_IsException(val)) 4477 goto fail; 4478 } else { 4479 val = JS_DupValue(ctx, val1); 4480 } 4481 4482 str = JS_VALUE_GET_STRING(val); 4483 len = str->len; 4484 if (!str->is_wide_char) { 4485 const uint8_t *src = str->u.str8; 4486 int count; 4487 4488 /* count the number of non-ASCII characters */ 4489 /* Scanning the whole string is required for ASCII strings, 4490 and computing the number of non-ASCII bytes is less expensive 4491 than testing each byte, hence this method is faster for ASCII 4492 strings, which is the most common case. 4493 */ 4494 count = 0; 4495 for (pos = 0; pos < len; pos++) { 4496 count += src[pos] >> 7; 4497 } 4498 if (count == 0) { 4499 if (plen) 4500 *plen = len; 4501 return (const char *)src; 4502 } 4503 str_new = js_alloc_string(ctx, len + count, 0); 4504 if (!str_new) 4505 goto fail; 4506 q = str_new->u.str8; 4507 for (pos = 0; pos < len; pos++) { 4508 c = src[pos]; 4509 if (c < 0x80) { 4510 *q++ = c; 4511 } else { 4512 *q++ = (c >> 6) | 0xc0; 4513 *q++ = (c & 0x3f) | 0x80; 4514 } 4515 } 4516 } else { 4517 const uint16_t *src = str->u.str16; 4518 /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may 4519 produce 4 bytes but use 2 code points. 4520 */ 4521 str_new = js_alloc_string(ctx, len * 3, 0); 4522 if (!str_new) 4523 goto fail; 4524 q = str_new->u.str8; 4525 pos = 0; 4526 while (pos < len) { 4527 c = src[pos++]; 4528 if (c < 0x80) { 4529 *q++ = c; 4530 } else { 4531 if (is_hi_surrogate(c)) { 4532 if (pos < len && !cesu8) { 4533 c1 = src[pos]; 4534 if (is_lo_surrogate(c1)) { 4535 pos++; 4536 c = from_surrogate(c, c1); 4537 } else { 4538 /* Keep unmatched surrogate code points */ 4539 /* c = 0xfffd; */ /* error */ 4540 } 4541 } else { 4542 /* Keep unmatched surrogate code points */ 4543 /* c = 0xfffd; */ /* error */ 4544 } 4545 } 4546 q += unicode_to_utf8(q, c); 4547 } 4548 } 4549 } 4550 4551 *q = '\0'; 4552 str_new->len = q - str_new->u.str8; 4553 JS_FreeValue(ctx, val); 4554 if (plen) 4555 *plen = str_new->len; 4556 return (const char *)str_new->u.str8; 4557 fail: 4558 if (plen) 4559 *plen = 0; 4560 return NULL; 4561 } 4562 4563 void JS_FreeCString(JSContext *ctx, const char *ptr) 4564 { 4565 JSString *p; 4566 if (!ptr) 4567 return; 4568 /* purposely removing constness */ 4569 p = container_of(ptr, JSString, u); 4570 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 4571 } 4572 4573 static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) 4574 { 4575 int c, i; 4576 for(i = 0; i < len; i++) { 4577 c = src1[i] - src2[i]; 4578 if (c != 0) 4579 return c; 4580 } 4581 return 0; 4582 } 4583 4584 static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) 4585 { 4586 int c, i; 4587 for(i = 0; i < len; i++) { 4588 c = src1[i] - src2[i]; 4589 if (c != 0) 4590 return c; 4591 } 4592 return 0; 4593 } 4594 4595 static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2, 4596 int pos2, int len) 4597 { 4598 int res; 4599 4600 if (likely(!p1->is_wide_char)) { 4601 if (likely(!p2->is_wide_char)) 4602 res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len); 4603 else 4604 res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len); 4605 } else { 4606 if (!p2->is_wide_char) 4607 res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len); 4608 else 4609 res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len); 4610 } 4611 return res; 4612 } 4613 4614 static BOOL js_string_eq(JSContext *ctx, 4615 const JSString *p1, const JSString *p2) 4616 { 4617 if (p1->len != p2->len) 4618 return FALSE; 4619 if (p1 == p2) 4620 return TRUE; 4621 return js_string_memcmp(p1, 0, p2, 0, p1->len) == 0; 4622 } 4623 4624 /* return < 0, 0 or > 0 */ 4625 static int js_string_compare(JSContext *ctx, 4626 const JSString *p1, const JSString *p2) 4627 { 4628 int res, len; 4629 len = min_int(p1->len, p2->len); 4630 res = js_string_memcmp(p1, 0, p2, 0, len); 4631 if (res == 0) { 4632 if (p1->len == p2->len) 4633 res = 0; 4634 else if (p1->len < p2->len) 4635 res = -1; 4636 else 4637 res = 1; 4638 } 4639 return res; 4640 } 4641 4642 static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) 4643 { 4644 if (p->is_wide_char) { 4645 memcpy(dst, p->u.str16 + offset, len * 2); 4646 } else { 4647 const uint8_t *src1 = p->u.str8 + offset; 4648 int i; 4649 4650 for(i = 0; i < len; i++) 4651 dst[i] = src1[i]; 4652 } 4653 } 4654 4655 static JSValue JS_ConcatString1(JSContext *ctx, 4656 const JSString *p1, const JSString *p2) 4657 { 4658 JSString *p; 4659 uint32_t len; 4660 int is_wide_char; 4661 4662 len = p1->len + p2->len; 4663 if (len > JS_STRING_LEN_MAX) 4664 return JS_ThrowInternalError(ctx, "string too long"); 4665 is_wide_char = p1->is_wide_char | p2->is_wide_char; 4666 p = js_alloc_string(ctx, len, is_wide_char); 4667 if (!p) 4668 return JS_EXCEPTION; 4669 if (!is_wide_char) { 4670 memcpy(p->u.str8, p1->u.str8, p1->len); 4671 memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); 4672 p->u.str8[len] = '\0'; 4673 } else { 4674 copy_str16(p->u.str16, p1, 0, p1->len); 4675 copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); 4676 } 4677 return JS_MKPTR(JS_TAG_STRING, p); 4678 } 4679 4680 static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { 4681 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { 4682 JSString *p2 = JS_VALUE_GET_STRING(op2); 4683 size_t size1; 4684 4685 if (p2->len == 0) 4686 return TRUE; 4687 if (js_rc(p1)->ref_count != 1) 4688 return FALSE; 4689 size1 = js_malloc_usable_size(ctx, p1); 4690 if (p1->is_wide_char) { 4691 if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { 4692 if (p2->is_wide_char) { 4693 memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); 4694 p1->len += p2->len; 4695 return TRUE; 4696 } else { 4697 size_t i; 4698 for (i = 0; i < p2->len; i++) { 4699 p1->u.str16[p1->len++] = p2->u.str8[i]; 4700 } 4701 return TRUE; 4702 } 4703 } 4704 } else if (!p2->is_wide_char) { 4705 if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { 4706 memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); 4707 p1->len += p2->len; 4708 p1->u.str8[p1->len] = '\0'; 4709 return TRUE; 4710 } 4711 } 4712 } 4713 return FALSE; 4714 } 4715 4716 static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2) 4717 { 4718 JSValue ret; 4719 JSString *p1, *p2; 4720 p1 = JS_VALUE_GET_STRING(op1); 4721 if (JS_ConcatStringInPlace(ctx, p1, op2)) { 4722 JS_FreeValue(ctx, op2); 4723 return op1; 4724 } 4725 p2 = JS_VALUE_GET_STRING(op2); 4726 ret = JS_ConcatString1(ctx, p1, p2); 4727 JS_FreeValue(ctx, op1); 4728 JS_FreeValue(ctx, op2); 4729 return ret; 4730 } 4731 4732 /* Return the character at position 'idx'. 'val' must be a string or rope */ 4733 static int string_rope_get(JSValueConst val, uint32_t idx) 4734 { 4735 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 4736 return string_get(JS_VALUE_GET_STRING(val), idx); 4737 } else { 4738 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); 4739 uint32_t len; 4740 if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING) 4741 len = JS_VALUE_GET_STRING(r->left)->len; 4742 else 4743 len = JS_VALUE_GET_STRING_ROPE(r->left)->len; 4744 if (idx < len) 4745 return string_rope_get(r->left, idx); 4746 else 4747 return string_rope_get(r->right, idx - len); 4748 } 4749 } 4750 4751 typedef struct { 4752 JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH]; 4753 int stack_len; 4754 } JSStringRopeIter; 4755 4756 static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val) 4757 { 4758 s->stack_len = 0; 4759 s->stack[s->stack_len++] = val; 4760 } 4761 4762 /* iterate thru a rope and return the strings in order */ 4763 static JSString *string_rope_iter_next(JSStringRopeIter *s) 4764 { 4765 JSValueConst val; 4766 JSStringRope *r; 4767 4768 if (s->stack_len == 0) 4769 return NULL; 4770 val = s->stack[--s->stack_len]; 4771 for(;;) { 4772 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) 4773 return JS_VALUE_GET_STRING(val); 4774 r = JS_VALUE_GET_STRING_ROPE(val); 4775 assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH); 4776 s->stack[s->stack_len++] = r->right; 4777 val = r->left; 4778 } 4779 } 4780 4781 static uint32_t string_rope_get_len(JSValueConst val) 4782 { 4783 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) 4784 return JS_VALUE_GET_STRING(val)->len; 4785 else 4786 return JS_VALUE_GET_STRING_ROPE(val)->len; 4787 } 4788 4789 static int js_string_rope_compare(JSContext *ctx, JSValueConst op1, 4790 JSValueConst op2, BOOL eq_only) 4791 { 4792 uint32_t len1, len2, len, pos1, pos2, l; 4793 int res; 4794 JSStringRopeIter it1, it2; 4795 JSString *p1, *p2; 4796 4797 len1 = string_rope_get_len(op1); 4798 len2 = string_rope_get_len(op2); 4799 /* no need to go further for equality test if 4800 different length */ 4801 if (eq_only && len1 != len2) 4802 return 1; 4803 len = min_uint32(len1, len2); 4804 string_rope_iter_init(&it1, op1); 4805 string_rope_iter_init(&it2, op2); 4806 p1 = string_rope_iter_next(&it1); 4807 p2 = string_rope_iter_next(&it2); 4808 pos1 = 0; 4809 pos2 = 0; 4810 while (len != 0) { 4811 l = min_uint32(p1->len - pos1, p2->len - pos2); 4812 l = min_uint32(l, len); 4813 res = js_string_memcmp(p1, pos1, p2, pos2, l); 4814 if (res != 0) 4815 return res; 4816 len -= l; 4817 pos1 += l; 4818 if (pos1 >= p1->len) { 4819 p1 = string_rope_iter_next(&it1); 4820 pos1 = 0; 4821 } 4822 pos2 += l; 4823 if (pos2 >= p2->len) { 4824 p2 = string_rope_iter_next(&it2); 4825 pos2 = 0; 4826 } 4827 } 4828 4829 if (len1 == len2) 4830 res = 0; 4831 else if (len1 < len2) 4832 res = -1; 4833 else 4834 res = 1; 4835 return res; 4836 } 4837 4838 /* 'rope' must be a rope. return a string and modify the rope so that 4839 it won't need to be linearized again. */ 4840 static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope) 4841 { 4842 StringBuffer b_s, *b = &b_s; 4843 JSStringRope *r; 4844 JSValue ret; 4845 4846 r = JS_VALUE_GET_STRING_ROPE(rope); 4847 4848 /* check whether it is already linearized */ 4849 if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING && 4850 JS_VALUE_GET_STRING(r->right)->len == 0) { 4851 ret = JS_DupValue(ctx, r->left); 4852 JS_FreeValue(ctx, rope); 4853 return ret; 4854 } 4855 if (string_buffer_init2(ctx, b, r->len, r->is_wide_char)) 4856 goto fail; 4857 if (string_buffer_concat_value(b, rope)) 4858 goto fail; 4859 ret = string_buffer_end(b); 4860 if (js_rc(r)->ref_count > 1) { 4861 /* update the rope so that it won't need to be linearized again */ 4862 JS_FreeValue(ctx, r->left); 4863 JS_FreeValue(ctx, r->right); 4864 r->left = JS_DupValue(ctx, ret); 4865 r->right = JS_AtomToString(ctx, JS_ATOM_empty_string); 4866 } 4867 JS_FreeValue(ctx, rope); 4868 return ret; 4869 fail: 4870 JS_FreeValue(ctx, rope); 4871 return JS_EXCEPTION; 4872 } 4873 4874 static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope); 4875 4876 /* op1 and op2 must be strings or string ropes */ 4877 static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2) 4878 { 4879 uint32_t len; 4880 int is_wide_char, depth; 4881 JSStringRope *r; 4882 JSValue res; 4883 4884 if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { 4885 JSString *p1 = JS_VALUE_GET_STRING(op1); 4886 len = p1->len; 4887 is_wide_char = p1->is_wide_char; 4888 depth = 0; 4889 } else { 4890 JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1); 4891 len = r1->len; 4892 is_wide_char = r1->is_wide_char; 4893 depth = r1->depth; 4894 } 4895 4896 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { 4897 JSString *p2 = JS_VALUE_GET_STRING(op2); 4898 len += p2->len; 4899 is_wide_char |= p2->is_wide_char; 4900 } else { 4901 JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2); 4902 len += r2->len; 4903 is_wide_char |= r2->is_wide_char; 4904 depth = max_int(depth, r2->depth); 4905 } 4906 if (len > JS_STRING_LEN_MAX) { 4907 JS_ThrowInternalError(ctx, "string too long"); 4908 goto fail; 4909 } 4910 r = js_malloc(ctx, sizeof(*r)); 4911 if (!r) 4912 goto fail; 4913 js_rc(r)->ref_count = 1; 4914 r->len = len; 4915 r->is_wide_char = is_wide_char; 4916 r->depth = depth + 1; 4917 r->left = op1; 4918 r->right = op2; 4919 res = JS_MKPTR(JS_TAG_STRING_ROPE, r); 4920 if (r->depth > JS_STRING_ROPE_MAX_DEPTH) { 4921 JSValue res2; 4922 #ifdef DUMP_ROPE_REBALANCE 4923 printf("rebalance: initial depth=%d\n", r->depth); 4924 #endif 4925 res2 = js_rebalancee_string_rope(ctx, res); 4926 #ifdef DUMP_ROPE_REBALANCE 4927 if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE) 4928 printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth); 4929 #endif 4930 JS_FreeValue(ctx, res); 4931 return res2; 4932 } else { 4933 return res; 4934 } 4935 fail: 4936 JS_FreeValue(ctx, op1); 4937 JS_FreeValue(ctx, op2); 4938 return JS_EXCEPTION; 4939 } 4940 4941 #define ROPE_N_BUCKETS 44 4942 4943 /* Fibonacii numbers starting from F_2 */ 4944 static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = { 4945 1, 2, 3, 5, 4946 8, 13, 21, 34, 4947 55, 89, 144, 233, 4948 377, 610, 987, 1597, 4949 2584, 4181, 6765, 10946, 4950 17711, 28657, 46368, 75025, 4951 121393, 196418, 317811, 514229, 4952 832040, 1346269, 2178309, 3524578, 4953 5702887, 9227465, 14930352, 24157817, 4954 39088169, 63245986, 102334155, 165580141, 4955 267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */ 4956 }; 4957 4958 static int js_rebalancee_string_rope_rec(JSContext *ctx, JSValue *buckets, 4959 JSValueConst val) 4960 { 4961 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 4962 JSString *p = JS_VALUE_GET_STRING(val); 4963 uint32_t len, i; 4964 JSValue a, b; 4965 4966 len = p->len; 4967 if (len == 0) 4968 return 0; /* nothing to do */ 4969 /* find the bucket i so that rope_bucket_len[i] <= len < 4970 rope_bucket_len[i + 1] and concatenate the ropes in the 4971 buckets before */ 4972 a = JS_NULL; 4973 i = 0; 4974 while (len >= rope_bucket_len[i + 1]) { 4975 b = buckets[i]; 4976 if (!JS_IsNull(b)) { 4977 buckets[i] = JS_NULL; 4978 if (JS_IsNull(a)) { 4979 a = b; 4980 } else { 4981 a = js_new_string_rope(ctx, b, a); 4982 if (JS_IsException(a)) 4983 return -1; 4984 } 4985 } 4986 i++; 4987 } 4988 if (!JS_IsNull(a)) { 4989 a = js_new_string_rope(ctx, a, JS_DupValue(ctx, val)); 4990 if (JS_IsException(a)) 4991 return -1; 4992 } else { 4993 a = JS_DupValue(ctx, val); 4994 } 4995 while (!JS_IsNull(buckets[i])) { 4996 a = js_new_string_rope(ctx, buckets[i], a); 4997 buckets[i] = JS_NULL; 4998 if (JS_IsException(a)) 4999 return -1; 5000 i++; 5001 } 5002 buckets[i] = a; 5003 } else { 5004 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); 5005 js_rebalancee_string_rope_rec(ctx, buckets, r->left); 5006 js_rebalancee_string_rope_rec(ctx, buckets, r->right); 5007 } 5008 return 0; 5009 } 5010 5011 /* Return a new rope which is balanced. Algorithm from "Ropes: an 5012 Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael 5013 Plass. */ 5014 static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope) 5015 { 5016 JSValue buckets[ROPE_N_BUCKETS], a, b; 5017 int i; 5018 5019 for(i = 0; i < ROPE_N_BUCKETS; i++) 5020 buckets[i] = JS_NULL; 5021 if (js_rebalancee_string_rope_rec(ctx, buckets, rope)) 5022 goto fail; 5023 a = JS_NULL; 5024 for(i = 0; i < ROPE_N_BUCKETS; i++) { 5025 b = buckets[i]; 5026 if (!JS_IsNull(b)) { 5027 buckets[i] = JS_NULL; 5028 if (JS_IsNull(a)) { 5029 a = b; 5030 } else { 5031 a = js_new_string_rope(ctx, b, a); 5032 if (JS_IsException(a)) 5033 goto fail; 5034 } 5035 } 5036 } 5037 /* fail safe */ 5038 if (JS_IsNull(a)) 5039 return JS_AtomToString(ctx, JS_ATOM_empty_string); 5040 else 5041 return a; 5042 fail: 5043 for(i = 0; i < ROPE_N_BUCKETS; i++) { 5044 JS_FreeValue(ctx, buckets[i]); 5045 } 5046 return JS_EXCEPTION; 5047 } 5048 5049 /* op1 and op2 are converted to strings. For convenience, op1 or op2 = 5050 JS_EXCEPTION are accepted and return JS_EXCEPTION. */ 5051 static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) 5052 { 5053 JSString *p1, *p2; 5054 5055 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING && 5056 JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) { 5057 op1 = JS_ToStringFree(ctx, op1); 5058 if (JS_IsException(op1)) { 5059 JS_FreeValue(ctx, op2); 5060 return JS_EXCEPTION; 5061 } 5062 } 5063 if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING && 5064 JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) { 5065 op2 = JS_ToStringFree(ctx, op2); 5066 if (JS_IsException(op2)) { 5067 JS_FreeValue(ctx, op1); 5068 return JS_EXCEPTION; 5069 } 5070 } 5071 5072 /* normal concatenation for short strings */ 5073 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { 5074 p2 = JS_VALUE_GET_STRING(op2); 5075 if (p2->len == 0) { 5076 JS_FreeValue(ctx, op2); 5077 return op1; 5078 } 5079 if (p2->len <= JS_STRING_ROPE_SHORT_LEN) { 5080 if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { 5081 p1 = JS_VALUE_GET_STRING(op1); 5082 if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) { 5083 return JS_ConcatString2(ctx, op1, op2); 5084 } else { 5085 return js_new_string_rope(ctx, op1, op2); 5086 } 5087 } else { 5088 JSStringRope *r1; 5089 r1 = JS_VALUE_GET_STRING_ROPE(op1); 5090 if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING && 5091 JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) { 5092 JSValue val, ret; 5093 val = JS_ConcatString2(ctx, JS_DupValue(ctx, r1->right), op2); 5094 if (JS_IsException(val)) { 5095 JS_FreeValue(ctx, op1); 5096 return JS_EXCEPTION; 5097 } 5098 ret = js_new_string_rope(ctx, JS_DupValue(ctx, r1->left), val); 5099 JS_FreeValue(ctx, op1); 5100 return ret; 5101 } 5102 } 5103 } 5104 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) { 5105 JSStringRope *r2; 5106 p1 = JS_VALUE_GET_STRING(op1); 5107 if (p1->len == 0) { 5108 JS_FreeValue(ctx, op1); 5109 return op2; 5110 } 5111 r2 = JS_VALUE_GET_STRING_ROPE(op2); 5112 if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING && 5113 JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) { 5114 JSValue val, ret; 5115 val = JS_ConcatString2(ctx, op1, JS_DupValue(ctx, r2->left)); 5116 if (JS_IsException(val)) { 5117 JS_FreeValue(ctx, op2); 5118 return JS_EXCEPTION; 5119 } 5120 ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, r2->right)); 5121 JS_FreeValue(ctx, op2); 5122 return ret; 5123 } 5124 } 5125 return js_new_string_rope(ctx, op1, op2); 5126 } 5127 5128 /* Shape support */ 5129 5130 static inline size_t get_shape_size(size_t hash_size, size_t prop_size) 5131 { 5132 return sizeof(JSShape) + hash_size * sizeof(uint32_t) + 5133 prop_size * sizeof(JSShapeProperty); 5134 } 5135 5136 static inline JSShapeProperty *get_shape_prop(JSShape *sh) 5137 { 5138 return (JSShapeProperty *)((uint32_t *)(sh + 1) + sh->prop_hash_mask + 1); 5139 } 5140 5141 static int init_shape_hash(JSRuntime *rt) 5142 { 5143 rt->shape_hash_bits = 4; /* 16 shapes */ 5144 rt->shape_hash_size = 1 << rt->shape_hash_bits; 5145 rt->shape_hash_count = 0; 5146 rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * 5147 rt->shape_hash_size); 5148 if (!rt->shape_hash) 5149 return -1; 5150 return 0; 5151 } 5152 5153 /* same magic hash multiplier as the Linux kernel */ 5154 static uint32_t shape_hash(uint32_t h, uint32_t val) 5155 { 5156 return (h + val) * 0x9e370001; 5157 } 5158 5159 /* truncate the shape hash to 'hash_bits' bits */ 5160 static uint32_t get_shape_hash(uint32_t h, int hash_bits) 5161 { 5162 return h >> (32 - hash_bits); 5163 } 5164 5165 static uint32_t shape_initial_hash(JSObject *proto) 5166 { 5167 uint32_t h; 5168 h = shape_hash(1, (uintptr_t)proto); 5169 if (sizeof(proto) > 4) 5170 h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); 5171 return h; 5172 } 5173 5174 static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) 5175 { 5176 int new_shape_hash_size, i; 5177 uint32_t h; 5178 JSShape **new_shape_hash, *sh, *sh_next; 5179 5180 new_shape_hash_size = 1 << new_shape_hash_bits; 5181 new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * 5182 new_shape_hash_size); 5183 if (!new_shape_hash) 5184 return -1; 5185 for(i = 0; i < rt->shape_hash_size; i++) { 5186 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { 5187 sh_next = sh->shape_hash_next; 5188 h = get_shape_hash(sh->hash, new_shape_hash_bits); 5189 sh->shape_hash_next = new_shape_hash[h]; 5190 new_shape_hash[h] = sh; 5191 } 5192 } 5193 js_free_rt(rt, rt->shape_hash); 5194 rt->shape_hash_bits = new_shape_hash_bits; 5195 rt->shape_hash_size = new_shape_hash_size; 5196 rt->shape_hash = new_shape_hash; 5197 return 0; 5198 } 5199 5200 static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) 5201 { 5202 uint32_t h; 5203 h = get_shape_hash(sh->hash, rt->shape_hash_bits); 5204 sh->shape_hash_next = rt->shape_hash[h]; 5205 rt->shape_hash[h] = sh; 5206 rt->shape_hash_count++; 5207 } 5208 5209 static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) 5210 { 5211 uint32_t h; 5212 JSShape **psh; 5213 5214 h = get_shape_hash(sh->hash, rt->shape_hash_bits); 5215 psh = &rt->shape_hash[h]; 5216 while (*psh != sh) 5217 psh = &(*psh)->shape_hash_next; 5218 *psh = sh->shape_hash_next; 5219 rt->shape_hash_count--; 5220 } 5221 5222 /* create a new empty shape with prototype 'proto'. It is not hashed */ 5223 static inline JSShape *js_new_shape_nohash(JSContext *ctx, JSObject *proto, 5224 int hash_size, int prop_size) 5225 { 5226 JSRuntime *rt = ctx->rt; 5227 JSShape *sh; 5228 5229 sh = js_malloc(ctx, get_shape_size(hash_size, prop_size)); 5230 if (!sh) 5231 return NULL; 5232 js_rc(sh)->ref_count = 1; 5233 add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 5234 if (proto) 5235 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); 5236 sh->proto = proto; 5237 memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * hash_size); 5238 sh->prop_hash_mask = hash_size - 1; 5239 sh->prop_size = prop_size; 5240 sh->prop_count = 0; 5241 sh->deleted_prop_count = 0; 5242 sh->is_hashed = FALSE; 5243 return sh; 5244 } 5245 5246 /* create a new empty shape with prototype 'proto' */ 5247 static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, 5248 int hash_size, int prop_size) 5249 { 5250 JSRuntime *rt = ctx->rt; 5251 JSShape *sh; 5252 5253 /* resize the shape hash table if necessary */ 5254 if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { 5255 resize_shape_hash(rt, rt->shape_hash_bits + 1); 5256 } 5257 5258 sh = js_new_shape_nohash(ctx, proto, hash_size, prop_size); 5259 if (!sh) 5260 return NULL; 5261 5262 /* insert in the hash table */ 5263 sh->hash = shape_initial_hash(proto); 5264 sh->is_hashed = TRUE; 5265 js_shape_hash_link(ctx->rt, sh); 5266 return sh; 5267 } 5268 5269 static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) 5270 { 5271 return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, 5272 JS_PROP_INITIAL_SIZE); 5273 } 5274 5275 /* The shape is cloned. The new shape is not inserted in the shape 5276 hash table */ 5277 static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) 5278 { 5279 JSShape *sh; 5280 size_t size; 5281 JSShapeProperty *pr; 5282 uint32_t i, hash_size; 5283 5284 hash_size = sh1->prop_hash_mask + 1; 5285 size = get_shape_size(hash_size, sh1->prop_size); 5286 sh = js_malloc(ctx, size); 5287 if (!sh) 5288 return NULL; 5289 memcpy(&sh->header + 1, &sh1->header + 1, 5290 size - sizeof(JSGCObjectHeader)); 5291 js_rc(sh)->ref_count = 1; 5292 add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 5293 sh->is_hashed = FALSE; 5294 if (sh->proto) { 5295 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 5296 } 5297 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { 5298 JS_DupAtom(ctx, pr->atom); 5299 } 5300 return sh; 5301 } 5302 5303 static JSShape *js_dup_shape(JSShape *sh) 5304 { 5305 js_rc(sh)->ref_count++; 5306 return sh; 5307 } 5308 5309 static void js_free_shape0(JSRuntime *rt, JSShape *sh) 5310 { 5311 uint32_t i; 5312 JSShapeProperty *pr; 5313 5314 assert(js_rc(sh)->ref_count == 0); 5315 if (sh->is_hashed) 5316 js_shape_hash_unlink(rt, sh); 5317 if (sh->proto != NULL) { 5318 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 5319 } 5320 pr = get_shape_prop(sh); 5321 for(i = 0; i < sh->prop_count; i++) { 5322 JS_FreeAtomRT(rt, pr->atom); 5323 pr++; 5324 } 5325 remove_gc_object(&sh->header); 5326 js_free_rt(rt, sh); 5327 } 5328 5329 static void js_free_shape(JSRuntime *rt, JSShape *sh) 5330 { 5331 if (unlikely(--js_rc(sh)->ref_count <= 0)) { 5332 js_free_shape0(rt, sh); 5333 } 5334 } 5335 5336 static void js_free_shape_null(JSRuntime *rt, JSShape *sh) 5337 { 5338 if (sh) 5339 js_free_shape(rt, sh); 5340 } 5341 5342 /* make space to hold at least 'count' properties */ 5343 static no_inline int resize_properties(JSContext *ctx, JSShape **psh, 5344 JSObject *p, uint32_t count) 5345 { 5346 JSShape *sh; 5347 uint32_t new_size, new_hash_size, new_hash_mask, i; 5348 JSShapeProperty *pr; 5349 intptr_t h; 5350 JSShape *old_sh; 5351 5352 sh = *psh; 5353 new_size = max_int(count, sh->prop_size * 3 / 2); 5354 /* Reallocate prop array first to avoid crash or size inconsistency 5355 in case of memory allocation failure */ 5356 if (p) { 5357 JSProperty *new_prop; 5358 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); 5359 if (unlikely(!new_prop)) 5360 return -1; 5361 p->prop = new_prop; 5362 } 5363 new_hash_size = sh->prop_hash_mask + 1; 5364 while (new_hash_size < new_size) 5365 new_hash_size = 2 * new_hash_size; 5366 /* resize the property shapes. Using js_realloc() is not possible in 5367 case the GC runs during the allocation */ 5368 old_sh = sh; 5369 sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); 5370 if (!sh) 5371 return -1; 5372 remove_gc_object(&old_sh->header); 5373 5374 js_rc(sh)->ref_count = 1; 5375 add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 5376 5377 memcpy(&sh->header + 1, &old_sh->header + 1, 5378 sizeof(JSShape) - sizeof(JSGCObjectHeader)); 5379 5380 if (new_hash_size != (sh->prop_hash_mask + 1)) { 5381 /* resize the hash table and the properties */ 5382 new_hash_mask = new_hash_size - 1; 5383 sh->prop_hash_mask = new_hash_mask; 5384 memset(sh->hash_table, 0, 5385 sizeof(sh->hash_table[0]) * new_hash_size); 5386 memcpy(get_shape_prop(sh), get_shape_prop(old_sh), 5387 sizeof(JSShapeProperty) * old_sh->prop_count); 5388 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { 5389 if (pr->atom != JS_ATOM_NULL) { 5390 h = ((uintptr_t)pr->atom & new_hash_mask); 5391 pr->hash_next = sh->hash_table[h]; 5392 sh->hash_table[h] = i + 1; 5393 } 5394 } 5395 } else { 5396 /* just copy the previous hash table and the properties */ 5397 memcpy(sh->hash_table, old_sh->hash_table, 5398 sizeof(sh->hash_table[0]) * new_hash_size); 5399 5400 memcpy(get_shape_prop(sh), get_shape_prop(old_sh), 5401 sizeof(JSShapeProperty) * old_sh->prop_count); 5402 } 5403 js_free(ctx, old_sh); 5404 *psh = sh; 5405 sh->prop_size = new_size; 5406 return 0; 5407 } 5408 5409 /* remove the deleted properties. */ 5410 static int compact_properties(JSContext *ctx, JSObject *p) 5411 { 5412 JSShape *sh, *old_sh; 5413 intptr_t h; 5414 uint32_t new_hash_size, i, j, new_hash_mask, new_size; 5415 JSShapeProperty *old_pr, *pr; 5416 JSProperty *prop, *new_prop; 5417 5418 sh = p->shape; 5419 assert(!sh->is_hashed); 5420 5421 new_size = max_int(JS_PROP_INITIAL_SIZE, 5422 sh->prop_count - sh->deleted_prop_count); 5423 assert(new_size <= sh->prop_size); 5424 5425 new_hash_size = sh->prop_hash_mask + 1; 5426 while ((new_hash_size / 2) >= new_size) 5427 new_hash_size = new_hash_size / 2; 5428 new_hash_mask = new_hash_size - 1; 5429 5430 /* resize the hash table and the properties */ 5431 old_sh = sh; 5432 sh = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); 5433 if (!sh) 5434 return -1; 5435 remove_gc_object(&old_sh->header); 5436 5437 js_rc(sh)->ref_count = 1; 5438 add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 5439 5440 memcpy(&sh->header + 1, &old_sh->header + 1, 5441 sizeof(JSShape) - sizeof(JSGCObjectHeader)); 5442 5443 memset(sh->hash_table, 0, sizeof(sh->hash_table[0]) * new_hash_size); 5444 sh->prop_hash_mask = new_hash_mask; 5445 5446 j = 0; 5447 old_pr = get_shape_prop(old_sh); 5448 pr = get_shape_prop(sh); 5449 prop = p->prop; 5450 for(i = 0; i < sh->prop_count; i++) { 5451 if (old_pr->atom != JS_ATOM_NULL) { 5452 pr->atom = old_pr->atom; 5453 pr->flags = old_pr->flags; 5454 h = ((uintptr_t)old_pr->atom & new_hash_mask); 5455 pr->hash_next = sh->hash_table[h]; 5456 sh->hash_table[h] = j + 1; 5457 prop[j] = prop[i]; 5458 j++; 5459 pr++; 5460 } 5461 old_pr++; 5462 } 5463 assert(j == (sh->prop_count - sh->deleted_prop_count)); 5464 sh->prop_size = new_size; 5465 sh->deleted_prop_count = 0; 5466 sh->prop_count = j; 5467 5468 p->shape = sh; 5469 js_free(ctx, old_sh); 5470 5471 /* reduce the size of the object properties */ 5472 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); 5473 if (new_prop) 5474 p->prop = new_prop; 5475 return 0; 5476 } 5477 5478 static int add_shape_property(JSContext *ctx, JSShape **psh, 5479 JSObject *p, JSAtom atom, int prop_flags) 5480 { 5481 JSRuntime *rt = ctx->rt; 5482 JSShape *sh = *psh; 5483 JSShapeProperty *pr, *prop; 5484 uint32_t hash_mask, new_shape_hash = 0; 5485 intptr_t h; 5486 5487 /* update the shape hash */ 5488 if (sh->is_hashed) { 5489 js_shape_hash_unlink(rt, sh); 5490 new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); 5491 } 5492 5493 if (unlikely(sh->prop_count >= sh->prop_size)) { 5494 if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { 5495 /* in case of error, reinsert in the hash table. 5496 sh is still valid if resize_properties() failed */ 5497 if (sh->is_hashed) 5498 js_shape_hash_link(rt, sh); 5499 return -1; 5500 } 5501 sh = *psh; 5502 } 5503 if (sh->is_hashed) { 5504 sh->hash = new_shape_hash; 5505 js_shape_hash_link(rt, sh); 5506 } 5507 /* Initialize the new shape property. 5508 The object property at p->prop[sh->prop_count] is uninitialized */ 5509 prop = get_shape_prop(sh); 5510 pr = &prop[sh->prop_count++]; 5511 pr->atom = JS_DupAtom(ctx, atom); 5512 pr->flags = prop_flags; 5513 /* add in hash table */ 5514 hash_mask = sh->prop_hash_mask; 5515 h = atom & hash_mask; 5516 pr->hash_next = sh->hash_table[h]; 5517 sh->hash_table[h] = sh->prop_count; 5518 return 0; 5519 } 5520 5521 /* find a hashed empty shape matching the prototype. Return NULL if 5522 not found */ 5523 static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) 5524 { 5525 JSShape *sh1; 5526 uint32_t h, h1; 5527 5528 h = shape_initial_hash(proto); 5529 h1 = get_shape_hash(h, rt->shape_hash_bits); 5530 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { 5531 if (sh1->hash == h && 5532 sh1->proto == proto && 5533 sh1->prop_count == 0) { 5534 return sh1; 5535 } 5536 } 5537 return NULL; 5538 } 5539 5540 /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if 5541 not found */ 5542 static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, 5543 JSAtom atom, int prop_flags) 5544 { 5545 JSShape *sh1; 5546 uint32_t h, h1, i, n; 5547 5548 h = sh->hash; 5549 h = shape_hash(h, atom); 5550 h = shape_hash(h, prop_flags); 5551 h1 = get_shape_hash(h, rt->shape_hash_bits); 5552 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { 5553 /* we test the hash first so that the rest is done only if the 5554 shapes really match */ 5555 if (sh1->hash == h && 5556 sh1->proto == sh->proto && 5557 sh1->prop_count == ((n = sh->prop_count) + 1)) { 5558 JSShapeProperty *prop = get_shape_prop(sh); 5559 JSShapeProperty *prop1 = get_shape_prop(sh1); 5560 for(i = 0; i < n; i++) { 5561 if (unlikely(prop1[i].atom != prop[i].atom) || 5562 unlikely(prop1[i].flags != prop[i].flags)) 5563 goto next; 5564 } 5565 if (unlikely(prop1[n].atom != atom) || 5566 unlikely(prop1[n].flags != prop_flags)) 5567 goto next; 5568 return sh1; 5569 } 5570 next: ; 5571 } 5572 return NULL; 5573 } 5574 5575 static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) 5576 { 5577 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 5578 int j; 5579 5580 /* XXX: should output readable class prototype */ 5581 printf("%5d %3d%c %14p %5d %5d", i, 5582 js_rc(sh)->ref_count, " *"[sh->is_hashed], 5583 (void *)sh->proto, sh->prop_size, sh->prop_count); 5584 for(j = 0; j < sh->prop_count; j++) { 5585 printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), 5586 get_shape_prop(sh)[j].atom)); 5587 } 5588 printf("\n"); 5589 } 5590 5591 static __maybe_unused void JS_DumpShapes(JSRuntime *rt) 5592 { 5593 int i; 5594 JSShape *sh; 5595 struct list_head *el; 5596 JSObject *p; 5597 JSGCObjectHeader *gp; 5598 5599 printf("JSShapes: {\n"); 5600 printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); 5601 for(i = 0; i < rt->shape_hash_size; i++) { 5602 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { 5603 JS_DumpShape(rt, i, sh); 5604 assert(sh->is_hashed); 5605 } 5606 } 5607 /* dump non-hashed shapes */ 5608 list_for_each(el, &rt->gc_obj_list) { 5609 gp = list_entry(el, JSGCObjectHeader, link); 5610 if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 5611 p = (JSObject *)gp; 5612 if (!p->shape->is_hashed) { 5613 JS_DumpShape(rt, -1, p->shape); 5614 } 5615 } 5616 } 5617 printf("}\n"); 5618 } 5619 5620 /* 'props[]' is used to initialized the object properties. The number 5621 of elements depends on the shape. */ 5622 static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id, 5623 JSProperty *props) 5624 { 5625 JSObject *p; 5626 int i; 5627 5628 js_trigger_gc(ctx->rt, sizeof(JSObject)); 5629 p = js_malloc(ctx, sizeof(JSObject)); 5630 if (unlikely(!p)) 5631 goto fail; 5632 p->class_id = class_id; 5633 p->is_std_array_prototype = 0; 5634 p->extensible = TRUE; 5635 p->free_mark = 0; 5636 p->is_exotic = 0; 5637 p->fast_array = 0; 5638 p->is_constructor = 0; 5639 p->has_immutable_prototype = 0; 5640 p->tmp_mark = 0; 5641 p->is_HTMLDDA = 0; 5642 p->weakref_count = 0; 5643 p->u.opaque = NULL; 5644 p->shape = sh; 5645 p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); 5646 if (unlikely(!p->prop)) { 5647 js_free(ctx, p); 5648 fail: 5649 if (props) { 5650 JSShapeProperty *prs = get_shape_prop(sh); 5651 for(i = 0; i < sh->prop_count; i++) { 5652 free_property(ctx->rt, &props[i], prs->flags); 5653 prs++; 5654 } 5655 } 5656 js_free_shape(ctx->rt, sh); 5657 return JS_EXCEPTION; 5658 } 5659 5660 switch(class_id) { 5661 case JS_CLASS_OBJECT: 5662 break; 5663 case JS_CLASS_ARRAY: 5664 { 5665 JSProperty *pr; 5666 p->is_exotic = 1; 5667 p->fast_array = 1; 5668 p->u.array.u.values = NULL; 5669 p->u.array.count = 0; 5670 p->u.array.u1.size = 0; 5671 if (!props) { 5672 /* XXX: remove */ 5673 /* the length property is always the first one */ 5674 if (likely(sh == ctx->array_shape)) { 5675 pr = &p->prop[0]; 5676 } else { 5677 /* only used for the first array */ 5678 /* cannot fail */ 5679 pr = add_property(ctx, p, JS_ATOM_length, 5680 JS_PROP_WRITABLE | JS_PROP_LENGTH); 5681 } 5682 pr->u.value = JS_NewInt32(ctx, 0); 5683 } 5684 } 5685 break; 5686 case JS_CLASS_C_FUNCTION: 5687 p->prop[0].u.value = JS_UNDEFINED; 5688 break; 5689 case JS_CLASS_ARGUMENTS: 5690 case JS_CLASS_MAPPED_ARGUMENTS: 5691 case JS_CLASS_UINT8C_ARRAY: 5692 case JS_CLASS_INT8_ARRAY: 5693 case JS_CLASS_UINT8_ARRAY: 5694 case JS_CLASS_INT16_ARRAY: 5695 case JS_CLASS_UINT16_ARRAY: 5696 case JS_CLASS_INT32_ARRAY: 5697 case JS_CLASS_UINT32_ARRAY: 5698 case JS_CLASS_BIG_INT64_ARRAY: 5699 case JS_CLASS_BIG_UINT64_ARRAY: 5700 case JS_CLASS_FLOAT16_ARRAY: 5701 case JS_CLASS_FLOAT32_ARRAY: 5702 case JS_CLASS_FLOAT64_ARRAY: 5703 p->is_exotic = 1; 5704 p->fast_array = 1; 5705 p->u.array.u.ptr = NULL; 5706 p->u.array.count = 0; 5707 break; 5708 case JS_CLASS_DATAVIEW: 5709 p->u.array.u.ptr = NULL; 5710 p->u.array.count = 0; 5711 break; 5712 case JS_CLASS_NUMBER: 5713 case JS_CLASS_STRING: 5714 case JS_CLASS_BOOLEAN: 5715 case JS_CLASS_SYMBOL: 5716 case JS_CLASS_DATE: 5717 case JS_CLASS_BIG_INT: 5718 p->u.object_data = JS_UNDEFINED; 5719 goto set_exotic; 5720 case JS_CLASS_REGEXP: 5721 p->u.regexp.pattern = NULL; 5722 p->u.regexp.bytecode = NULL; 5723 break; 5724 case JS_CLASS_GLOBAL_OBJECT: 5725 p->u.global_object.uninitialized_vars = JS_UNDEFINED; 5726 break; 5727 default: 5728 set_exotic: 5729 if (ctx->rt->class_array[class_id].exotic) { 5730 p->is_exotic = 1; 5731 } 5732 break; 5733 } 5734 js_rc(p)->ref_count = 1; 5735 add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); 5736 if (props) { 5737 for(i = 0; i < sh->prop_count; i++) 5738 p->prop[i] = props[i]; 5739 } 5740 return JS_MKPTR(JS_TAG_OBJECT, p); 5741 } 5742 5743 static JSObject *get_proto_obj(JSValueConst proto_val) 5744 { 5745 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) 5746 return NULL; 5747 else 5748 return JS_VALUE_GET_OBJ(proto_val); 5749 } 5750 5751 /* WARNING: proto must be an object or JS_NULL */ 5752 JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, 5753 JSClassID class_id) 5754 { 5755 JSShape *sh; 5756 JSObject *proto; 5757 5758 proto = get_proto_obj(proto_val); 5759 sh = find_hashed_shape_proto(ctx->rt, proto); 5760 if (likely(sh)) { 5761 sh = js_dup_shape(sh); 5762 } else { 5763 sh = js_new_shape(ctx, proto); 5764 if (!sh) 5765 return JS_EXCEPTION; 5766 } 5767 return JS_NewObjectFromShape(ctx, sh, class_id, NULL); 5768 } 5769 5770 /* WARNING: the shape is not hashed. It is used for objects where 5771 factorizing the shape is not relevant (prototypes, constructors) */ 5772 static JSValue JS_NewObjectProtoClassAlloc(JSContext *ctx, JSValueConst proto_val, 5773 JSClassID class_id, int n_alloc_props) 5774 { 5775 JSShape *sh; 5776 JSObject *proto; 5777 int hash_size, hash_bits; 5778 5779 if (n_alloc_props <= JS_PROP_INITIAL_SIZE) { 5780 n_alloc_props = JS_PROP_INITIAL_SIZE; 5781 hash_size = JS_PROP_INITIAL_HASH_SIZE; 5782 } else { 5783 hash_bits = 32 - clz32(n_alloc_props - 1); /* ceil(log2(radix)) */ 5784 hash_size = 1 << hash_bits; 5785 } 5786 proto = get_proto_obj(proto_val); 5787 sh = js_new_shape_nohash(ctx, proto, hash_size, n_alloc_props); 5788 if (!sh) 5789 return JS_EXCEPTION; 5790 return JS_NewObjectFromShape(ctx, sh, class_id, NULL); 5791 } 5792 5793 #if 0 5794 static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) 5795 { 5796 JSObject *p; 5797 5798 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 5799 p = JS_VALUE_GET_OBJ(obj); 5800 switch(p->class_id) { 5801 case JS_CLASS_NUMBER: 5802 case JS_CLASS_STRING: 5803 case JS_CLASS_BOOLEAN: 5804 case JS_CLASS_SYMBOL: 5805 case JS_CLASS_DATE: 5806 case JS_CLASS_BIG_INT: 5807 return JS_DupValue(ctx, p->u.object_data); 5808 } 5809 } 5810 return JS_UNDEFINED; 5811 } 5812 #endif 5813 5814 static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) 5815 { 5816 JSObject *p; 5817 5818 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 5819 p = JS_VALUE_GET_OBJ(obj); 5820 switch(p->class_id) { 5821 case JS_CLASS_NUMBER: 5822 case JS_CLASS_STRING: 5823 case JS_CLASS_BOOLEAN: 5824 case JS_CLASS_SYMBOL: 5825 case JS_CLASS_DATE: 5826 case JS_CLASS_BIG_INT: 5827 JS_FreeValue(ctx, p->u.object_data); 5828 p->u.object_data = val; /* for JS_CLASS_STRING, 'val' must 5829 be JS_TAG_STRING (and not a 5830 rope) */ 5831 return 0; 5832 } 5833 } 5834 JS_FreeValue(ctx, val); 5835 if (!JS_IsException(obj)) 5836 JS_ThrowTypeError(ctx, "invalid object type"); 5837 return -1; 5838 } 5839 5840 JSValue JS_NewObjectClass(JSContext *ctx, int class_id) 5841 { 5842 return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); 5843 } 5844 5845 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) 5846 { 5847 return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); 5848 } 5849 5850 JSValue JS_NewArray(JSContext *ctx) 5851 { 5852 return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), 5853 JS_CLASS_ARRAY, NULL); 5854 } 5855 5856 JSValue JS_NewObject(JSContext *ctx) 5857 { 5858 /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ 5859 return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); 5860 } 5861 5862 static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, 5863 JSAtom name, int len) 5864 { 5865 /* ES6 feature non compatible with ES5.1: length is configurable */ 5866 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), 5867 JS_PROP_CONFIGURABLE); 5868 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, 5869 JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); 5870 } 5871 5872 static BOOL js_class_has_bytecode(JSClassID class_id) 5873 { 5874 return (class_id == JS_CLASS_BYTECODE_FUNCTION || 5875 class_id == JS_CLASS_GENERATOR_FUNCTION || 5876 class_id == JS_CLASS_ASYNC_FUNCTION || 5877 class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); 5878 } 5879 5880 /* return NULL without exception if not a function or no bytecode */ 5881 static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) 5882 { 5883 JSObject *p; 5884 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 5885 return NULL; 5886 p = JS_VALUE_GET_OBJ(val); 5887 if (!js_class_has_bytecode(p->class_id)) 5888 return NULL; 5889 return p->u.func.function_bytecode; 5890 } 5891 5892 static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, 5893 JSValueConst home_obj) 5894 { 5895 JSObject *p, *p1; 5896 JSFunctionBytecode *b; 5897 5898 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 5899 return; 5900 p = JS_VALUE_GET_OBJ(func_obj); 5901 if (!js_class_has_bytecode(p->class_id)) 5902 return; 5903 b = p->u.func.function_bytecode; 5904 if (b->need_home_object) { 5905 p1 = p->u.func.home_object; 5906 if (p1) { 5907 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 5908 } 5909 if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) 5910 p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); 5911 else 5912 p1 = NULL; 5913 p->u.func.home_object = p1; 5914 } 5915 } 5916 5917 static JSValue js_get_function_name(JSContext *ctx, JSAtom name) 5918 { 5919 JSValue name_str; 5920 5921 name_str = JS_AtomToString(ctx, name); 5922 if (JS_AtomSymbolHasDescription(ctx, name)) { 5923 name_str = JS_ConcatString3(ctx, "[", name_str, "]"); 5924 } 5925 return name_str; 5926 } 5927 5928 /* Modify the name of a method according to the atom and 5929 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and 5930 JS_PROP_HAS_SET. Also set the home object of the method. 5931 Return < 0 if exception. */ 5932 static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, 5933 JSAtom name, int flags, JSValueConst home_obj) 5934 { 5935 JSValue name_str; 5936 5937 name_str = js_get_function_name(ctx, name); 5938 if (flags & JS_PROP_HAS_GET) { 5939 name_str = JS_ConcatString3(ctx, "get ", name_str, ""); 5940 } else if (flags & JS_PROP_HAS_SET) { 5941 name_str = JS_ConcatString3(ctx, "set ", name_str, ""); 5942 } 5943 if (JS_IsException(name_str)) 5944 return -1; 5945 if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, 5946 JS_PROP_CONFIGURABLE) < 0) 5947 return -1; 5948 js_method_set_home_object(ctx, func_obj, home_obj); 5949 return 0; 5950 } 5951 5952 /* Note: at least 'length' arguments will be readable in 'argv' */ 5953 static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, 5954 const char *name, 5955 int length, JSCFunctionEnum cproto, int magic, 5956 JSValueConst proto_val, int n_fields) 5957 { 5958 JSValue func_obj; 5959 JSObject *p; 5960 JSAtom name_atom; 5961 5962 if (n_fields > 0) { 5963 func_obj = JS_NewObjectProtoClassAlloc(ctx, proto_val, JS_CLASS_C_FUNCTION, n_fields); 5964 } else { 5965 func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); 5966 } 5967 if (JS_IsException(func_obj)) 5968 return func_obj; 5969 p = JS_VALUE_GET_OBJ(func_obj); 5970 p->u.cfunc.realm = JS_DupContext(ctx); 5971 p->u.cfunc.c_function.generic = func; 5972 p->u.cfunc.length = length; 5973 p->u.cfunc.cproto = cproto; 5974 p->u.cfunc.magic = magic; 5975 p->is_constructor = (cproto == JS_CFUNC_constructor || 5976 cproto == JS_CFUNC_constructor_magic || 5977 cproto == JS_CFUNC_constructor_or_func || 5978 cproto == JS_CFUNC_constructor_or_func_magic); 5979 if (!name) 5980 name = ""; 5981 name_atom = JS_NewAtom(ctx, name); 5982 if (name_atom == JS_ATOM_NULL) { 5983 JS_FreeValue(ctx, func_obj); 5984 return JS_EXCEPTION; 5985 } 5986 js_function_set_properties(ctx, func_obj, name_atom, length); 5987 JS_FreeAtom(ctx, name_atom); 5988 return func_obj; 5989 } 5990 5991 /* Note: at least 'length' arguments will be readable in 'argv' */ 5992 JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, 5993 const char *name, 5994 int length, JSCFunctionEnum cproto, int magic) 5995 { 5996 return JS_NewCFunction3(ctx, func, name, length, cproto, magic, 5997 ctx->function_proto, 0); 5998 } 5999 6000 typedef struct JSCFunctionDataRecord { 6001 JSCFunctionData *func; 6002 uint8_t length; 6003 uint8_t data_len; 6004 uint16_t magic; 6005 JSValue data[0]; 6006 } JSCFunctionDataRecord; 6007 6008 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) 6009 { 6010 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); 6011 int i; 6012 6013 if (s) { 6014 for(i = 0; i < s->data_len; i++) { 6015 JS_FreeValueRT(rt, s->data[i]); 6016 } 6017 js_free_rt(rt, s); 6018 } 6019 } 6020 6021 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, 6022 JS_MarkFunc *mark_func) 6023 { 6024 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); 6025 int i; 6026 6027 if (s) { 6028 for(i = 0; i < s->data_len; i++) { 6029 JS_MarkValue(rt, s->data[i], mark_func); 6030 } 6031 } 6032 } 6033 6034 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, 6035 JSValueConst this_val, 6036 int argc, JSValueConst *argv, int flags) 6037 { 6038 JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); 6039 JSValueConst *arg_buf; 6040 int i; 6041 6042 /* XXX: could add the function on the stack for debug */ 6043 if (unlikely(argc < s->length)) { 6044 arg_buf = alloca(sizeof(arg_buf[0]) * s->length); 6045 for(i = 0; i < argc; i++) 6046 arg_buf[i] = argv[i]; 6047 for(i = argc; i < s->length; i++) 6048 arg_buf[i] = JS_UNDEFINED; 6049 } else { 6050 arg_buf = argv; 6051 } 6052 6053 return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); 6054 } 6055 6056 JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, 6057 int length, int magic, int data_len, 6058 JSValueConst *data) 6059 { 6060 JSCFunctionDataRecord *s; 6061 JSValue func_obj; 6062 int i; 6063 6064 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 6065 JS_CLASS_C_FUNCTION_DATA); 6066 if (JS_IsException(func_obj)) 6067 return func_obj; 6068 s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); 6069 if (!s) { 6070 JS_FreeValue(ctx, func_obj); 6071 return JS_EXCEPTION; 6072 } 6073 s->func = func; 6074 s->length = length; 6075 s->data_len = data_len; 6076 s->magic = magic; 6077 for(i = 0; i < data_len; i++) 6078 s->data[i] = JS_DupValue(ctx, data[i]); 6079 JS_SetOpaque(func_obj, s); 6080 js_function_set_properties(ctx, func_obj, 6081 JS_ATOM_empty_string, length); 6082 return func_obj; 6083 } 6084 6085 static JSContext *js_autoinit_get_realm(JSProperty *pr) 6086 { 6087 return (JSContext *)(pr->u.init.realm_and_id & ~3); 6088 } 6089 6090 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) 6091 { 6092 return pr->u.init.realm_and_id & 3; 6093 } 6094 6095 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) 6096 { 6097 JS_FreeContext(js_autoinit_get_realm(pr)); 6098 } 6099 6100 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, 6101 JS_MarkFunc *mark_func) 6102 { 6103 mark_func(rt, &js_autoinit_get_realm(pr)->header); 6104 } 6105 6106 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) 6107 { 6108 if (unlikely(prop_flags & JS_PROP_TMASK)) { 6109 if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 6110 if (pr->u.getset.getter) 6111 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 6112 if (pr->u.getset.setter) 6113 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 6114 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 6115 free_var_ref(rt, pr->u.var_ref); 6116 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 6117 js_autoinit_free(rt, pr); 6118 } 6119 } else { 6120 JS_FreeValueRT(rt, pr->u.value); 6121 } 6122 } 6123 6124 static force_inline JSShapeProperty *find_own_property1(JSObject *p, 6125 JSAtom atom) 6126 { 6127 JSShape *sh; 6128 JSShapeProperty *pr, *prop; 6129 intptr_t h; 6130 sh = p->shape; 6131 h = (uintptr_t)atom & sh->prop_hash_mask; 6132 h = sh->hash_table[h]; 6133 prop = get_shape_prop(sh); 6134 while (h) { 6135 pr = &prop[h - 1]; 6136 if (likely(pr->atom == atom)) { 6137 return pr; 6138 } 6139 h = pr->hash_next; 6140 } 6141 return NULL; 6142 } 6143 6144 static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, 6145 JSObject *p, 6146 JSAtom atom) 6147 { 6148 JSShape *sh; 6149 JSShapeProperty *pr, *prop; 6150 intptr_t h; 6151 sh = p->shape; 6152 h = (uintptr_t)atom & sh->prop_hash_mask; 6153 h = sh->hash_table[h]; 6154 prop = get_shape_prop(sh); 6155 while (h) { 6156 pr = &prop[h - 1]; 6157 if (likely(pr->atom == atom)) { 6158 *ppr = &p->prop[h - 1]; 6159 /* the compiler should be able to assume that pr != NULL here */ 6160 return pr; 6161 } 6162 h = pr->hash_next; 6163 } 6164 *ppr = NULL; 6165 return NULL; 6166 } 6167 6168 /* indicate that the object may be part of a function prototype cycle */ 6169 static void set_cycle_flag(JSContext *ctx, JSValueConst obj) 6170 { 6171 } 6172 6173 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) 6174 { 6175 if (var_ref) { 6176 assert(js_rc(var_ref)->ref_count > 0); 6177 if (--js_rc(var_ref)->ref_count == 0) { 6178 if (var_ref->is_detached) { 6179 JS_FreeValueRT(rt, var_ref->value); 6180 } else { 6181 JSStackFrame *sf = var_ref->stack_frame; 6182 assert(sf->var_refs[var_ref->var_ref_idx] == var_ref); 6183 sf->var_refs[var_ref->var_ref_idx] = NULL; 6184 if (sf->js_mode & JS_MODE_ASYNC) { 6185 JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); 6186 async_func_free(rt, async_func); 6187 } 6188 } 6189 remove_gc_object(&var_ref->header); 6190 js_free_rt(rt, var_ref); 6191 } 6192 } 6193 } 6194 6195 static void js_array_finalizer(JSRuntime *rt, JSValue val) 6196 { 6197 JSObject *p = JS_VALUE_GET_OBJ(val); 6198 int i; 6199 6200 for(i = 0; i < p->u.array.count; i++) { 6201 JS_FreeValueRT(rt, p->u.array.u.values[i]); 6202 } 6203 js_free_rt(rt, p->u.array.u.values); 6204 } 6205 6206 static void js_array_mark(JSRuntime *rt, JSValueConst val, 6207 JS_MarkFunc *mark_func) 6208 { 6209 JSObject *p = JS_VALUE_GET_OBJ(val); 6210 int i; 6211 6212 for(i = 0; i < p->u.array.count; i++) { 6213 JS_MarkValue(rt, p->u.array.u.values[i], mark_func); 6214 } 6215 } 6216 6217 static void js_object_data_finalizer(JSRuntime *rt, JSValue val) 6218 { 6219 JSObject *p = JS_VALUE_GET_OBJ(val); 6220 JS_FreeValueRT(rt, p->u.object_data); 6221 p->u.object_data = JS_UNDEFINED; 6222 } 6223 6224 static void js_object_data_mark(JSRuntime *rt, JSValueConst val, 6225 JS_MarkFunc *mark_func) 6226 { 6227 JSObject *p = JS_VALUE_GET_OBJ(val); 6228 JS_MarkValue(rt, p->u.object_data, mark_func); 6229 } 6230 6231 static void js_c_function_finalizer(JSRuntime *rt, JSValue val) 6232 { 6233 JSObject *p = JS_VALUE_GET_OBJ(val); 6234 6235 if (p->u.cfunc.realm) 6236 JS_FreeContext(p->u.cfunc.realm); 6237 } 6238 6239 static void js_c_function_mark(JSRuntime *rt, JSValueConst val, 6240 JS_MarkFunc *mark_func) 6241 { 6242 JSObject *p = JS_VALUE_GET_OBJ(val); 6243 6244 if (p->u.cfunc.realm) 6245 mark_func(rt, &p->u.cfunc.realm->header); 6246 } 6247 6248 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) 6249 { 6250 JSObject *p1, *p = JS_VALUE_GET_OBJ(val); 6251 JSFunctionBytecode *b; 6252 JSVarRef **var_refs; 6253 int i; 6254 6255 p1 = p->u.func.home_object; 6256 if (p1) { 6257 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); 6258 } 6259 b = p->u.func.function_bytecode; 6260 if (b) { 6261 var_refs = p->u.func.var_refs; 6262 if (var_refs) { 6263 for(i = 0; i < b->closure_var_count; i++) 6264 free_var_ref(rt, var_refs[i]); 6265 js_free_rt(rt, var_refs); 6266 } 6267 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); 6268 } 6269 } 6270 6271 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, 6272 JS_MarkFunc *mark_func) 6273 { 6274 JSObject *p = JS_VALUE_GET_OBJ(val); 6275 JSVarRef **var_refs = p->u.func.var_refs; 6276 JSFunctionBytecode *b = p->u.func.function_bytecode; 6277 int i; 6278 6279 if (p->u.func.home_object) { 6280 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), 6281 mark_func); 6282 } 6283 if (b) { 6284 if (var_refs) { 6285 for(i = 0; i < b->closure_var_count; i++) { 6286 JSVarRef *var_ref = var_refs[i]; 6287 if (var_ref) { 6288 mark_func(rt, &var_ref->header); 6289 } 6290 } 6291 } 6292 /* must mark the function bytecode because template objects may be 6293 part of a cycle */ 6294 JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); 6295 } 6296 } 6297 6298 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) 6299 { 6300 JSObject *p = JS_VALUE_GET_OBJ(val); 6301 JSBoundFunction *bf = p->u.bound_function; 6302 int i; 6303 6304 JS_FreeValueRT(rt, bf->func_obj); 6305 JS_FreeValueRT(rt, bf->this_val); 6306 for(i = 0; i < bf->argc; i++) { 6307 JS_FreeValueRT(rt, bf->argv[i]); 6308 } 6309 js_free_rt(rt, bf); 6310 } 6311 6312 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, 6313 JS_MarkFunc *mark_func) 6314 { 6315 JSObject *p = JS_VALUE_GET_OBJ(val); 6316 JSBoundFunction *bf = p->u.bound_function; 6317 int i; 6318 6319 JS_MarkValue(rt, bf->func_obj, mark_func); 6320 JS_MarkValue(rt, bf->this_val, mark_func); 6321 for(i = 0; i < bf->argc; i++) 6322 JS_MarkValue(rt, bf->argv[i], mark_func); 6323 } 6324 6325 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) 6326 { 6327 JSObject *p = JS_VALUE_GET_OBJ(val); 6328 JSForInIterator *it = p->u.for_in_iterator; 6329 int i; 6330 6331 JS_FreeValueRT(rt, it->obj); 6332 if (!it->is_array) { 6333 for(i = 0; i < it->atom_count; i++) { 6334 JS_FreeAtomRT(rt, it->tab_atom[i].atom); 6335 } 6336 js_free_rt(rt, it->tab_atom); 6337 } 6338 js_free_rt(rt, it); 6339 } 6340 6341 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, 6342 JS_MarkFunc *mark_func) 6343 { 6344 JSObject *p = JS_VALUE_GET_OBJ(val); 6345 JSForInIterator *it = p->u.for_in_iterator; 6346 JS_MarkValue(rt, it->obj, mark_func); 6347 } 6348 6349 static void free_object(JSRuntime *rt, JSObject *p) 6350 { 6351 int i; 6352 JSClassFinalizer *finalizer; 6353 JSShape *sh; 6354 JSShapeProperty *pr; 6355 6356 p->free_mark = 1; /* used to tell the object is invalid when 6357 freeing cycles */ 6358 /* free all the fields */ 6359 sh = p->shape; 6360 pr = get_shape_prop(sh); 6361 for(i = 0; i < sh->prop_count; i++) { 6362 free_property(rt, &p->prop[i], pr->flags); 6363 pr++; 6364 } 6365 js_free_rt(rt, p->prop); 6366 /* as an optimization we destroy the shape immediately without 6367 putting it in gc_zero_ref_count_list */ 6368 js_free_shape(rt, sh); 6369 6370 /* fail safe */ 6371 p->shape = NULL; 6372 p->prop = NULL; 6373 6374 finalizer = rt->class_array[p->class_id].finalizer; 6375 if (finalizer) 6376 (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); 6377 6378 /* fail safe */ 6379 p->class_id = 0; 6380 p->u.opaque = NULL; 6381 p->u.func.var_refs = NULL; 6382 p->u.func.home_object = NULL; 6383 6384 remove_gc_object(&p->header); 6385 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) { 6386 if (js_rc(p)->ref_count == 0 && p->weakref_count == 0) { 6387 js_free_rt(rt, p); 6388 } else { 6389 /* keep the object structure because there are may be 6390 references to it */ 6391 list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); 6392 } 6393 } else { 6394 /* keep the object structure in case there are weak references to it */ 6395 if (p->weakref_count == 0) { 6396 js_free_rt(rt, p); 6397 } else { 6398 js_rc(p)->mark = 0; /* reset the mark so that the weakref can be freed */ 6399 } 6400 } 6401 } 6402 6403 static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) 6404 { 6405 switch(js_rc(gp)->gc_obj_type) { 6406 case JS_GC_OBJ_TYPE_JS_OBJECT: 6407 free_object(rt, (JSObject *)gp); 6408 break; 6409 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 6410 free_function_bytecode(rt, (JSFunctionBytecode *)gp); 6411 break; 6412 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 6413 __async_func_free(rt, (JSAsyncFunctionState *)gp); 6414 break; 6415 case JS_GC_OBJ_TYPE_MODULE: 6416 js_free_module_def(rt, (JSModuleDef *)gp); 6417 break; 6418 default: 6419 abort(); 6420 } 6421 } 6422 6423 static void free_zero_refcount(JSRuntime *rt) 6424 { 6425 struct list_head *el; 6426 JSGCObjectHeader *p; 6427 6428 rt->gc_phase = JS_GC_PHASE_DECREF; 6429 for(;;) { 6430 el = rt->gc_zero_ref_count_list.next; 6431 if (el == &rt->gc_zero_ref_count_list) 6432 break; 6433 p = list_entry(el, JSGCObjectHeader, link); 6434 assert(js_rc(p)->ref_count == 0); 6435 free_gc_object(rt, p); 6436 } 6437 rt->gc_phase = JS_GC_PHASE_NONE; 6438 } 6439 6440 /* called with the ref_count of 'v' reaches zero. */ 6441 void __JS_FreeValueRT(JSRuntime *rt, JSValue v) 6442 { 6443 uint32_t tag = JS_VALUE_GET_TAG(v); 6444 6445 #ifdef DUMP_FREE 6446 { 6447 printf("Freeing "); 6448 if (tag == JS_TAG_OBJECT) { 6449 JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); 6450 } else { 6451 JS_DumpValueShort(rt, v); 6452 printf("\n"); 6453 } 6454 } 6455 #endif 6456 6457 switch(tag) { 6458 case JS_TAG_STRING: 6459 { 6460 JSString *p = JS_VALUE_GET_STRING(v); 6461 if (p->atom_type) { 6462 JS_FreeAtomStruct(rt, p); 6463 } else { 6464 #ifdef DUMP_LEAKS 6465 list_del(&p->link); 6466 #endif 6467 js_free_rt(rt, p); 6468 } 6469 } 6470 break; 6471 case JS_TAG_STRING_ROPE: 6472 /* Note: recursion is acceptable because the rope depth is bounded */ 6473 { 6474 JSStringRope *p = JS_VALUE_GET_STRING_ROPE(v); 6475 JS_FreeValueRT(rt, p->left); 6476 JS_FreeValueRT(rt, p->right); 6477 js_free_rt(rt, p); 6478 } 6479 break; 6480 case JS_TAG_OBJECT: 6481 case JS_TAG_FUNCTION_BYTECODE: 6482 case JS_TAG_MODULE: 6483 { 6484 JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); 6485 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { 6486 list_del(&p->link); 6487 list_add(&p->link, &rt->gc_zero_ref_count_list); 6488 js_rc(p)->mark = 1; /* indicate that the object is about to be freed */ 6489 if (rt->gc_phase == JS_GC_PHASE_NONE) { 6490 free_zero_refcount(rt); 6491 } 6492 } 6493 } 6494 break; 6495 case JS_TAG_BIG_INT: 6496 { 6497 JSBigInt *p = JS_VALUE_GET_PTR(v); 6498 js_free_rt(rt, p); 6499 } 6500 break; 6501 case JS_TAG_SYMBOL: 6502 { 6503 JSAtomStruct *p = JS_VALUE_GET_PTR(v); 6504 JS_FreeAtomStruct(rt, p); 6505 } 6506 break; 6507 default: 6508 abort(); 6509 } 6510 } 6511 6512 void __JS_FreeValue(JSContext *ctx, JSValue v) 6513 { 6514 __JS_FreeValueRT(ctx->rt, v); 6515 } 6516 6517 /* garbage collection */ 6518 6519 static void gc_remove_weak_objects(JSRuntime *rt) 6520 { 6521 struct list_head *el; 6522 6523 /* add the freed objects to rt->gc_zero_ref_count_list so that 6524 rt->weakref_list is not modified while we traverse it */ 6525 rt->gc_phase = JS_GC_PHASE_DECREF; 6526 6527 list_for_each(el, &rt->weakref_list) { 6528 JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link); 6529 switch(wh->weakref_type) { 6530 case JS_WEAKREF_TYPE_MAP: 6531 map_delete_weakrefs(rt, wh); 6532 break; 6533 case JS_WEAKREF_TYPE_WEAKREF: 6534 weakref_delete_weakref(rt, wh); 6535 break; 6536 case JS_WEAKREF_TYPE_FINREC: 6537 finrec_delete_weakref(rt, wh); 6538 break; 6539 default: 6540 abort(); 6541 } 6542 } 6543 6544 rt->gc_phase = JS_GC_PHASE_NONE; 6545 /* free the freed objects here. */ 6546 free_zero_refcount(rt); 6547 } 6548 6549 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, 6550 JSGCObjectTypeEnum type) 6551 { 6552 js_rc(h)->mark = 0; 6553 js_rc(h)->gc_obj_type = type; 6554 list_add_tail(&h->link, &rt->gc_obj_list); 6555 } 6556 6557 static void remove_gc_object(JSGCObjectHeader *h) 6558 { 6559 list_del(&h->link); 6560 } 6561 6562 void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) 6563 { 6564 if (JS_VALUE_HAS_REF_COUNT(val)) { 6565 switch(JS_VALUE_GET_TAG(val)) { 6566 case JS_TAG_OBJECT: 6567 case JS_TAG_FUNCTION_BYTECODE: 6568 case JS_TAG_MODULE: 6569 mark_func(rt, JS_VALUE_GET_PTR(val)); 6570 break; 6571 default: 6572 break; 6573 } 6574 } 6575 } 6576 6577 static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, 6578 JS_MarkFunc *mark_func) 6579 { 6580 switch(js_rc(gp)->gc_obj_type) { 6581 case JS_GC_OBJ_TYPE_JS_OBJECT: 6582 { 6583 JSObject *p = (JSObject *)gp; 6584 JSShapeProperty *prs; 6585 JSShape *sh; 6586 int i; 6587 sh = p->shape; 6588 mark_func(rt, &sh->header); 6589 /* mark all the fields */ 6590 prs = get_shape_prop(sh); 6591 for(i = 0; i < sh->prop_count; i++) { 6592 JSProperty *pr = &p->prop[i]; 6593 if (prs->atom != JS_ATOM_NULL) { 6594 if (prs->flags & JS_PROP_TMASK) { 6595 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 6596 if (pr->u.getset.getter) 6597 mark_func(rt, &pr->u.getset.getter->header); 6598 if (pr->u.getset.setter) 6599 mark_func(rt, &pr->u.getset.setter->header); 6600 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 6601 /* Note: the tag does not matter 6602 provided it is a GC object */ 6603 mark_func(rt, &pr->u.var_ref->header); 6604 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 6605 js_autoinit_mark(rt, pr, mark_func); 6606 } 6607 } else { 6608 JS_MarkValue(rt, pr->u.value, mark_func); 6609 } 6610 } 6611 prs++; 6612 } 6613 6614 if (p->class_id != JS_CLASS_OBJECT) { 6615 JSClassGCMark *gc_mark; 6616 gc_mark = rt->class_array[p->class_id].gc_mark; 6617 if (gc_mark) 6618 gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); 6619 } 6620 } 6621 break; 6622 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 6623 /* the template objects can be part of a cycle */ 6624 { 6625 JSFunctionBytecode *b = (JSFunctionBytecode *)gp; 6626 int i; 6627 for(i = 0; i < b->cpool_count; i++) { 6628 JS_MarkValue(rt, b->cpool[i], mark_func); 6629 } 6630 if (b->realm) 6631 mark_func(rt, &b->realm->header); 6632 } 6633 break; 6634 case JS_GC_OBJ_TYPE_VAR_REF: 6635 { 6636 JSVarRef *var_ref = (JSVarRef *)gp; 6637 if (var_ref->is_detached) { 6638 JS_MarkValue(rt, *var_ref->pvalue, mark_func); 6639 } else { 6640 JSStackFrame *sf = var_ref->stack_frame; 6641 if (sf->js_mode & JS_MODE_ASYNC) { 6642 JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); 6643 mark_func(rt, &async_func->header); 6644 } 6645 } 6646 } 6647 break; 6648 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 6649 { 6650 JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp; 6651 JSStackFrame *sf = &s->frame; 6652 JSValue *sp; 6653 6654 if (!s->is_completed) { 6655 JS_MarkValue(rt, sf->cur_func, mark_func); 6656 JS_MarkValue(rt, s->this_val, mark_func); 6657 /* sf->cur_sp = NULL if the function is running */ 6658 if (sf->cur_sp) { 6659 /* if the function is running, cur_sp is not known so we 6660 cannot mark the stack. Marking the variables is not needed 6661 because a running function cannot be part of a removable 6662 cycle */ 6663 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) 6664 JS_MarkValue(rt, *sp, mark_func); 6665 } 6666 } 6667 JS_MarkValue(rt, s->resolving_funcs[0], mark_func); 6668 JS_MarkValue(rt, s->resolving_funcs[1], mark_func); 6669 } 6670 break; 6671 case JS_GC_OBJ_TYPE_SHAPE: 6672 { 6673 JSShape *sh = (JSShape *)gp; 6674 if (sh->proto != NULL) { 6675 mark_func(rt, &sh->proto->header); 6676 } 6677 } 6678 break; 6679 case JS_GC_OBJ_TYPE_JS_CONTEXT: 6680 { 6681 JSContext *ctx = (JSContext *)gp; 6682 JS_MarkContext(rt, ctx, mark_func); 6683 } 6684 break; 6685 case JS_GC_OBJ_TYPE_MODULE: 6686 { 6687 JSModuleDef *m = (JSModuleDef *)gp; 6688 js_mark_module_def(rt, m, mark_func); 6689 } 6690 break; 6691 default: 6692 abort(); 6693 } 6694 } 6695 6696 static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) 6697 { 6698 assert(js_rc(p)->ref_count > 0); 6699 js_rc(p)->ref_count--; 6700 if (js_rc(p)->ref_count == 0 && js_rc(p)->mark == 1) { 6701 list_del(&p->link); 6702 list_add_tail(&p->link, &rt->tmp_obj_list); 6703 } 6704 } 6705 6706 static void gc_decref(JSRuntime *rt) 6707 { 6708 struct list_head *el, *el1; 6709 JSGCObjectHeader *p; 6710 6711 init_list_head(&rt->tmp_obj_list); 6712 6713 /* decrement the refcount of all the children of all the GC 6714 objects and move the GC objects with zero refcount to 6715 tmp_obj_list */ 6716 list_for_each_safe(el, el1, &rt->gc_obj_list) { 6717 p = list_entry(el, JSGCObjectHeader, link); 6718 assert(js_rc(p)->mark == 0); 6719 mark_children(rt, p, gc_decref_child); 6720 js_rc(p)->mark = 1; 6721 if (js_rc(p)->ref_count == 0) { 6722 list_del(&p->link); 6723 list_add_tail(&p->link, &rt->tmp_obj_list); 6724 } 6725 } 6726 } 6727 6728 static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) 6729 { 6730 js_rc(p)->ref_count++; 6731 if (js_rc(p)->ref_count == 1) { 6732 /* ref_count was 0: remove from tmp_obj_list and add at the 6733 end of gc_obj_list */ 6734 list_del(&p->link); 6735 list_add_tail(&p->link, &rt->gc_obj_list); 6736 js_rc(p)->mark = 0; /* reset the mark for the next GC call */ 6737 } 6738 } 6739 6740 static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) 6741 { 6742 js_rc(p)->ref_count++; 6743 } 6744 6745 static void gc_scan(JSRuntime *rt) 6746 { 6747 struct list_head *el; 6748 JSGCObjectHeader *p; 6749 6750 /* keep the objects with a refcount > 0 and their children. */ 6751 list_for_each(el, &rt->gc_obj_list) { 6752 p = list_entry(el, JSGCObjectHeader, link); 6753 assert(js_rc(p)->ref_count > 0); 6754 js_rc(p)->mark = 0; /* reset the mark for the next GC call */ 6755 mark_children(rt, p, gc_scan_incref_child); 6756 } 6757 6758 /* restore the refcount of the objects to be deleted. */ 6759 list_for_each(el, &rt->tmp_obj_list) { 6760 p = list_entry(el, JSGCObjectHeader, link); 6761 mark_children(rt, p, gc_scan_incref_child2); 6762 } 6763 } 6764 6765 static void gc_free_cycles(JSRuntime *rt) 6766 { 6767 struct list_head *el, *el1; 6768 JSGCObjectHeader *p; 6769 #ifdef DUMP_GC_FREE 6770 BOOL header_done = FALSE; 6771 #endif 6772 6773 rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; 6774 6775 for(;;) { 6776 el = rt->tmp_obj_list.next; 6777 if (el == &rt->tmp_obj_list) 6778 break; 6779 p = list_entry(el, JSGCObjectHeader, link); 6780 /* Only need to free the GC object associated with JS values 6781 or async functions. The rest will be automatically removed 6782 because they must be referenced by them. */ 6783 switch(js_rc(p)->gc_obj_type) { 6784 case JS_GC_OBJ_TYPE_JS_OBJECT: 6785 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 6786 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 6787 case JS_GC_OBJ_TYPE_MODULE: 6788 #ifdef DUMP_GC_FREE 6789 if (!header_done) { 6790 printf("Freeing cycles:\n"); 6791 JS_DumpObjectHeader(rt); 6792 header_done = TRUE; 6793 } 6794 JS_DumpGCObject(rt, p); 6795 #endif 6796 free_gc_object(rt, p); 6797 break; 6798 default: 6799 list_del(&p->link); 6800 list_add_tail(&p->link, &rt->gc_zero_ref_count_list); 6801 break; 6802 } 6803 } 6804 rt->gc_phase = JS_GC_PHASE_NONE; 6805 6806 list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { 6807 p = list_entry(el, JSGCObjectHeader, link); 6808 assert(js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || 6809 js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || 6810 js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION || 6811 js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_MODULE); 6812 if (js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT && 6813 ((JSObject *)p)->weakref_count != 0) { 6814 /* keep the object because there are weak references to it */ 6815 js_rc(p)->mark = 0; 6816 } else { 6817 js_free_rt(rt, p); 6818 } 6819 } 6820 6821 init_list_head(&rt->gc_zero_ref_count_list); 6822 } 6823 6824 static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects) 6825 { 6826 if (remove_weak_objects) { 6827 /* free the weakly referenced object or symbol structures, delete 6828 the associated Map/Set entries and queue the finalization 6829 registry callbacks. */ 6830 gc_remove_weak_objects(rt); 6831 } 6832 6833 /* decrement the reference of the children of each object. mark = 6834 1 after this pass. */ 6835 gc_decref(rt); 6836 6837 /* keep the GC objects with a non zero refcount and their childs */ 6838 gc_scan(rt); 6839 6840 /* free the GC objects in a cycle */ 6841 gc_free_cycles(rt); 6842 } 6843 6844 void JS_RunGC(JSRuntime *rt) 6845 { 6846 JS_RunGCInternal(rt, TRUE); 6847 } 6848 6849 /* Return false if not an object or if the object has already been 6850 freed (zombie objects are visible in finalizers when freeing 6851 cycles). */ 6852 BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) 6853 { 6854 JSObject *p; 6855 if (!JS_IsObject(obj)) 6856 return FALSE; 6857 p = JS_VALUE_GET_OBJ(obj); 6858 return !p->free_mark; 6859 } 6860 6861 /* Compute memory used by various object types */ 6862 /* XXX: poor man's approach to handling multiply referenced objects */ 6863 typedef struct JSMemoryUsage_helper { 6864 double memory_used_count; 6865 double str_count; 6866 double str_size; 6867 int64_t js_func_count; 6868 double js_func_size; 6869 int64_t js_func_code_size; 6870 int64_t js_func_pc2line_count; 6871 int64_t js_func_pc2line_size; 6872 } JSMemoryUsage_helper; 6873 6874 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); 6875 6876 static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) 6877 { 6878 if (!str->atom_type) { /* atoms are handled separately */ 6879 double s_ref_count = js_rc(str)->ref_count; 6880 hp->str_count += 1 / s_ref_count; 6881 hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + 6882 1 - str->is_wide_char) / s_ref_count); 6883 } 6884 } 6885 6886 static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) 6887 { 6888 int memory_used_count, js_func_size, i; 6889 6890 memory_used_count = 0; 6891 js_func_size = offsetof(JSFunctionBytecode, debug); 6892 if (b->vardefs) { 6893 js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); 6894 } 6895 if (b->cpool) { 6896 js_func_size += b->cpool_count * sizeof(*b->cpool); 6897 for (i = 0; i < b->cpool_count; i++) { 6898 JSValueConst val = b->cpool[i]; 6899 compute_value_size(val, hp); 6900 } 6901 } 6902 if (b->closure_var) { 6903 js_func_size += b->closure_var_count * sizeof(*b->closure_var); 6904 } 6905 if (!b->read_only_bytecode && b->byte_code_buf) { 6906 hp->js_func_code_size += b->byte_code_len; 6907 } 6908 if (b->has_debug) { 6909 js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); 6910 if (b->debug.source) { 6911 memory_used_count++; 6912 js_func_size += b->debug.source_len + 1; 6913 } 6914 if (b->debug.pc2line_len) { 6915 memory_used_count++; 6916 hp->js_func_pc2line_count += 1; 6917 hp->js_func_pc2line_size += b->debug.pc2line_len; 6918 } 6919 } 6920 hp->js_func_size += js_func_size; 6921 hp->js_func_count += 1; 6922 hp->memory_used_count += memory_used_count; 6923 } 6924 6925 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) 6926 { 6927 switch(JS_VALUE_GET_TAG(val)) { 6928 case JS_TAG_STRING: 6929 compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); 6930 break; 6931 case JS_TAG_BIG_INT: 6932 /* should track JSBigInt usage */ 6933 break; 6934 } 6935 } 6936 6937 void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) 6938 { 6939 struct list_head *el, *el1; 6940 int i; 6941 JSMemoryUsage_helper mem = { 0 }, *hp = &mem; 6942 6943 memset(s, 0, sizeof(*s)); 6944 s->malloc_count = rt->malloc_ctx.malloc_state.malloc_count; 6945 s->malloc_size = rt->malloc_ctx.malloc_state.malloc_size; 6946 s->malloc_limit = rt->malloc_ctx.malloc_state.malloc_limit; 6947 6948 s->memory_used_count = 2; /* rt + rt->class_array */ 6949 s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; 6950 6951 list_for_each(el, &rt->context_list) { 6952 JSContext *ctx = list_entry(el, JSContext, link); 6953 JSShape *sh = ctx->array_shape; 6954 s->memory_used_count += 2; /* ctx + ctx->class_proto */ 6955 s->memory_used_size += sizeof(JSContext) + 6956 sizeof(JSValue) * rt->class_count; 6957 s->binary_object_count += ctx->binary_object_count; 6958 s->binary_object_size += ctx->binary_object_size; 6959 6960 /* the hashed shapes are counted separately */ 6961 if (sh && !sh->is_hashed) { 6962 int hash_size = sh->prop_hash_mask + 1; 6963 s->shape_count++; 6964 s->shape_size += get_shape_size(hash_size, sh->prop_size); 6965 } 6966 list_for_each(el1, &ctx->loaded_modules) { 6967 JSModuleDef *m = list_entry(el1, JSModuleDef, link); 6968 s->memory_used_count += 1; 6969 s->memory_used_size += sizeof(*m); 6970 if (m->req_module_entries) { 6971 s->memory_used_count += 1; 6972 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); 6973 } 6974 if (m->export_entries) { 6975 s->memory_used_count += 1; 6976 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); 6977 for (i = 0; i < m->export_entries_count; i++) { 6978 JSExportEntry *me = &m->export_entries[i]; 6979 if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { 6980 /* potential multiple count */ 6981 s->memory_used_count += 1; 6982 compute_value_size(me->u.local.var_ref->value, hp); 6983 } 6984 } 6985 } 6986 if (m->star_export_entries) { 6987 s->memory_used_count += 1; 6988 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); 6989 } 6990 if (m->import_entries) { 6991 s->memory_used_count += 1; 6992 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); 6993 } 6994 compute_value_size(m->module_ns, hp); 6995 compute_value_size(m->func_obj, hp); 6996 } 6997 } 6998 6999 list_for_each(el, &rt->gc_obj_list) { 7000 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); 7001 JSObject *p; 7002 JSShape *sh; 7003 JSShapeProperty *prs; 7004 7005 /* XXX: could count the other GC object types too */ 7006 if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { 7007 compute_bytecode_size((JSFunctionBytecode *)gp, hp); 7008 continue; 7009 } else if (js_rc(gp)->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { 7010 continue; 7011 } 7012 p = (JSObject *)gp; 7013 sh = p->shape; 7014 s->obj_count++; 7015 if (p->prop) { 7016 s->memory_used_count++; 7017 s->prop_size += sh->prop_size * sizeof(*p->prop); 7018 s->prop_count += sh->prop_count; 7019 prs = get_shape_prop(sh); 7020 for(i = 0; i < sh->prop_count; i++) { 7021 JSProperty *pr = &p->prop[i]; 7022 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { 7023 compute_value_size(pr->u.value, hp); 7024 } 7025 prs++; 7026 } 7027 } 7028 /* the hashed shapes are counted separately */ 7029 if (!sh->is_hashed) { 7030 int hash_size = sh->prop_hash_mask + 1; 7031 s->shape_count++; 7032 s->shape_size += get_shape_size(hash_size, sh->prop_size); 7033 } 7034 7035 switch(p->class_id) { 7036 case JS_CLASS_ARRAY: /* u.array | length */ 7037 case JS_CLASS_ARGUMENTS: /* u.array | length */ 7038 s->array_count++; 7039 if (p->fast_array) { 7040 s->fast_array_count++; 7041 if (p->u.array.u.values) { 7042 s->memory_used_count++; 7043 s->memory_used_size += p->u.array.count * 7044 sizeof(*p->u.array.u.values); 7045 s->fast_array_elements += p->u.array.count; 7046 for (i = 0; i < p->u.array.count; i++) { 7047 compute_value_size(p->u.array.u.values[i], hp); 7048 } 7049 } 7050 } 7051 break; 7052 case JS_CLASS_MAPPED_ARGUMENTS: /* u.array | length */ 7053 if (p->fast_array) { 7054 s->fast_array_count++; 7055 if (p->u.array.u.values) { 7056 s->memory_used_count++; 7057 s->memory_used_size += p->u.array.count * 7058 sizeof(*p->u.array.u.var_refs); 7059 s->fast_array_elements += p->u.array.count; 7060 for (i = 0; i < p->u.array.count; i++) { 7061 compute_value_size(*p->u.array.u.var_refs[i]->pvalue, hp); 7062 } 7063 } 7064 } 7065 break; 7066 case JS_CLASS_NUMBER: /* u.object_data */ 7067 case JS_CLASS_STRING: /* u.object_data */ 7068 case JS_CLASS_BOOLEAN: /* u.object_data */ 7069 case JS_CLASS_SYMBOL: /* u.object_data */ 7070 case JS_CLASS_DATE: /* u.object_data */ 7071 case JS_CLASS_BIG_INT: /* u.object_data */ 7072 compute_value_size(p->u.object_data, hp); 7073 break; 7074 case JS_CLASS_C_FUNCTION: /* u.cfunc */ 7075 s->c_func_count++; 7076 break; 7077 case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ 7078 { 7079 JSFunctionBytecode *b = p->u.func.function_bytecode; 7080 JSVarRef **var_refs = p->u.func.var_refs; 7081 /* home_object: object will be accounted for in list scan */ 7082 if (var_refs) { 7083 s->memory_used_count++; 7084 s->js_func_size += b->closure_var_count * sizeof(*var_refs); 7085 for (i = 0; i < b->closure_var_count; i++) { 7086 if (var_refs[i]) { 7087 double ref_count = js_rc(var_refs[i])->ref_count; 7088 s->memory_used_count += 1 / ref_count; 7089 s->js_func_size += sizeof(*var_refs[i]) / ref_count; 7090 /* handle non object closed values */ 7091 if (var_refs[i]->pvalue == &var_refs[i]->value) { 7092 /* potential multiple count */ 7093 compute_value_size(var_refs[i]->value, hp); 7094 } 7095 } 7096 } 7097 } 7098 } 7099 break; 7100 case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ 7101 { 7102 JSBoundFunction *bf = p->u.bound_function; 7103 /* func_obj and this_val are objects */ 7104 for (i = 0; i < bf->argc; i++) { 7105 compute_value_size(bf->argv[i], hp); 7106 } 7107 s->memory_used_count += 1; 7108 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); 7109 } 7110 break; 7111 case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ 7112 { 7113 JSCFunctionDataRecord *fd = p->u.c_function_data_record; 7114 if (fd) { 7115 for (i = 0; i < fd->data_len; i++) { 7116 compute_value_size(fd->data[i], hp); 7117 } 7118 s->memory_used_count += 1; 7119 s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); 7120 } 7121 } 7122 break; 7123 case JS_CLASS_REGEXP: /* u.regexp */ 7124 compute_jsstring_size(p->u.regexp.pattern, hp); 7125 compute_jsstring_size(p->u.regexp.bytecode, hp); 7126 break; 7127 7128 case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ 7129 { 7130 JSForInIterator *it = p->u.for_in_iterator; 7131 if (it) { 7132 compute_value_size(it->obj, hp); 7133 s->memory_used_count += 1; 7134 s->memory_used_size += sizeof(*it); 7135 } 7136 } 7137 break; 7138 case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ 7139 case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ 7140 { 7141 JSArrayBuffer *abuf = p->u.array_buffer; 7142 if (abuf) { 7143 s->memory_used_count += 1; 7144 s->memory_used_size += sizeof(*abuf); 7145 if (abuf->data) { 7146 s->memory_used_count += 1; 7147 s->memory_used_size += abuf->byte_length; 7148 } 7149 } 7150 } 7151 break; 7152 case JS_CLASS_GENERATOR: /* u.generator_data */ 7153 case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ 7154 case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ 7155 case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ 7156 case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ 7157 case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ 7158 case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ 7159 case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ 7160 case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ 7161 case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ 7162 case JS_CLASS_FLOAT16_ARRAY: /* u.typed_array / u.array */ 7163 case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ 7164 case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ 7165 case JS_CLASS_DATAVIEW: /* u.typed_array */ 7166 case JS_CLASS_MAP: /* u.map_state */ 7167 case JS_CLASS_SET: /* u.map_state */ 7168 case JS_CLASS_WEAKMAP: /* u.map_state */ 7169 case JS_CLASS_WEAKSET: /* u.map_state */ 7170 case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ 7171 case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ 7172 case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ 7173 case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ 7174 case JS_CLASS_PROXY: /* u.proxy_data */ 7175 case JS_CLASS_PROMISE: /* u.promise_data */ 7176 case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ 7177 case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ 7178 case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ 7179 case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ 7180 case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ 7181 case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ 7182 /* TODO */ 7183 default: 7184 /* XXX: class definition should have an opaque block size */ 7185 if (p->u.opaque) { 7186 s->memory_used_count += 1; 7187 } 7188 break; 7189 } 7190 } 7191 s->obj_size += s->obj_count * sizeof(JSObject); 7192 7193 /* hashed shapes */ 7194 s->memory_used_count++; /* rt->shape_hash */ 7195 s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; 7196 for(i = 0; i < rt->shape_hash_size; i++) { 7197 JSShape *sh; 7198 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { 7199 int hash_size = sh->prop_hash_mask + 1; 7200 s->shape_count++; 7201 s->shape_size += get_shape_size(hash_size, sh->prop_size); 7202 } 7203 } 7204 7205 /* atoms */ 7206 s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ 7207 s->atom_count = rt->atom_count; 7208 s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + 7209 sizeof(rt->atom_hash[0]) * rt->atom_hash_size; 7210 for(i = 0; i < rt->atom_size; i++) { 7211 JSAtomStruct *p = rt->atom_array[i]; 7212 if (!atom_is_free(p)) { 7213 s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + 7214 1 - p->is_wide_char); 7215 } 7216 } 7217 s->str_count = round(mem.str_count); 7218 s->str_size = round(mem.str_size); 7219 s->js_func_count = mem.js_func_count; 7220 s->js_func_size = round(mem.js_func_size); 7221 s->js_func_code_size = mem.js_func_code_size; 7222 s->js_func_pc2line_count = mem.js_func_pc2line_count; 7223 s->js_func_pc2line_size = mem.js_func_pc2line_size; 7224 s->memory_used_count += round(mem.memory_used_count) + 7225 s->atom_count + s->str_count + 7226 s->obj_count + s->shape_count + 7227 s->js_func_count + s->js_func_pc2line_count; 7228 s->memory_used_size += s->atom_size + s->str_size + 7229 s->obj_size + s->prop_size + s->shape_size + 7230 s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; 7231 } 7232 7233 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) 7234 { 7235 fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", 7236 (int)sizeof(void *) * 8, s->malloc_limit); 7237 #if 1 7238 if (rt) { 7239 static const struct { 7240 const char *name; 7241 size_t size; 7242 } object_types[] = { 7243 { "JSRuntime", sizeof(JSRuntime) }, 7244 { "JSContext", sizeof(JSContext) }, 7245 { "JSObject", sizeof(JSObject) }, 7246 { "JSString", sizeof(JSString) }, 7247 { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, 7248 }; 7249 int i, usage_size_ok = 0; 7250 for(i = 0; i < countof(object_types); i++) { 7251 unsigned int size = object_types[i].size; 7252 void *p = js_malloc_rt(rt, size); 7253 if (p) { 7254 unsigned int size1 = js_malloc_usable_size_rt(rt, p); 7255 if (size1 >= size) { 7256 usage_size_ok = 1; 7257 fprintf(fp, " %3u + %-2u %s\n", 7258 size, size1 - size, object_types[i].name); 7259 } 7260 js_free_rt(rt, p); 7261 } 7262 } 7263 if (!usage_size_ok) { 7264 fprintf(fp, " malloc_usable_size unavailable\n"); 7265 } 7266 { 7267 int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; 7268 int class_id; 7269 struct list_head *el; 7270 list_for_each(el, &rt->gc_obj_list) { 7271 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); 7272 JSObject *p; 7273 if (js_rc(gp)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 7274 p = (JSObject *)gp; 7275 obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; 7276 } 7277 } 7278 fprintf(fp, "\n" "JSObject classes\n"); 7279 if (obj_classes[0]) 7280 fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); 7281 for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { 7282 if (obj_classes[class_id] && class_id < rt->class_count) { 7283 char buf[ATOM_GET_STR_BUF_SIZE]; 7284 fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, 7285 JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); 7286 } 7287 } 7288 if (obj_classes[JS_CLASS_INIT_COUNT]) 7289 fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); 7290 } 7291 fprintf(fp, "\n"); 7292 } 7293 #endif 7294 fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); 7295 7296 if (s->malloc_count) { 7297 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", 7298 "memory allocated", s->malloc_count, s->malloc_size, 7299 (double)s->malloc_size / s->malloc_count); 7300 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", 7301 "memory used", s->memory_used_count, s->memory_used_size, 7302 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / 7303 s->memory_used_count)); 7304 } 7305 if (s->atom_count) { 7306 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", 7307 "atoms", s->atom_count, s->atom_size, 7308 (double)s->atom_size / s->atom_count); 7309 } 7310 if (s->str_count) { 7311 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", 7312 "strings", s->str_count, s->str_size, 7313 (double)s->str_size / s->str_count); 7314 } 7315 if (s->obj_count) { 7316 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", 7317 "objects", s->obj_count, s->obj_size, 7318 (double)s->obj_size / s->obj_count); 7319 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", 7320 " properties", s->prop_count, s->prop_size, 7321 (double)s->prop_count / s->obj_count); 7322 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", 7323 " shapes", s->shape_count, s->shape_size, 7324 (double)s->shape_size / s->shape_count); 7325 } 7326 if (s->js_func_count) { 7327 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", 7328 "bytecode functions", s->js_func_count, s->js_func_size); 7329 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", 7330 " bytecode", s->js_func_count, s->js_func_code_size, 7331 (double)s->js_func_code_size / s->js_func_count); 7332 if (s->js_func_pc2line_count) { 7333 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", 7334 " pc2line", s->js_func_pc2line_count, 7335 s->js_func_pc2line_size, 7336 (double)s->js_func_pc2line_size / s->js_func_pc2line_count); 7337 } 7338 } 7339 if (s->c_func_count) { 7340 fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); 7341 } 7342 if (s->array_count) { 7343 fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); 7344 if (s->fast_array_count) { 7345 fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); 7346 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", 7347 " elements", s->fast_array_elements, 7348 s->fast_array_elements * (int)sizeof(JSValue), 7349 (double)s->fast_array_elements / s->fast_array_count); 7350 } 7351 } 7352 if (s->binary_object_count) { 7353 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", 7354 "binary objects", s->binary_object_count, s->binary_object_size); 7355 } 7356 } 7357 7358 JSValue JS_GetGlobalObject(JSContext *ctx) 7359 { 7360 return JS_DupValue(ctx, ctx->global_obj); 7361 } 7362 7363 /* WARNING: obj is freed */ 7364 JSValue JS_Throw(JSContext *ctx, JSValue obj) 7365 { 7366 JSRuntime *rt = ctx->rt; 7367 JS_FreeValue(ctx, rt->current_exception); 7368 rt->current_exception = obj; 7369 rt->current_exception_is_uncatchable = FALSE; 7370 return JS_EXCEPTION; 7371 } 7372 7373 /* return the pending exception (cannot be called twice). */ 7374 JSValue JS_GetException(JSContext *ctx) 7375 { 7376 JSValue val; 7377 JSRuntime *rt = ctx->rt; 7378 val = rt->current_exception; 7379 rt->current_exception = JS_UNINITIALIZED; 7380 return val; 7381 } 7382 7383 JS_BOOL JS_HasException(JSContext *ctx) 7384 { 7385 return !JS_IsUninitialized(ctx->rt->current_exception); 7386 } 7387 7388 static void dbuf_put_leb128(DynBuf *s, uint32_t v) 7389 { 7390 uint32_t a; 7391 for(;;) { 7392 a = v & 0x7f; 7393 v >>= 7; 7394 if (v != 0) { 7395 dbuf_putc(s, a | 0x80); 7396 } else { 7397 dbuf_putc(s, a); 7398 break; 7399 } 7400 } 7401 } 7402 7403 static void dbuf_put_sleb128(DynBuf *s, int32_t v1) 7404 { 7405 uint32_t v = v1; 7406 dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); 7407 } 7408 7409 static int get_leb128(uint32_t *pval, const uint8_t *buf, 7410 const uint8_t *buf_end) 7411 { 7412 const uint8_t *ptr = buf; 7413 uint32_t v, a, i; 7414 v = 0; 7415 for(i = 0; i < 5; i++) { 7416 if (unlikely(ptr >= buf_end)) 7417 break; 7418 a = *ptr++; 7419 v |= (a & 0x7f) << (i * 7); 7420 if (!(a & 0x80)) { 7421 *pval = v; 7422 return ptr - buf; 7423 } 7424 } 7425 *pval = 0; 7426 return -1; 7427 } 7428 7429 static int get_sleb128(int32_t *pval, const uint8_t *buf, 7430 const uint8_t *buf_end) 7431 { 7432 int ret; 7433 uint32_t val; 7434 ret = get_leb128(&val, buf, buf_end); 7435 if (ret < 0) { 7436 *pval = 0; 7437 return -1; 7438 } 7439 *pval = (val >> 1) ^ -(val & 1); 7440 return ret; 7441 } 7442 7443 /* use pc_value = -1 to get the position of the function definition */ 7444 static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, 7445 uint32_t pc_value, int *pcol_num) 7446 { 7447 const uint8_t *p_end, *p; 7448 int new_line_num, line_num, pc, v, ret, new_col_num, col_num; 7449 uint32_t val; 7450 unsigned int op; 7451 7452 if (!b->has_debug || !b->debug.pc2line_buf) 7453 goto fail; /* function was stripped */ 7454 7455 p = b->debug.pc2line_buf; 7456 p_end = p + b->debug.pc2line_len; 7457 7458 /* get the function line and column numbers */ 7459 ret = get_leb128(&val, p, p_end); 7460 if (ret < 0) 7461 goto fail; 7462 p += ret; 7463 line_num = val + 1; 7464 7465 ret = get_leb128(&val, p, p_end); 7466 if (ret < 0) 7467 goto fail; 7468 p += ret; 7469 col_num = val + 1; 7470 7471 if (pc_value != -1) { 7472 pc = 0; 7473 while (p < p_end) { 7474 op = *p++; 7475 if (op == 0) { 7476 ret = get_leb128(&val, p, p_end); 7477 if (ret < 0) 7478 goto fail; 7479 pc += val; 7480 p += ret; 7481 ret = get_sleb128(&v, p, p_end); 7482 if (ret < 0) 7483 goto fail; 7484 p += ret; 7485 new_line_num = line_num + v; 7486 } else { 7487 op -= PC2LINE_OP_FIRST; 7488 pc += (op / PC2LINE_RANGE); 7489 new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; 7490 } 7491 ret = get_sleb128(&v, p, p_end); 7492 if (ret < 0) 7493 goto fail; 7494 p += ret; 7495 new_col_num = col_num + v; 7496 7497 if (pc_value < pc) 7498 goto done; 7499 line_num = new_line_num; 7500 col_num = new_col_num; 7501 } 7502 } 7503 done: 7504 *pcol_num = col_num; 7505 return line_num; 7506 fail: 7507 *pcol_num = 0; 7508 return 0; 7509 } 7510 7511 /* return a string property without executing arbitrary JS code (used 7512 when dumping the stack trace or in debug print). */ 7513 static const char *get_prop_string(JSContext *ctx, JSValueConst obj, JSAtom prop) 7514 { 7515 JSObject *p; 7516 JSProperty *pr; 7517 JSShapeProperty *prs; 7518 JSValueConst val; 7519 7520 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 7521 return NULL; 7522 p = JS_VALUE_GET_OBJ(obj); 7523 prs = find_own_property(&pr, p, prop); 7524 if (!prs) { 7525 /* we look at one level in the prototype to handle the 'name' 7526 field of the Error objects */ 7527 p = p->shape->proto; 7528 if (!p) 7529 return NULL; 7530 prs = find_own_property(&pr, p, prop); 7531 if (!prs) 7532 return NULL; 7533 } 7534 7535 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 7536 return NULL; 7537 val = pr->u.value; 7538 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 7539 return NULL; 7540 return JS_ToCString(ctx, val); 7541 } 7542 7543 #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) 7544 7545 /* if filename != NULL, an additional level is added with the filename 7546 and line number information (used for parse error). */ 7547 static void build_backtrace(JSContext *ctx, JSValueConst error_obj, 7548 const char *filename, int line_num, int col_num, 7549 int backtrace_flags) 7550 { 7551 JSStackFrame *sf; 7552 JSValue str; 7553 DynBuf dbuf; 7554 const char *func_name_str; 7555 const char *str1; 7556 JSObject *p; 7557 7558 if (!JS_IsObject(error_obj)) 7559 return; /* protection in the out of memory case */ 7560 7561 js_dbuf_init(ctx, &dbuf); 7562 if (filename) { 7563 dbuf_printf(&dbuf, " at %s", filename); 7564 if (line_num != -1) 7565 dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); 7566 dbuf_putc(&dbuf, '\n'); 7567 str = JS_NewString(ctx, filename); 7568 if (JS_IsException(str)) 7569 return; 7570 /* Note: SpiderMonkey does that, could update once there is a standard */ 7571 if (JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, 7572 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || 7573 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), 7574 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0 || 7575 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, col_num), 7576 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) { 7577 return; 7578 } 7579 } 7580 for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { 7581 if (sf->js_mode & JS_MODE_BACKTRACE_BARRIER) 7582 break; 7583 if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { 7584 backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; 7585 continue; 7586 } 7587 func_name_str = get_prop_string(ctx, sf->cur_func, JS_ATOM_name); 7588 if (!func_name_str || func_name_str[0] == '\0') 7589 str1 = "<anonymous>"; 7590 else 7591 str1 = func_name_str; 7592 dbuf_printf(&dbuf, " at %s", str1); 7593 JS_FreeCString(ctx, func_name_str); 7594 7595 p = JS_VALUE_GET_OBJ(sf->cur_func); 7596 if (js_class_has_bytecode(p->class_id)) { 7597 JSFunctionBytecode *b; 7598 const char *atom_str; 7599 int line_num1, col_num1; 7600 7601 b = p->u.func.function_bytecode; 7602 if (b->has_debug) { 7603 line_num1 = find_line_num(ctx, b, 7604 sf->cur_pc - b->byte_code_buf - 1, &col_num1); 7605 atom_str = JS_AtomToCString(ctx, b->debug.filename); 7606 dbuf_printf(&dbuf, " (%s", 7607 atom_str ? atom_str : "<null>"); 7608 JS_FreeCString(ctx, atom_str); 7609 if (line_num1 != 0) 7610 dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); 7611 dbuf_putc(&dbuf, ')'); 7612 } 7613 } else { 7614 dbuf_printf(&dbuf, " (native)"); 7615 } 7616 dbuf_putc(&dbuf, '\n'); 7617 } 7618 dbuf_putc(&dbuf, '\0'); 7619 if (dbuf_error(&dbuf)) 7620 str = JS_NULL; 7621 else 7622 str = JS_NewString(ctx, (char *)dbuf.buf); 7623 dbuf_free(&dbuf); 7624 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, 7625 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 7626 } 7627 7628 /* Note: it is important that no exception is returned by this function */ 7629 static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) 7630 { 7631 JSObject *p; 7632 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 7633 return FALSE; 7634 p = JS_VALUE_GET_OBJ(obj); 7635 if (p->class_id != JS_CLASS_ERROR) 7636 return FALSE; 7637 if (find_own_property1(p, JS_ATOM_stack)) 7638 return FALSE; 7639 return TRUE; 7640 } 7641 7642 JSValue JS_NewError(JSContext *ctx) 7643 { 7644 return JS_NewObjectClass(ctx, JS_CLASS_ERROR); 7645 } 7646 7647 static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, 7648 const char *fmt, va_list ap, BOOL add_backtrace) 7649 { 7650 char buf[256]; 7651 JSValue obj, ret; 7652 7653 vsnprintf(buf, sizeof(buf), fmt, ap); 7654 obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], 7655 JS_CLASS_ERROR); 7656 if (unlikely(JS_IsException(obj))) { 7657 /* out of memory: throw JS_NULL to avoid recursing */ 7658 obj = JS_NULL; 7659 } else { 7660 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, 7661 JS_NewString(ctx, buf), 7662 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 7663 if (add_backtrace) { 7664 build_backtrace(ctx, obj, NULL, 0, 0, 0); 7665 } 7666 } 7667 ret = JS_Throw(ctx, obj); 7668 return ret; 7669 } 7670 7671 static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, 7672 const char *fmt, va_list ap) 7673 { 7674 JSRuntime *rt = ctx->rt; 7675 JSStackFrame *sf; 7676 BOOL add_backtrace; 7677 7678 /* the backtrace is added later if called from a bytecode function */ 7679 sf = rt->current_stack_frame; 7680 add_backtrace = !rt->in_out_of_memory && 7681 (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); 7682 return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); 7683 } 7684 7685 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) 7686 { 7687 JSValue val; 7688 va_list ap; 7689 7690 va_start(ap, fmt); 7691 val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); 7692 va_end(ap); 7693 return val; 7694 } 7695 7696 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) 7697 { 7698 JSValue val; 7699 va_list ap; 7700 7701 va_start(ap, fmt); 7702 val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); 7703 va_end(ap); 7704 return val; 7705 } 7706 7707 static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) 7708 { 7709 va_list ap; 7710 7711 if ((flags & JS_PROP_THROW) || 7712 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 7713 va_start(ap, fmt); 7714 JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); 7715 va_end(ap); 7716 return -1; 7717 } else { 7718 return FALSE; 7719 } 7720 } 7721 7722 /* never use it directly */ 7723 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) 7724 { 7725 char buf[ATOM_GET_STR_BUF_SIZE]; 7726 return JS_ThrowTypeError(ctx, fmt, 7727 JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); 7728 } 7729 7730 /* never use it directly */ 7731 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) 7732 { 7733 char buf[ATOM_GET_STR_BUF_SIZE]; 7734 return JS_ThrowSyntaxError(ctx, fmt, 7735 JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); 7736 } 7737 7738 /* %s is replaced by 'atom'. The macro is used so that gcc can check 7739 the format string. */ 7740 #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") 7741 #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") 7742 7743 static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) 7744 { 7745 if ((flags & JS_PROP_THROW) || 7746 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 7747 JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); 7748 return -1; 7749 } else { 7750 return FALSE; 7751 } 7752 } 7753 7754 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) 7755 { 7756 JSValue val; 7757 va_list ap; 7758 7759 va_start(ap, fmt); 7760 val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); 7761 va_end(ap); 7762 return val; 7763 } 7764 7765 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) 7766 { 7767 JSValue val; 7768 va_list ap; 7769 7770 va_start(ap, fmt); 7771 val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); 7772 va_end(ap); 7773 return val; 7774 } 7775 7776 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) 7777 { 7778 JSValue val; 7779 va_list ap; 7780 7781 va_start(ap, fmt); 7782 val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); 7783 va_end(ap); 7784 return val; 7785 } 7786 7787 JSValue JS_ThrowOutOfMemory(JSContext *ctx) 7788 { 7789 JSRuntime *rt = ctx->rt; 7790 if (!rt->in_out_of_memory) { 7791 rt->in_out_of_memory = TRUE; 7792 JS_ThrowInternalError(ctx, "out of memory"); 7793 rt->in_out_of_memory = FALSE; 7794 } 7795 return JS_EXCEPTION; 7796 } 7797 7798 static JSValue JS_ThrowStackOverflow(JSContext *ctx) 7799 { 7800 return JS_ThrowInternalError(ctx, "stack overflow"); 7801 } 7802 7803 static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) 7804 { 7805 return JS_ThrowTypeError(ctx, "not an object"); 7806 } 7807 7808 static JSValue JS_ThrowTypeErrorNotAConstructor(JSContext *ctx, 7809 JSValueConst func_obj) 7810 { 7811 const char *name; 7812 if (!JS_IsFunction(ctx, func_obj)) 7813 goto fail; 7814 name = get_prop_string(ctx, func_obj, JS_ATOM_name); 7815 if (!name) { 7816 fail: 7817 return JS_ThrowTypeError(ctx, "not a constructor"); 7818 } 7819 JS_ThrowTypeError(ctx, "%s is not a constructor", name); 7820 JS_FreeCString(ctx, name); 7821 return JS_EXCEPTION; 7822 } 7823 7824 static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) 7825 { 7826 return JS_ThrowTypeError(ctx, "not a symbol"); 7827 } 7828 7829 static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) 7830 { 7831 char buf[ATOM_GET_STR_BUF_SIZE]; 7832 return JS_ThrowReferenceError(ctx, "'%s' is not defined", 7833 JS_AtomGetStr(ctx, buf, sizeof(buf), name)); 7834 } 7835 7836 static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) 7837 { 7838 char buf[ATOM_GET_STR_BUF_SIZE]; 7839 return JS_ThrowReferenceError(ctx, "%s is not initialized", 7840 name == JS_ATOM_NULL ? "lexical variable" : 7841 JS_AtomGetStr(ctx, buf, sizeof(buf), name)); 7842 } 7843 7844 static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, 7845 JSFunctionBytecode *b, 7846 int idx, BOOL is_ref) 7847 { 7848 JSAtom atom = JS_ATOM_NULL; 7849 if (is_ref) { 7850 atom = b->closure_var[idx].var_name; 7851 } else { 7852 /* not present if the function is stripped and contains no eval() */ 7853 if (b->vardefs) 7854 atom = b->vardefs[b->arg_count + idx].var_name; 7855 } 7856 return JS_ThrowReferenceErrorUninitialized(ctx, atom); 7857 } 7858 7859 static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) 7860 { 7861 JSRuntime *rt = ctx->rt; 7862 JSAtom name; 7863 name = rt->class_array[class_id].class_name; 7864 return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); 7865 } 7866 7867 static void JS_ThrowInterrupted(JSContext *ctx) 7868 { 7869 JS_ThrowInternalError(ctx, "interrupted"); 7870 JS_SetUncatchableException(ctx, TRUE); 7871 } 7872 7873 static no_inline __exception int __js_poll_interrupts(JSContext *ctx) 7874 { 7875 JSRuntime *rt = ctx->rt; 7876 ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; 7877 if (rt->interrupt_handler) { 7878 if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { 7879 JS_ThrowInterrupted(ctx); 7880 return -1; 7881 } 7882 } 7883 return 0; 7884 } 7885 7886 static inline __exception int js_poll_interrupts(JSContext *ctx) 7887 { 7888 if (unlikely(--ctx->interrupt_counter <= 0)) { 7889 return __js_poll_interrupts(ctx); 7890 } else { 7891 return 0; 7892 } 7893 } 7894 7895 static void JS_SetImmutablePrototype(JSContext *ctx, JSValueConst obj) 7896 { 7897 JSObject *p; 7898 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 7899 return; 7900 p = JS_VALUE_GET_OBJ(obj); 7901 p->has_immutable_prototype = TRUE; 7902 } 7903 7904 /* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates 7905 that it is called from Reflect.setPrototypeOf(). */ 7906 static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, 7907 JSValueConst proto_val, 7908 BOOL throw_flag) 7909 { 7910 JSObject *proto, *p, *p1; 7911 JSShape *sh; 7912 7913 if (throw_flag) { 7914 if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || 7915 JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) 7916 goto not_obj; 7917 } else { 7918 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 7919 goto not_obj; 7920 } 7921 p = JS_VALUE_GET_OBJ(obj); 7922 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { 7923 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { 7924 not_obj: 7925 JS_ThrowTypeErrorNotAnObject(ctx); 7926 return -1; 7927 } 7928 proto = NULL; 7929 } else { 7930 proto = JS_VALUE_GET_OBJ(proto_val); 7931 } 7932 7933 if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 7934 return TRUE; 7935 7936 if (unlikely(p->is_exotic)) { 7937 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 7938 int ret; 7939 if (em && em->set_prototype) { 7940 ret = em->set_prototype(ctx, obj, proto_val); 7941 if (ret == 0 && throw_flag) { 7942 JS_ThrowTypeError(ctx, "proxy: bad prototype"); 7943 return -1; 7944 } else { 7945 return ret; 7946 } 7947 } 7948 } 7949 7950 sh = p->shape; 7951 if (sh->proto == proto) 7952 return TRUE; 7953 if (unlikely(p->has_immutable_prototype)) { 7954 if (throw_flag) { 7955 JS_ThrowTypeError(ctx, "prototype is immutable"); 7956 return -1; 7957 } else { 7958 return FALSE; 7959 } 7960 } 7961 if (unlikely(!p->extensible)) { 7962 if (throw_flag) { 7963 JS_ThrowTypeError(ctx, "object is not extensible"); 7964 return -1; 7965 } else { 7966 return FALSE; 7967 } 7968 } 7969 if (proto) { 7970 /* check if there is a cycle */ 7971 p1 = proto; 7972 do { 7973 if (p1 == p) { 7974 if (throw_flag) { 7975 JS_ThrowTypeError(ctx, "circular prototype chain"); 7976 return -1; 7977 } else { 7978 return FALSE; 7979 } 7980 } 7981 /* Note: for Proxy objects, proto is NULL */ 7982 p1 = p1->shape->proto; 7983 } while (p1 != NULL); 7984 JS_DupValue(ctx, proto_val); 7985 } 7986 7987 if (js_shape_prepare_update(ctx, p, NULL)) 7988 return -1; 7989 sh = p->shape; 7990 if (sh->proto) 7991 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 7992 sh->proto = proto; 7993 p->is_std_array_prototype = FALSE; 7994 return TRUE; 7995 } 7996 7997 /* return -1 (exception) or TRUE/FALSE */ 7998 int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) 7999 { 8000 return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); 8001 } 8002 8003 /* Only works for primitive types, otherwise return JS_NULL. */ 8004 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) 8005 { 8006 switch(JS_VALUE_GET_NORM_TAG(val)) { 8007 case JS_TAG_SHORT_BIG_INT: 8008 case JS_TAG_BIG_INT: 8009 val = ctx->class_proto[JS_CLASS_BIG_INT]; 8010 break; 8011 case JS_TAG_INT: 8012 case JS_TAG_FLOAT64: 8013 val = ctx->class_proto[JS_CLASS_NUMBER]; 8014 break; 8015 case JS_TAG_BOOL: 8016 val = ctx->class_proto[JS_CLASS_BOOLEAN]; 8017 break; 8018 case JS_TAG_STRING: 8019 case JS_TAG_STRING_ROPE: 8020 val = ctx->class_proto[JS_CLASS_STRING]; 8021 break; 8022 case JS_TAG_SYMBOL: 8023 val = ctx->class_proto[JS_CLASS_SYMBOL]; 8024 break; 8025 case JS_TAG_OBJECT: 8026 case JS_TAG_NULL: 8027 case JS_TAG_UNDEFINED: 8028 default: 8029 val = JS_NULL; 8030 break; 8031 } 8032 return val; 8033 } 8034 8035 /* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */ 8036 JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) 8037 { 8038 JSValue val; 8039 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 8040 JSObject *p; 8041 p = JS_VALUE_GET_OBJ(obj); 8042 if (unlikely(p->is_exotic)) { 8043 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8044 if (em && em->get_prototype) { 8045 return em->get_prototype(ctx, obj); 8046 } 8047 } 8048 p = p->shape->proto; 8049 if (!p) 8050 val = JS_NULL; 8051 else 8052 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8053 } else { 8054 val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); 8055 } 8056 return val; 8057 } 8058 8059 static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) 8060 { 8061 JSValue obj1; 8062 obj1 = JS_GetPrototype(ctx, obj); 8063 JS_FreeValue(ctx, obj); 8064 return obj1; 8065 } 8066 8067 /* return TRUE, FALSE or (-1) in case of exception */ 8068 static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, 8069 JSValueConst obj) 8070 { 8071 JSValue obj_proto; 8072 JSObject *proto; 8073 const JSObject *p, *proto1; 8074 BOOL ret; 8075 8076 if (!JS_IsFunction(ctx, obj)) 8077 return FALSE; 8078 p = JS_VALUE_GET_OBJ(obj); 8079 if (p->class_id == JS_CLASS_BOUND_FUNCTION) { 8080 JSBoundFunction *s = p->u.bound_function; 8081 return JS_IsInstanceOf(ctx, val, s->func_obj); 8082 } 8083 8084 /* Only explicitly boxed values are instances of constructors */ 8085 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 8086 return FALSE; 8087 obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); 8088 if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { 8089 if (!JS_IsException(obj_proto)) 8090 JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); 8091 ret = -1; 8092 goto done; 8093 } 8094 proto = JS_VALUE_GET_OBJ(obj_proto); 8095 p = JS_VALUE_GET_OBJ(val); 8096 for(;;) { 8097 proto1 = p->shape->proto; 8098 if (!proto1) { 8099 /* slow case if exotic object in the prototype chain */ 8100 if (unlikely(p->is_exotic && !p->fast_array)) { 8101 JSValue obj1; 8102 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); 8103 for(;;) { 8104 obj1 = JS_GetPrototypeFree(ctx, obj1); 8105 if (JS_IsException(obj1)) { 8106 ret = -1; 8107 break; 8108 } 8109 if (JS_IsNull(obj1)) { 8110 ret = FALSE; 8111 break; 8112 } 8113 if (proto == JS_VALUE_GET_OBJ(obj1)) { 8114 JS_FreeValue(ctx, obj1); 8115 ret = TRUE; 8116 break; 8117 } 8118 /* must check for timeout to avoid infinite loop */ 8119 if (js_poll_interrupts(ctx)) { 8120 JS_FreeValue(ctx, obj1); 8121 ret = -1; 8122 break; 8123 } 8124 } 8125 } else { 8126 ret = FALSE; 8127 } 8128 break; 8129 } 8130 p = proto1; 8131 if (proto == p) { 8132 ret = TRUE; 8133 break; 8134 } 8135 } 8136 done: 8137 JS_FreeValue(ctx, obj_proto); 8138 return ret; 8139 } 8140 8141 /* return TRUE, FALSE or (-1) in case of exception */ 8142 int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) 8143 { 8144 JSValue method; 8145 8146 if (!JS_IsObject(obj)) 8147 goto fail; 8148 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); 8149 if (JS_IsException(method)) 8150 return -1; 8151 if (!JS_IsNull(method) && !JS_IsUndefined(method)) { 8152 JSValue ret; 8153 ret = JS_CallFree(ctx, method, obj, 1, &val); 8154 return JS_ToBoolFree(ctx, ret); 8155 } 8156 8157 /* legacy case */ 8158 if (!JS_IsFunction(ctx, obj)) { 8159 fail: 8160 JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); 8161 return -1; 8162 } 8163 return JS_OrdinaryIsInstanceOf(ctx, val, obj); 8164 } 8165 8166 /* return the value associated to the autoinit property or an exception */ 8167 typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); 8168 8169 static JSAutoInitFunc *js_autoinit_func_table[] = { 8170 js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ 8171 js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ 8172 JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ 8173 }; 8174 8175 /* warning: 'prs' is reallocated after it */ 8176 static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, 8177 JSProperty *pr, JSShapeProperty *prs) 8178 { 8179 JSValue val; 8180 JSContext *realm; 8181 JSAutoInitFunc *func; 8182 JSAutoInitIDEnum id; 8183 8184 if (js_shape_prepare_update(ctx, p, &prs)) 8185 return -1; 8186 8187 realm = js_autoinit_get_realm(pr); 8188 id = js_autoinit_get_id(pr); 8189 func = js_autoinit_func_table[id]; 8190 /* 'func' shall not modify the object properties 'pr' */ 8191 val = func(realm, p, prop, pr->u.init.opaque); 8192 js_autoinit_free(ctx->rt, pr); 8193 prs->flags &= ~JS_PROP_TMASK; 8194 pr->u.value = JS_UNDEFINED; 8195 if (JS_IsException(val)) 8196 return -1; 8197 if (id == JS_AUTOINIT_ID_MODULE_NS && 8198 JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 8199 /* WARNING: a varref is returned as a string ! */ 8200 prs->flags |= JS_PROP_VARREF; 8201 pr->u.var_ref = JS_VALUE_GET_PTR(val); 8202 js_rc(pr->u.var_ref)->ref_count++; 8203 } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { 8204 JSVarRef *var_ref; 8205 /* in the global object we use references */ 8206 var_ref = js_create_var_ref(ctx, FALSE); 8207 if (!var_ref) 8208 return -1; 8209 prs->flags |= JS_PROP_VARREF; 8210 pr->u.var_ref = var_ref; 8211 var_ref->value = val; 8212 var_ref->is_const = !(prs->flags & JS_PROP_WRITABLE); 8213 } else { 8214 pr->u.value = val; 8215 } 8216 return 0; 8217 } 8218 8219 JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 8220 JSAtom prop, JSValueConst this_obj, 8221 BOOL throw_ref_error) 8222 { 8223 JSObject *p; 8224 JSProperty *pr; 8225 JSShapeProperty *prs; 8226 uint32_t tag; 8227 8228 tag = JS_VALUE_GET_TAG(obj); 8229 if (unlikely(tag != JS_TAG_OBJECT)) { 8230 switch(tag) { 8231 case JS_TAG_NULL: 8232 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); 8233 case JS_TAG_UNDEFINED: 8234 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); 8235 case JS_TAG_EXCEPTION: 8236 return JS_EXCEPTION; 8237 case JS_TAG_STRING: 8238 { 8239 JSString *p1 = JS_VALUE_GET_STRING(obj); 8240 if (__JS_AtomIsTaggedInt(prop)) { 8241 uint32_t idx; 8242 idx = __JS_AtomToUInt32(prop); 8243 if (idx < p1->len) { 8244 return js_new_string_char(ctx, string_get(p1, idx)); 8245 } 8246 } else if (prop == JS_ATOM_length) { 8247 return JS_NewInt32(ctx, p1->len); 8248 } 8249 } 8250 break; 8251 case JS_TAG_STRING_ROPE: 8252 { 8253 JSStringRope *p1 = JS_VALUE_GET_STRING_ROPE(obj); 8254 if (__JS_AtomIsTaggedInt(prop)) { 8255 uint32_t idx; 8256 idx = __JS_AtomToUInt32(prop); 8257 if (idx < p1->len) { 8258 return js_new_string_char(ctx, string_rope_get(obj, idx)); 8259 } 8260 } else if (prop == JS_ATOM_length) { 8261 return JS_NewInt32(ctx, p1->len); 8262 } 8263 } 8264 break; 8265 default: 8266 break; 8267 } 8268 /* cannot raise an exception */ 8269 p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); 8270 if (!p) 8271 return JS_UNDEFINED; 8272 } else { 8273 p = JS_VALUE_GET_OBJ(obj); 8274 } 8275 8276 for(;;) { 8277 prs = find_own_property(&pr, p, prop); 8278 if (prs) { 8279 /* found */ 8280 if (unlikely(prs->flags & JS_PROP_TMASK)) { 8281 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 8282 if (unlikely(!pr->u.getset.getter)) { 8283 return JS_UNDEFINED; 8284 } else { 8285 JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); 8286 /* Note: the field could be removed in the getter */ 8287 func = JS_DupValue(ctx, func); 8288 return JS_CallFree(ctx, func, this_obj, 0, NULL); 8289 } 8290 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 8291 JSValue val = *pr->u.var_ref->pvalue; 8292 if (unlikely(JS_IsUninitialized(val))) 8293 return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 8294 return JS_DupValue(ctx, val); 8295 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 8296 /* Instantiate property and retry */ 8297 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 8298 return JS_EXCEPTION; 8299 continue; 8300 } 8301 } else { 8302 return JS_DupValue(ctx, pr->u.value); 8303 } 8304 } 8305 if (unlikely(p->is_exotic)) { 8306 /* exotic behaviors */ 8307 if (p->fast_array) { 8308 if (__JS_AtomIsTaggedInt(prop)) { 8309 uint32_t idx = __JS_AtomToUInt32(prop); 8310 if (idx < p->u.array.count) { 8311 /* we avoid duplicating the code */ 8312 return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); 8313 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 8314 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8315 return JS_UNDEFINED; 8316 } 8317 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 8318 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8319 int ret; 8320 ret = JS_AtomIsNumericIndex(ctx, prop); 8321 if (ret != 0) { 8322 if (ret < 0) 8323 return JS_EXCEPTION; 8324 return JS_UNDEFINED; 8325 } 8326 } 8327 } else { 8328 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8329 if (em) { 8330 if (em->get_property) { 8331 JSValue obj1, retval; 8332 /* XXX: should pass throw_ref_error */ 8333 /* Note: if 'p' is a prototype, it can be 8334 freed in the called function */ 8335 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8336 retval = em->get_property(ctx, obj1, prop, this_obj); 8337 JS_FreeValue(ctx, obj1); 8338 return retval; 8339 } 8340 if (em->get_own_property) { 8341 JSPropertyDescriptor desc; 8342 int ret; 8343 JSValue obj1; 8344 8345 /* Note: if 'p' is a prototype, it can be 8346 freed in the called function */ 8347 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8348 ret = em->get_own_property(ctx, &desc, obj1, prop); 8349 JS_FreeValue(ctx, obj1); 8350 if (ret < 0) 8351 return JS_EXCEPTION; 8352 if (ret) { 8353 if (desc.flags & JS_PROP_GETSET) { 8354 JS_FreeValue(ctx, desc.setter); 8355 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); 8356 } else { 8357 return desc.value; 8358 } 8359 } 8360 } 8361 } 8362 } 8363 } 8364 p = p->shape->proto; 8365 if (!p) 8366 break; 8367 } 8368 if (unlikely(throw_ref_error)) { 8369 return JS_ThrowReferenceErrorNotDefined(ctx, prop); 8370 } else { 8371 return JS_UNDEFINED; 8372 } 8373 } 8374 8375 static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) 8376 { 8377 return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", 8378 atom); 8379 } 8380 8381 /* Private fields can be added even on non extensible objects or 8382 Proxies */ 8383 static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, 8384 JSValueConst name, JSValue val) 8385 { 8386 JSObject *p; 8387 JSShapeProperty *prs; 8388 JSProperty *pr; 8389 JSAtom prop; 8390 8391 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 8392 JS_ThrowTypeErrorNotAnObject(ctx); 8393 goto fail; 8394 } 8395 /* safety check */ 8396 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { 8397 JS_ThrowTypeErrorNotASymbol(ctx); 8398 goto fail; 8399 } 8400 prop = js_symbol_to_atom(ctx, (JSValue)name); 8401 p = JS_VALUE_GET_OBJ(obj); 8402 prs = find_own_property(&pr, p, prop); 8403 if (prs) { 8404 JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", 8405 prop); 8406 goto fail; 8407 } 8408 pr = add_property(ctx, p, prop, JS_PROP_C_W_E); 8409 if (unlikely(!pr)) { 8410 fail: 8411 JS_FreeValue(ctx, val); 8412 return -1; 8413 } 8414 pr->u.value = val; 8415 return 0; 8416 } 8417 8418 static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, 8419 JSValueConst name) 8420 { 8421 JSObject *p; 8422 JSShapeProperty *prs; 8423 JSProperty *pr; 8424 JSAtom prop; 8425 8426 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 8427 return JS_ThrowTypeErrorNotAnObject(ctx); 8428 /* safety check */ 8429 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) 8430 return JS_ThrowTypeErrorNotASymbol(ctx); 8431 prop = js_symbol_to_atom(ctx, (JSValue)name); 8432 p = JS_VALUE_GET_OBJ(obj); 8433 prs = find_own_property(&pr, p, prop); 8434 if (!prs) { 8435 JS_ThrowTypeErrorPrivateNotFound(ctx, prop); 8436 return JS_EXCEPTION; 8437 } 8438 return JS_DupValue(ctx, pr->u.value); 8439 } 8440 8441 static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, 8442 JSValueConst name, JSValue val) 8443 { 8444 JSObject *p; 8445 JSShapeProperty *prs; 8446 JSProperty *pr; 8447 JSAtom prop; 8448 8449 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 8450 JS_ThrowTypeErrorNotAnObject(ctx); 8451 goto fail; 8452 } 8453 /* safety check */ 8454 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { 8455 JS_ThrowTypeErrorNotASymbol(ctx); 8456 goto fail; 8457 } 8458 prop = js_symbol_to_atom(ctx, (JSValue)name); 8459 p = JS_VALUE_GET_OBJ(obj); 8460 prs = find_own_property(&pr, p, prop); 8461 if (!prs) { 8462 JS_ThrowTypeErrorPrivateNotFound(ctx, prop); 8463 fail: 8464 JS_FreeValue(ctx, val); 8465 return -1; 8466 } 8467 set_value(ctx, &pr->u.value, val); 8468 return 0; 8469 } 8470 8471 /* add a private brand field to 'home_obj' if not already present and 8472 if obj is != null add a private brand to it */ 8473 static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) 8474 { 8475 JSObject *p, *p1; 8476 JSShapeProperty *prs; 8477 JSProperty *pr; 8478 JSValue brand; 8479 JSAtom brand_atom; 8480 8481 if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { 8482 JS_ThrowTypeErrorNotAnObject(ctx); 8483 return -1; 8484 } 8485 p = JS_VALUE_GET_OBJ(home_obj); 8486 prs = find_own_property(&pr, p, JS_ATOM_Private_brand); 8487 if (!prs) { 8488 /* if the brand is not present, add it */ 8489 brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); 8490 if (JS_IsException(brand)) 8491 return -1; 8492 pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); 8493 if (!pr) { 8494 JS_FreeValue(ctx, brand); 8495 return -1; 8496 } 8497 pr->u.value = JS_DupValue(ctx, brand); 8498 } else { 8499 brand = JS_DupValue(ctx, pr->u.value); 8500 } 8501 brand_atom = js_symbol_to_atom(ctx, brand); 8502 8503 if (JS_IsObject(obj)) { 8504 p1 = JS_VALUE_GET_OBJ(obj); 8505 prs = find_own_property(&pr, p1, brand_atom); 8506 if (unlikely(prs)) { 8507 JS_FreeAtom(ctx, brand_atom); 8508 JS_ThrowTypeError(ctx, "private method is already present"); 8509 return -1; 8510 } 8511 pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); 8512 JS_FreeAtom(ctx, brand_atom); 8513 if (!pr) 8514 return -1; 8515 pr->u.value = JS_UNDEFINED; 8516 } else { 8517 JS_FreeAtom(ctx, brand_atom); 8518 } 8519 return 0; 8520 } 8521 8522 /* return a boolean telling if the brand of the home object of 'func' 8523 is present on 'obj' or -1 in case of exception */ 8524 static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) 8525 { 8526 JSObject *p, *p1, *home_obj; 8527 JSShapeProperty *prs; 8528 JSProperty *pr; 8529 JSValueConst brand; 8530 8531 /* get the home object of 'func' */ 8532 if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) 8533 goto not_obj; 8534 p1 = JS_VALUE_GET_OBJ(func); 8535 if (!js_class_has_bytecode(p1->class_id)) 8536 goto not_obj; 8537 home_obj = p1->u.func.home_object; 8538 if (!home_obj) 8539 goto not_obj; 8540 prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); 8541 if (!prs) { 8542 JS_ThrowTypeError(ctx, "expecting <brand> private field"); 8543 return -1; 8544 } 8545 brand = pr->u.value; 8546 /* safety check */ 8547 if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) 8548 goto not_obj; 8549 8550 /* get the brand array of 'obj' */ 8551 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 8552 not_obj: 8553 JS_ThrowTypeErrorNotAnObject(ctx); 8554 return -1; 8555 } 8556 p = JS_VALUE_GET_OBJ(obj); 8557 prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); 8558 return (prs != NULL); 8559 } 8560 8561 static uint32_t js_string_obj_get_length(JSContext *ctx, 8562 JSValueConst obj) 8563 { 8564 JSObject *p; 8565 uint32_t len = 0; 8566 8567 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ 8568 p = JS_VALUE_GET_OBJ(obj); 8569 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { 8570 JSString *p1 = JS_VALUE_GET_STRING(p->u.object_data); 8571 len = p1->len; 8572 } 8573 return len; 8574 } 8575 8576 static int num_keys_cmp(const void *p1, const void *p2, void *opaque) 8577 { 8578 JSContext *ctx = opaque; 8579 JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; 8580 JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; 8581 uint32_t v1, v2; 8582 BOOL atom1_is_integer, atom2_is_integer; 8583 8584 atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); 8585 atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); 8586 assert(atom1_is_integer && atom2_is_integer); 8587 if (v1 < v2) 8588 return -1; 8589 else if (v1 == v2) 8590 return 0; 8591 else 8592 return 1; 8593 } 8594 8595 void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) 8596 { 8597 uint32_t i; 8598 if (tab) { 8599 for(i = 0; i < len; i++) 8600 JS_FreeAtom(ctx, tab[i].atom); 8601 js_free(ctx, tab); 8602 } 8603 } 8604 8605 /* return < 0 in case if exception, 0 if OK. ptab and its atoms must 8606 be freed by the user. */ 8607 static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 8608 JSPropertyEnum **ptab, 8609 uint32_t *plen, 8610 JSObject *p, int flags) 8611 { 8612 int i, j; 8613 JSShape *sh; 8614 JSShapeProperty *prs; 8615 JSPropertyEnum *tab_atom, *tab_exotic; 8616 JSAtom atom; 8617 uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; 8618 uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; 8619 BOOL is_enumerable, num_sorted; 8620 uint32_t num_key; 8621 JSAtomKindEnum kind; 8622 8623 /* clear pointer for consistency in case of failure */ 8624 *ptab = NULL; 8625 *plen = 0; 8626 8627 /* compute the number of returned properties */ 8628 num_keys_count = 0; 8629 str_keys_count = 0; 8630 sym_keys_count = 0; 8631 exotic_keys_count = 0; 8632 exotic_count = 0; 8633 tab_exotic = NULL; 8634 sh = p->shape; 8635 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 8636 atom = prs->atom; 8637 if (atom != JS_ATOM_NULL) { 8638 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); 8639 kind = JS_AtomGetKind(ctx, atom); 8640 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 8641 ((flags >> kind) & 1) != 0) { 8642 /* need to raise an exception in case of the module 8643 name space (implicit GetOwnProperty) */ 8644 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && 8645 (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { 8646 JSVarRef *var_ref = p->prop[i].u.var_ref; 8647 if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { 8648 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 8649 return -1; 8650 } 8651 } 8652 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { 8653 num_keys_count++; 8654 } else if (kind == JS_ATOM_KIND_STRING) { 8655 str_keys_count++; 8656 } else { 8657 sym_keys_count++; 8658 } 8659 } 8660 } 8661 } 8662 8663 if (p->is_exotic) { 8664 if (p->fast_array) { 8665 if (flags & JS_GPN_STRING_MASK) { 8666 num_keys_count += p->u.array.count; 8667 } 8668 } else if (p->class_id == JS_CLASS_STRING) { 8669 if (flags & JS_GPN_STRING_MASK) { 8670 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8671 } 8672 } else { 8673 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8674 if (em && em->get_own_property_names) { 8675 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, 8676 JS_MKPTR(JS_TAG_OBJECT, p))) 8677 return -1; 8678 for(i = 0; i < exotic_count; i++) { 8679 atom = tab_exotic[i].atom; 8680 kind = JS_AtomGetKind(ctx, atom); 8681 if (((flags >> kind) & 1) != 0) { 8682 is_enumerable = FALSE; 8683 if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { 8684 JSPropertyDescriptor desc; 8685 int res; 8686 /* set the "is_enumerable" field if necessary */ 8687 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 8688 if (res < 0) { 8689 JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); 8690 return -1; 8691 } 8692 if (res) { 8693 is_enumerable = 8694 ((desc.flags & JS_PROP_ENUMERABLE) != 0); 8695 js_free_desc(ctx, &desc); 8696 } 8697 tab_exotic[i].is_enumerable = is_enumerable; 8698 } 8699 if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { 8700 exotic_keys_count++; 8701 } 8702 } 8703 } 8704 } 8705 } 8706 } 8707 8708 /* fill them */ 8709 8710 atom_count = num_keys_count + str_keys_count; 8711 if (atom_count < str_keys_count) 8712 goto add_overflow; 8713 atom_count += sym_keys_count; 8714 if (atom_count < sym_keys_count) 8715 goto add_overflow; 8716 atom_count += exotic_keys_count; 8717 if (atom_count < exotic_keys_count || atom_count > INT32_MAX) { 8718 add_overflow: 8719 JS_ThrowOutOfMemory(ctx); 8720 JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); 8721 return -1; 8722 } 8723 /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */ 8724 8725 /* avoid allocating 0 bytes */ 8726 tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); 8727 if (!tab_atom) { 8728 JS_FreePropertyEnum(ctx, tab_exotic, exotic_count); 8729 return -1; 8730 } 8731 8732 num_index = 0; 8733 str_index = num_keys_count; 8734 sym_index = str_index + str_keys_count; 8735 8736 num_sorted = TRUE; 8737 sh = p->shape; 8738 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 8739 atom = prs->atom; 8740 if (atom != JS_ATOM_NULL) { 8741 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); 8742 kind = JS_AtomGetKind(ctx, atom); 8743 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 8744 ((flags >> kind) & 1) != 0) { 8745 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { 8746 j = num_index++; 8747 num_sorted = FALSE; 8748 } else if (kind == JS_ATOM_KIND_STRING) { 8749 j = str_index++; 8750 } else { 8751 j = sym_index++; 8752 } 8753 tab_atom[j].atom = JS_DupAtom(ctx, atom); 8754 tab_atom[j].is_enumerable = is_enumerable; 8755 } 8756 } 8757 } 8758 8759 if (p->is_exotic) { 8760 int len; 8761 if (p->fast_array) { 8762 if (flags & JS_GPN_STRING_MASK) { 8763 len = p->u.array.count; 8764 goto add_array_keys; 8765 } 8766 } else if (p->class_id == JS_CLASS_STRING) { 8767 if (flags & JS_GPN_STRING_MASK) { 8768 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8769 add_array_keys: 8770 for(i = 0; i < len; i++) { 8771 tab_atom[num_index].atom = __JS_AtomFromUInt32(i); 8772 if (tab_atom[num_index].atom == JS_ATOM_NULL) { 8773 JS_FreePropertyEnum(ctx, tab_atom, num_index); 8774 return -1; 8775 } 8776 tab_atom[num_index].is_enumerable = TRUE; 8777 num_index++; 8778 } 8779 } 8780 } else { 8781 /* Note: exotic keys are not reordered and comes after the object own properties. */ 8782 for(i = 0; i < exotic_count; i++) { 8783 atom = tab_exotic[i].atom; 8784 is_enumerable = tab_exotic[i].is_enumerable; 8785 kind = JS_AtomGetKind(ctx, atom); 8786 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 8787 ((flags >> kind) & 1) != 0) { 8788 tab_atom[sym_index].atom = atom; 8789 tab_atom[sym_index].is_enumerable = is_enumerable; 8790 sym_index++; 8791 } else { 8792 JS_FreeAtom(ctx, atom); 8793 } 8794 } 8795 js_free(ctx, tab_exotic); 8796 } 8797 } 8798 8799 assert(num_index == num_keys_count); 8800 assert(str_index == num_keys_count + str_keys_count); 8801 assert(sym_index == atom_count); 8802 8803 if (num_keys_count != 0 && !num_sorted) { 8804 rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, 8805 ctx); 8806 } 8807 *ptab = tab_atom; 8808 *plen = atom_count; 8809 return 0; 8810 } 8811 8812 int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, 8813 uint32_t *plen, JSValueConst obj, int flags) 8814 { 8815 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 8816 JS_ThrowTypeErrorNotAnObject(ctx); 8817 return -1; 8818 } 8819 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, 8820 JS_VALUE_GET_OBJ(obj), flags); 8821 } 8822 8823 /* Return -1 if exception, 8824 FALSE if the property does not exist, TRUE if it exists. If TRUE is 8825 returned, the property descriptor 'desc' is filled present. */ 8826 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, 8827 JSObject *p, JSAtom prop) 8828 { 8829 JSShapeProperty *prs; 8830 JSProperty *pr; 8831 8832 retry: 8833 prs = find_own_property(&pr, p, prop); 8834 if (prs) { 8835 if (desc) { 8836 desc->flags = prs->flags & JS_PROP_C_W_E; 8837 desc->getter = JS_UNDEFINED; 8838 desc->setter = JS_UNDEFINED; 8839 desc->value = JS_UNDEFINED; 8840 if (unlikely(prs->flags & JS_PROP_TMASK)) { 8841 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 8842 desc->flags |= JS_PROP_GETSET; 8843 if (pr->u.getset.getter) 8844 desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 8845 if (pr->u.getset.setter) 8846 desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 8847 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 8848 JSValue val = *pr->u.var_ref->pvalue; 8849 if (unlikely(JS_IsUninitialized(val))) { 8850 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 8851 return -1; 8852 } 8853 desc->value = JS_DupValue(ctx, val); 8854 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 8855 /* Instantiate property and retry */ 8856 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 8857 return -1; 8858 goto retry; 8859 } 8860 } else { 8861 desc->value = JS_DupValue(ctx, pr->u.value); 8862 } 8863 } else { 8864 /* for consistency, send the exception even if desc is NULL */ 8865 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { 8866 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { 8867 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 8868 return -1; 8869 } 8870 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 8871 /* nothing to do: delay instantiation until actual value and/or attributes are read */ 8872 } 8873 } 8874 return TRUE; 8875 } 8876 if (p->is_exotic) { 8877 if (p->fast_array) { 8878 /* specific case for fast arrays */ 8879 if (__JS_AtomIsTaggedInt(prop)) { 8880 uint32_t idx; 8881 idx = __JS_AtomToUInt32(prop); 8882 if (idx < p->u.array.count) { 8883 if (desc) { 8884 desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | 8885 JS_PROP_CONFIGURABLE; 8886 desc->getter = JS_UNDEFINED; 8887 desc->setter = JS_UNDEFINED; 8888 desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); 8889 } 8890 return TRUE; 8891 } 8892 } 8893 } else { 8894 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8895 if (em && em->get_own_property) { 8896 return em->get_own_property(ctx, desc, 8897 JS_MKPTR(JS_TAG_OBJECT, p), prop); 8898 } 8899 } 8900 } 8901 return FALSE; 8902 } 8903 8904 int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, 8905 JSValueConst obj, JSAtom prop) 8906 { 8907 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 8908 JS_ThrowTypeErrorNotAnObject(ctx); 8909 return -1; 8910 } 8911 return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); 8912 } 8913 8914 /* return -1 if exception (exotic object only) or TRUE/FALSE */ 8915 int JS_IsExtensible(JSContext *ctx, JSValueConst obj) 8916 { 8917 JSObject *p; 8918 8919 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 8920 return FALSE; 8921 p = JS_VALUE_GET_OBJ(obj); 8922 if (unlikely(p->is_exotic)) { 8923 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8924 if (em && em->is_extensible) { 8925 return em->is_extensible(ctx, obj); 8926 } 8927 } 8928 return p->extensible; 8929 } 8930 8931 /* return -1 if exception (exotic object only) or TRUE/FALSE */ 8932 int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) 8933 { 8934 JSObject *p; 8935 8936 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 8937 return FALSE; 8938 p = JS_VALUE_GET_OBJ(obj); 8939 if (unlikely(p->is_exotic)) { 8940 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 8941 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8942 JSTypedArray *ta; 8943 JSArrayBuffer *abuf; 8944 /* resizable type arrays return FALSE */ 8945 ta = p->u.typed_array; 8946 abuf = ta->buffer->u.array_buffer; 8947 if (ta->track_rab || 8948 (array_buffer_is_resizable(abuf) && !abuf->shared)) 8949 return FALSE; 8950 } else { 8951 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8952 if (em && em->prevent_extensions) { 8953 return em->prevent_extensions(ctx, obj); 8954 } 8955 } 8956 } 8957 p->extensible = FALSE; 8958 return TRUE; 8959 } 8960 8961 /* return -1 if exception otherwise TRUE or FALSE */ 8962 int JS_HasPropertyStr(JSContext *ctx, JSValueConst obj, const char *propname) 8963 { 8964 JSAtom atom; 8965 int ret; 8966 atom = JS_NewAtom(ctx, propname); 8967 if (atom == JS_ATOM_NULL) { 8968 return -1; 8969 } 8970 ret = JS_HasProperty(ctx, obj, atom); 8971 JS_FreeAtom(ctx, atom); 8972 return ret; 8973 } 8974 8975 /* return -1 if exception otherwise TRUE or FALSE */ 8976 int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) 8977 { 8978 JSObject *p; 8979 int ret; 8980 JSValue obj1; 8981 8982 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 8983 return FALSE; 8984 p = JS_VALUE_GET_OBJ(obj); 8985 for(;;) { 8986 if (p->is_exotic) { 8987 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8988 if (em && em->has_property) { 8989 /* has_property can free the prototype */ 8990 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8991 ret = em->has_property(ctx, obj1, prop); 8992 JS_FreeValue(ctx, obj1); 8993 return ret; 8994 } 8995 } 8996 /* JS_GetOwnPropertyInternal can free the prototype */ 8997 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8998 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); 8999 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 9000 if (ret != 0) 9001 return ret; 9002 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 9003 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 9004 ret = JS_AtomIsNumericIndex(ctx, prop); 9005 if (ret != 0) { 9006 if (ret < 0) 9007 return -1; 9008 return FALSE; 9009 } 9010 } 9011 p = p->shape->proto; 9012 if (!p) 9013 break; 9014 } 9015 return FALSE; 9016 } 9017 9018 /* val must be a symbol */ 9019 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) 9020 { 9021 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 9022 return js_get_atom_index(ctx->rt, p); 9023 } 9024 9025 /* return JS_ATOM_NULL in case of exception */ 9026 JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) 9027 { 9028 JSAtom atom; 9029 uint32_t tag; 9030 tag = JS_VALUE_GET_TAG(val); 9031 if (tag == JS_TAG_INT && 9032 (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { 9033 /* fast path for integer values */ 9034 atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); 9035 } else if (tag == JS_TAG_SYMBOL) { 9036 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 9037 atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); 9038 } else { 9039 JSValue str; 9040 str = JS_ToPropertyKey(ctx, val); 9041 if (JS_IsException(str)) 9042 return JS_ATOM_NULL; 9043 if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { 9044 atom = js_symbol_to_atom(ctx, str); 9045 } else { 9046 atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); 9047 } 9048 } 9049 return atom; 9050 } 9051 9052 static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, 9053 JSValue prop) 9054 { 9055 JSAtom atom; 9056 JSValue ret; 9057 9058 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && 9059 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { 9060 JSObject *p; 9061 uint32_t idx; 9062 /* fast path for array access */ 9063 p = JS_VALUE_GET_OBJ(this_obj); 9064 idx = JS_VALUE_GET_INT(prop); 9065 switch(p->class_id) { 9066 case JS_CLASS_ARRAY: 9067 case JS_CLASS_ARGUMENTS: 9068 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9069 return JS_DupValue(ctx, p->u.array.u.values[idx]); 9070 case JS_CLASS_MAPPED_ARGUMENTS: 9071 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9072 return JS_DupValue(ctx, *p->u.array.u.var_refs[idx]->pvalue); 9073 case JS_CLASS_INT8_ARRAY: 9074 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9075 return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); 9076 case JS_CLASS_UINT8C_ARRAY: 9077 case JS_CLASS_UINT8_ARRAY: 9078 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9079 return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); 9080 case JS_CLASS_INT16_ARRAY: 9081 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9082 return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); 9083 case JS_CLASS_UINT16_ARRAY: 9084 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9085 return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); 9086 case JS_CLASS_INT32_ARRAY: 9087 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9088 return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); 9089 case JS_CLASS_UINT32_ARRAY: 9090 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9091 return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); 9092 case JS_CLASS_BIG_INT64_ARRAY: 9093 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9094 return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); 9095 case JS_CLASS_BIG_UINT64_ARRAY: 9096 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9097 return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); 9098 case JS_CLASS_FLOAT16_ARRAY: 9099 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9100 return __JS_NewFloat64(ctx, fromfp16(p->u.array.u.fp16_ptr[idx])); 9101 case JS_CLASS_FLOAT32_ARRAY: 9102 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9103 return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); 9104 case JS_CLASS_FLOAT64_ARRAY: 9105 if (unlikely(idx >= p->u.array.count)) goto slow_path; 9106 return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); 9107 default: 9108 goto slow_path; 9109 } 9110 } else { 9111 slow_path: 9112 /* ToObject() must be done before ToPropertyKey() */ 9113 if (JS_IsNull(this_obj) || JS_IsUndefined(this_obj)) { 9114 JS_FreeValue(ctx, prop); 9115 return JS_ThrowTypeError(ctx, "cannot read property of %s", JS_IsNull(this_obj) ? "null" : "undefined"); 9116 } 9117 atom = JS_ValueToAtom(ctx, prop); 9118 JS_FreeValue(ctx, prop); 9119 if (unlikely(atom == JS_ATOM_NULL)) 9120 return JS_EXCEPTION; 9121 ret = JS_GetProperty(ctx, this_obj, atom); 9122 JS_FreeAtom(ctx, atom); 9123 return ret; 9124 } 9125 } 9126 9127 JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 9128 uint32_t idx) 9129 { 9130 return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); 9131 } 9132 9133 /* Check if an object has a generalized numeric property. Return value: 9134 -1 for exception, 9135 TRUE if property exists, stored into *pval, 9136 FALSE if proprty does not exist. 9137 */ 9138 static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) 9139 { 9140 JSValue val = JS_UNDEFINED; 9141 JSAtom prop; 9142 int present; 9143 9144 if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { 9145 /* fast path */ 9146 present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); 9147 if (present > 0) { 9148 val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); 9149 if (unlikely(JS_IsException(val))) 9150 present = -1; 9151 } 9152 } else { 9153 prop = JS_NewAtomInt64(ctx, idx); 9154 present = -1; 9155 if (likely(prop != JS_ATOM_NULL)) { 9156 present = JS_HasProperty(ctx, obj, prop); 9157 if (present > 0) { 9158 val = JS_GetProperty(ctx, obj, prop); 9159 if (unlikely(JS_IsException(val))) 9160 present = -1; 9161 } 9162 JS_FreeAtom(ctx, prop); 9163 } 9164 } 9165 *pval = val; 9166 return present; 9167 } 9168 9169 static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) 9170 { 9171 JSAtom prop; 9172 JSValue val; 9173 9174 if ((uint64_t)idx <= INT32_MAX) { 9175 /* fast path for fast arrays */ 9176 return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); 9177 } 9178 prop = JS_NewAtomInt64(ctx, idx); 9179 if (prop == JS_ATOM_NULL) 9180 return JS_EXCEPTION; 9181 9182 val = JS_GetProperty(ctx, obj, prop); 9183 JS_FreeAtom(ctx, prop); 9184 return val; 9185 } 9186 9187 JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, 9188 const char *prop) 9189 { 9190 JSAtom atom; 9191 JSValue ret; 9192 atom = JS_NewAtom(ctx, prop); 9193 if (atom == JS_ATOM_NULL) 9194 return JS_EXCEPTION; 9195 ret = JS_GetProperty(ctx, this_obj, atom); 9196 JS_FreeAtom(ctx, atom); 9197 return ret; 9198 } 9199 9200 /* Note: the property value is not initialized. Return NULL if memory 9201 error. */ 9202 static JSProperty *add_property(JSContext *ctx, 9203 JSObject *p, JSAtom prop, int prop_flags) 9204 { 9205 JSShape *sh, *new_sh; 9206 9207 if (unlikely(__JS_AtomIsTaggedInt(prop))) { 9208 /* update is_std_array_prototype */ 9209 if (unlikely(p->is_std_array_prototype)) { 9210 p->is_std_array_prototype = FALSE; 9211 } else if (unlikely(p->has_immutable_prototype)) { 9212 struct list_head *el; 9213 9214 /* modifying Object.prototype : reset the corresponding is_std_array_prototype */ 9215 list_for_each(el, &ctx->rt->context_list) { 9216 JSContext *ctx1 = list_entry(el, JSContext, link); 9217 if (JS_IsObject(ctx1->class_proto[JS_CLASS_OBJECT]) && 9218 JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_OBJECT]) == p) { 9219 if (JS_IsObject(ctx1->class_proto[JS_CLASS_ARRAY])) { 9220 JSObject *p1 = JS_VALUE_GET_OBJ(ctx1->class_proto[JS_CLASS_ARRAY]); 9221 p1->is_std_array_prototype = FALSE; 9222 } 9223 break; 9224 } 9225 } 9226 } 9227 } 9228 sh = p->shape; 9229 if (sh->is_hashed) { 9230 /* try to find an existing shape */ 9231 new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); 9232 if (new_sh) { 9233 /* matching shape found: use it */ 9234 /* the property array may need to be resized */ 9235 if (new_sh->prop_size != sh->prop_size) { 9236 JSProperty *new_prop; 9237 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * 9238 new_sh->prop_size); 9239 if (!new_prop) 9240 return NULL; 9241 p->prop = new_prop; 9242 } 9243 p->shape = js_dup_shape(new_sh); 9244 js_free_shape(ctx->rt, sh); 9245 return &p->prop[new_sh->prop_count - 1]; 9246 } else if (js_rc(sh)->ref_count != 1) { 9247 /* if the shape is shared, clone it */ 9248 new_sh = js_clone_shape(ctx, sh); 9249 if (!new_sh) 9250 return NULL; 9251 /* hash the cloned shape */ 9252 new_sh->is_hashed = TRUE; 9253 js_shape_hash_link(ctx->rt, new_sh); 9254 js_free_shape(ctx->rt, p->shape); 9255 p->shape = new_sh; 9256 } 9257 } 9258 assert(js_rc(p->shape)->ref_count == 1); 9259 if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) 9260 return NULL; 9261 return &p->prop[p->shape->prop_count - 1]; 9262 } 9263 9264 /* can be called on JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS or 9265 JS_CLASS_MAPPED_ARGUMENTS objects. return < 0 if memory alloc 9266 error. */ 9267 static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, 9268 JSObject *p) 9269 { 9270 JSProperty *pr; 9271 JSShape *sh; 9272 uint32_t i, len, new_count; 9273 9274 if (js_shape_prepare_update(ctx, p, NULL)) 9275 return -1; 9276 len = p->u.array.count; 9277 /* resize the properties once to simplify the error handling */ 9278 sh = p->shape; 9279 new_count = sh->prop_count + len; 9280 if (new_count > sh->prop_size) { 9281 if (resize_properties(ctx, &p->shape, p, new_count)) 9282 return -1; 9283 } 9284 9285 if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { 9286 JSVarRef **tab = p->u.array.u.var_refs; 9287 for(i = 0; i < len; i++) { 9288 /* add_property cannot fail here but 9289 __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ 9290 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); 9291 pr->u.var_ref = *tab++; 9292 } 9293 } else { 9294 JSValue *tab = p->u.array.u.values; 9295 for(i = 0; i < len; i++) { 9296 /* add_property cannot fail here but 9297 __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ 9298 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); 9299 pr->u.value = *tab++; 9300 } 9301 } 9302 js_free(ctx, p->u.array.u.values); 9303 p->u.array.count = 0; 9304 p->u.array.u.values = NULL; /* fail safe */ 9305 p->u.array.u1.size = 0; 9306 p->fast_array = 0; 9307 p->is_std_array_prototype = FALSE; 9308 return 0; 9309 } 9310 9311 static int remove_global_object_property(JSContext *ctx, JSObject *p, 9312 JSShapeProperty *prs, JSProperty *pr) 9313 { 9314 JSVarRef *var_ref; 9315 JSObject *p1; 9316 JSProperty *pr1; 9317 9318 var_ref = pr->u.var_ref; 9319 if (js_rc(var_ref)->ref_count == 1) 9320 return 0; 9321 p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); 9322 pr1 = add_property(ctx, p1, prs->atom, JS_PROP_C_W_E | JS_PROP_VARREF); 9323 if (!pr1) 9324 return -1; 9325 pr1->u.var_ref = var_ref; 9326 js_rc(var_ref)->ref_count++; 9327 JS_FreeValue(ctx, var_ref->value); 9328 var_ref->is_lexical = FALSE; 9329 var_ref->is_const = FALSE; 9330 var_ref->value = JS_UNINITIALIZED; 9331 return 0; 9332 } 9333 9334 static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) 9335 { 9336 JSShape *sh; 9337 JSShapeProperty *pr, *lpr, *prop; 9338 JSProperty *pr1; 9339 uint32_t lpr_idx; 9340 intptr_t h, h1; 9341 9342 redo: 9343 sh = p->shape; 9344 h1 = atom & sh->prop_hash_mask; 9345 h = sh->hash_table[h1]; 9346 prop = get_shape_prop(sh); 9347 lpr = NULL; 9348 lpr_idx = 0; /* prevent warning */ 9349 while (h != 0) { 9350 pr = &prop[h - 1]; 9351 if (likely(pr->atom == atom)) { 9352 /* found ! */ 9353 if (!(pr->flags & JS_PROP_CONFIGURABLE)) 9354 return FALSE; 9355 /* realloc the shape if needed */ 9356 if (lpr) 9357 lpr_idx = lpr - get_shape_prop(sh); 9358 if (js_shape_prepare_update(ctx, p, &pr)) 9359 return -1; 9360 sh = p->shape; 9361 /* remove property */ 9362 if (lpr) { 9363 lpr = get_shape_prop(sh) + lpr_idx; 9364 lpr->hash_next = pr->hash_next; 9365 } else { 9366 sh->hash_table[h1] = pr->hash_next; 9367 } 9368 sh->deleted_prop_count++; 9369 /* free the entry */ 9370 pr1 = &p->prop[h - 1]; 9371 if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { 9372 if ((pr->flags & JS_PROP_TMASK) == JS_PROP_VARREF) 9373 if (remove_global_object_property(ctx, p, pr, pr1)) 9374 return -1; 9375 } 9376 free_property(ctx->rt, pr1, pr->flags); 9377 JS_FreeAtom(ctx, pr->atom); 9378 /* put default values */ 9379 pr->flags = 0; 9380 pr->atom = JS_ATOM_NULL; 9381 pr1->u.value = JS_UNDEFINED; 9382 9383 /* compact the properties if too many deleted properties */ 9384 if (sh->deleted_prop_count >= 8 && 9385 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { 9386 compact_properties(ctx, p); 9387 } 9388 return TRUE; 9389 } 9390 lpr = pr; 9391 h = pr->hash_next; 9392 } 9393 9394 if (p->is_exotic) { 9395 if (p->fast_array) { 9396 uint32_t idx; 9397 if (JS_AtomIsArrayIndex(ctx, &idx, atom) && 9398 idx < p->u.array.count) { 9399 if (p->class_id == JS_CLASS_ARRAY || 9400 p->class_id == JS_CLASS_ARGUMENTS || 9401 p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { 9402 /* Special case deleting the last element of a fast Array */ 9403 if (idx == p->u.array.count - 1) { 9404 if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { 9405 free_var_ref(ctx->rt, p->u.array.u.var_refs[idx]); 9406 } else { 9407 JS_FreeValue(ctx, p->u.array.u.values[idx]); 9408 } 9409 p->u.array.count = idx; 9410 return TRUE; 9411 } 9412 if (convert_fast_array_to_array(ctx, p)) 9413 return -1; 9414 goto redo; 9415 } else { 9416 return FALSE; 9417 } 9418 } 9419 } else { 9420 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 9421 if (em && em->delete_property) { 9422 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); 9423 } 9424 } 9425 } 9426 /* not found */ 9427 return TRUE; 9428 } 9429 9430 static int call_setter(JSContext *ctx, JSObject *setter, 9431 JSValueConst this_obj, JSValue val, int flags) 9432 { 9433 JSValue ret, func; 9434 if (likely(setter)) { 9435 func = JS_MKPTR(JS_TAG_OBJECT, setter); 9436 /* Note: the field could be removed in the setter */ 9437 func = JS_DupValue(ctx, func); 9438 ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); 9439 JS_FreeValue(ctx, val); 9440 if (JS_IsException(ret)) 9441 return -1; 9442 JS_FreeValue(ctx, ret); 9443 return TRUE; 9444 } else { 9445 JS_FreeValue(ctx, val); 9446 if ((flags & JS_PROP_THROW) || 9447 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 9448 JS_ThrowTypeError(ctx, "no setter for property"); 9449 return -1; 9450 } 9451 return FALSE; 9452 } 9453 } 9454 9455 /* set the array length and remove the array elements if necessary. */ 9456 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, 9457 int flags) 9458 { 9459 uint32_t len, idx, cur_len; 9460 int i, ret; 9461 9462 /* Note: this call can reallocate the properties of 'p' */ 9463 ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); 9464 if (ret) 9465 return -1; 9466 /* JS_ToArrayLengthFree() must be done before the read-only test */ 9467 if (unlikely(!(get_shape_prop(p->shape)[0].flags & JS_PROP_WRITABLE))) 9468 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 9469 9470 if (likely(p->fast_array)) { 9471 uint32_t old_len = p->u.array.count; 9472 if (len < old_len) { 9473 for(i = len; i < old_len; i++) { 9474 JS_FreeValue(ctx, p->u.array.u.values[i]); 9475 } 9476 p->u.array.count = len; 9477 } 9478 p->prop[0].u.value = JS_NewUint32(ctx, len); 9479 } else { 9480 /* Note: length is always a uint32 because the object is an 9481 array */ 9482 JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); 9483 if (len < cur_len) { 9484 uint32_t d; 9485 JSShape *sh; 9486 JSShapeProperty *pr; 9487 9488 d = cur_len - len; 9489 sh = p->shape; 9490 if (d <= sh->prop_count) { 9491 JSAtom atom; 9492 9493 /* faster to iterate */ 9494 while (cur_len > len) { 9495 atom = JS_NewAtomUInt32(ctx, cur_len - 1); 9496 ret = delete_property(ctx, p, atom); 9497 JS_FreeAtom(ctx, atom); 9498 if (unlikely(!ret)) { 9499 /* unlikely case: property is not 9500 configurable */ 9501 break; 9502 } 9503 cur_len--; 9504 } 9505 } else { 9506 /* faster to iterate thru all the properties. Need two 9507 passes in case one of the property is not 9508 configurable */ 9509 cur_len = len; 9510 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; 9511 i++, pr++) { 9512 if (pr->atom != JS_ATOM_NULL && 9513 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { 9514 if (idx >= cur_len && 9515 !(pr->flags & JS_PROP_CONFIGURABLE)) { 9516 cur_len = idx + 1; 9517 } 9518 } 9519 } 9520 9521 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; 9522 i++, pr++) { 9523 if (pr->atom != JS_ATOM_NULL && 9524 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { 9525 if (idx >= cur_len) { 9526 /* remove the property */ 9527 delete_property(ctx, p, pr->atom); 9528 /* WARNING: the shape may have been modified */ 9529 sh = p->shape; 9530 pr = get_shape_prop(sh) + i; 9531 } 9532 } 9533 } 9534 } 9535 } else { 9536 cur_len = len; 9537 } 9538 set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); 9539 if (unlikely(cur_len > len)) { 9540 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); 9541 } 9542 } 9543 return TRUE; 9544 } 9545 9546 /* return -1 if exception */ 9547 static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) 9548 { 9549 uint32_t new_size; 9550 size_t slack; 9551 JSValue *new_array_prop; 9552 /* XXX: potential arithmetic overflow */ 9553 new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); 9554 new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); 9555 if (!new_array_prop) 9556 return -1; 9557 new_size += slack / sizeof(*new_array_prop); 9558 p->u.array.u.values = new_array_prop; 9559 p->u.array.u1.size = new_size; 9560 return 0; 9561 } 9562 9563 /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = 9564 TRUE and p->extensible = TRUE */ 9565 static inline int add_fast_array_element(JSContext *ctx, JSObject *p, 9566 JSValue val, int flags) 9567 { 9568 uint32_t new_len, array_len; 9569 /* extend the array by one */ 9570 /* XXX: convert to slow array if new_len > 2^31-1 elements */ 9571 new_len = p->u.array.count + 1; 9572 /* update the length if necessary. We assume that if the length is 9573 not an integer, then if it >= 2^31. */ 9574 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { 9575 array_len = JS_VALUE_GET_INT(p->prop[0].u.value); 9576 if (new_len > array_len) { 9577 if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { 9578 JS_FreeValue(ctx, val); 9579 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 9580 } 9581 p->prop[0].u.value = JS_NewInt32(ctx, new_len); 9582 } 9583 } 9584 if (unlikely(new_len > p->u.array.u1.size)) { 9585 if (expand_fast_array(ctx, p, new_len)) { 9586 JS_FreeValue(ctx, val); 9587 return -1; 9588 } 9589 } 9590 p->u.array.u.values[new_len - 1] = val; 9591 p->u.array.count = new_len; 9592 return TRUE; 9593 } 9594 9595 /* Allocate a new fast array initialized to JS_UNDEFINED. Its maximum 9596 size is 2^31-1 elements. For convenience, 'len' is a 64 bit 9597 integer. */ 9598 static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) 9599 { 9600 JSValue arr; 9601 JSObject *p; 9602 int i; 9603 9604 if (len > INT32_MAX) 9605 return JS_ThrowRangeError(ctx, "invalid array length"); 9606 arr = JS_NewArray(ctx); 9607 if (JS_IsException(arr)) 9608 return arr; 9609 if (len > 0) { 9610 p = JS_VALUE_GET_OBJ(arr); 9611 if (expand_fast_array(ctx, p, len) < 0) { 9612 JS_FreeValue(ctx, arr); 9613 return JS_EXCEPTION; 9614 } 9615 p->u.array.count = len; 9616 for(i = 0; i < len; i++) 9617 p->u.array.u.values[i] = JS_UNDEFINED; 9618 /* update the 'length' field */ 9619 set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len)); 9620 } 9621 return arr; 9622 } 9623 9624 static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) 9625 { 9626 JSValue obj; 9627 JSObject *p; 9628 int i; 9629 9630 obj = JS_NewArray(ctx); 9631 if (JS_IsException(obj)) 9632 return JS_EXCEPTION; 9633 if (len > 0) { 9634 p = JS_VALUE_GET_OBJ(obj); 9635 if (expand_fast_array(ctx, p, len) < 0) { 9636 JS_FreeValue(ctx, obj); 9637 return JS_EXCEPTION; 9638 } 9639 p->u.array.count = len; 9640 for(i = 0; i < len; i++) 9641 p->u.array.u.values[i] = JS_DupValue(ctx, tab[i]); 9642 /* update the 'length' field */ 9643 set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len)); 9644 } 9645 return obj; 9646 } 9647 9648 static JSValue js_create_array_free(JSContext *ctx, int len, JSValue *tab) 9649 { 9650 JSValue obj; 9651 JSObject *p; 9652 int i; 9653 9654 obj = JS_NewArray(ctx); 9655 if (JS_IsException(obj)) 9656 goto fail; 9657 if (len > 0) { 9658 p = JS_VALUE_GET_OBJ(obj); 9659 if (expand_fast_array(ctx, p, len) < 0) { 9660 JS_FreeValue(ctx, obj); 9661 fail: 9662 for(i = 0; i < len; i++) 9663 JS_FreeValue(ctx, tab[i]); 9664 return JS_EXCEPTION; 9665 } 9666 p->u.array.count = len; 9667 for(i = 0; i < len; i++) 9668 p->u.array.u.values[i] = tab[i]; 9669 /* update the 'length' field */ 9670 set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, len)); 9671 } 9672 return obj; 9673 } 9674 9675 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) 9676 { 9677 JS_FreeValue(ctx, desc->getter); 9678 JS_FreeValue(ctx, desc->setter); 9679 JS_FreeValue(ctx, desc->value); 9680 } 9681 9682 /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is 9683 freed by the function. 'flags' is a bitmask of JS_PROP_THROW and 9684 JS_PROP_THROW_STRICT. 'this_obj' is the receiver. If obj != 9685 this_obj, then obj must be an object (Reflect.set case). */ 9686 int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, 9687 JSAtom prop, JSValue val, JSValueConst this_obj, int flags) 9688 { 9689 JSObject *p, *p1; 9690 JSShapeProperty *prs; 9691 JSProperty *pr; 9692 uint32_t tag; 9693 JSPropertyDescriptor desc; 9694 int ret; 9695 #if 0 9696 printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); 9697 #endif 9698 tag = JS_VALUE_GET_TAG(this_obj); 9699 if (unlikely(tag != JS_TAG_OBJECT)) { 9700 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 9701 p = NULL; 9702 p1 = JS_VALUE_GET_OBJ(obj); 9703 goto prototype_lookup; 9704 } else { 9705 switch(tag) { 9706 case JS_TAG_NULL: 9707 JS_FreeValue(ctx, val); 9708 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); 9709 return -1; 9710 case JS_TAG_UNDEFINED: 9711 JS_FreeValue(ctx, val); 9712 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); 9713 return -1; 9714 default: 9715 /* even on a primitive type we can have setters on the prototype */ 9716 p = NULL; 9717 p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); 9718 goto prototype_lookup; 9719 } 9720 } 9721 } else { 9722 p = JS_VALUE_GET_OBJ(this_obj); 9723 p1 = JS_VALUE_GET_OBJ(obj); 9724 if (unlikely(p != p1)) 9725 goto retry2; 9726 } 9727 9728 /* fast path if obj == this_obj */ 9729 retry: 9730 prs = find_own_property(&pr, p1, prop); 9731 if (prs) { 9732 if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | 9733 JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { 9734 /* fast case */ 9735 set_value(ctx, &pr->u.value, val); 9736 return TRUE; 9737 } else if (prs->flags & JS_PROP_LENGTH) { 9738 assert(p->class_id == JS_CLASS_ARRAY); 9739 assert(prop == JS_ATOM_length); 9740 return set_array_length(ctx, p, val, flags); 9741 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 9742 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); 9743 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 9744 /* XXX: already use var_ref->is_const. Cannot simplify use the 9745 writable flag for JS_CLASS_MODULE_NS. */ 9746 if (p->class_id == JS_CLASS_MODULE_NS || pr->u.var_ref->is_const) 9747 goto read_only_prop; 9748 set_value(ctx, pr->u.var_ref->pvalue, val); 9749 return TRUE; 9750 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 9751 /* Instantiate property and retry (potentially useless) */ 9752 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { 9753 JS_FreeValue(ctx, val); 9754 return -1; 9755 } 9756 goto retry; 9757 } else { 9758 goto read_only_prop; 9759 } 9760 } 9761 9762 for(;;) { 9763 if (p1->is_exotic) { 9764 if (p1->fast_array) { 9765 if (__JS_AtomIsTaggedInt(prop)) { 9766 uint32_t idx = __JS_AtomToUInt32(prop); 9767 if (idx < p1->u.array.count) { 9768 if (unlikely(p == p1)) 9769 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); 9770 else 9771 break; 9772 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && 9773 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { 9774 goto typed_array_oob; 9775 } 9776 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && 9777 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { 9778 ret = JS_AtomIsNumericIndex(ctx, prop); 9779 if (ret != 0) { 9780 if (ret < 0) { 9781 JS_FreeValue(ctx, val); 9782 return -1; 9783 } 9784 typed_array_oob: 9785 if (p == p1) { 9786 /* must convert the argument even if out of bound access */ 9787 if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || 9788 p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { 9789 int64_t v; 9790 if (JS_ToBigInt64Free(ctx, &v, val)) 9791 return -1; 9792 } else { 9793 val = JS_ToNumberFree(ctx, val); 9794 JS_FreeValue(ctx, val); 9795 if (JS_IsException(val)) 9796 return -1; 9797 } 9798 } else { 9799 JS_FreeValue(ctx, val); 9800 } 9801 return TRUE; 9802 } 9803 } 9804 } else { 9805 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; 9806 if (em) { 9807 JSValue obj1; 9808 if (em->set_property) { 9809 /* set_property can free the prototype */ 9810 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 9811 ret = em->set_property(ctx, obj1, prop, 9812 val, this_obj, flags); 9813 JS_FreeValue(ctx, obj1); 9814 JS_FreeValue(ctx, val); 9815 return ret; 9816 } 9817 if (em->get_own_property) { 9818 /* get_own_property can free the prototype */ 9819 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 9820 ret = em->get_own_property(ctx, &desc, 9821 obj1, prop); 9822 JS_FreeValue(ctx, obj1); 9823 if (ret < 0) { 9824 JS_FreeValue(ctx, val); 9825 return ret; 9826 } 9827 if (ret) { 9828 if (desc.flags & JS_PROP_GETSET) { 9829 JSObject *setter; 9830 if (JS_IsUndefined(desc.setter)) 9831 setter = NULL; 9832 else 9833 setter = JS_VALUE_GET_OBJ(desc.setter); 9834 ret = call_setter(ctx, setter, this_obj, val, flags); 9835 JS_FreeValue(ctx, desc.getter); 9836 JS_FreeValue(ctx, desc.setter); 9837 return ret; 9838 } else { 9839 JS_FreeValue(ctx, desc.value); 9840 if (!(desc.flags & JS_PROP_WRITABLE)) 9841 goto read_only_prop; 9842 if (likely(p == p1)) { 9843 ret = JS_DefineProperty(ctx, this_obj, prop, val, 9844 JS_UNDEFINED, JS_UNDEFINED, 9845 JS_PROP_HAS_VALUE); 9846 JS_FreeValue(ctx, val); 9847 return ret; 9848 } else { 9849 break; 9850 } 9851 } 9852 } 9853 } 9854 } 9855 } 9856 } 9857 p1 = p1->shape->proto; 9858 prototype_lookup: 9859 if (!p1) 9860 break; 9861 9862 retry2: 9863 prs = find_own_property(&pr, p1, prop); 9864 if (prs) { 9865 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 9866 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); 9867 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 9868 /* Instantiate property and retry (potentially useless) */ 9869 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) 9870 return -1; 9871 goto retry2; 9872 } else if (!(prs->flags & JS_PROP_WRITABLE)) { 9873 goto read_only_prop; 9874 } else { 9875 break; 9876 } 9877 } 9878 } 9879 9880 if (unlikely(!p)) { 9881 JS_FreeValue(ctx, val); 9882 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); 9883 } 9884 9885 if (unlikely(!p->extensible)) { 9886 JS_FreeValue(ctx, val); 9887 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); 9888 } 9889 9890 if (likely(p == JS_VALUE_GET_OBJ(obj))) { 9891 if (p->is_exotic) { 9892 if (p->class_id == JS_CLASS_ARRAY && p->fast_array && 9893 __JS_AtomIsTaggedInt(prop)) { 9894 uint32_t idx = __JS_AtomToUInt32(prop); 9895 if (idx == p->u.array.count) { 9896 /* fast case */ 9897 return add_fast_array_element(ctx, p, val, flags); 9898 } else { 9899 goto generic_create_prop; 9900 } 9901 } else { 9902 goto generic_create_prop; 9903 } 9904 } else { 9905 if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) 9906 goto generic_create_prop; 9907 pr = add_property(ctx, p, prop, JS_PROP_C_W_E); 9908 if (unlikely(!pr)) { 9909 JS_FreeValue(ctx, val); 9910 return -1; 9911 } 9912 pr->u.value = val; 9913 return TRUE; 9914 } 9915 } else { 9916 /* generic case: modify the property in this_obj if it already exists */ 9917 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 9918 if (ret < 0) { 9919 JS_FreeValue(ctx, val); 9920 return ret; 9921 } 9922 if (ret) { 9923 if (desc.flags & JS_PROP_GETSET) { 9924 JS_FreeValue(ctx, desc.getter); 9925 JS_FreeValue(ctx, desc.setter); 9926 JS_FreeValue(ctx, val); 9927 return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); 9928 } else { 9929 JS_FreeValue(ctx, desc.value); 9930 if (!(desc.flags & JS_PROP_WRITABLE) || 9931 p->class_id == JS_CLASS_MODULE_NS) { 9932 read_only_prop: 9933 JS_FreeValue(ctx, val); 9934 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); 9935 } 9936 } 9937 ret = JS_DefineProperty(ctx, this_obj, prop, val, 9938 JS_UNDEFINED, JS_UNDEFINED, 9939 JS_PROP_HAS_VALUE); 9940 JS_FreeValue(ctx, val); 9941 return ret; 9942 } else { 9943 generic_create_prop: 9944 ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, 9945 flags | 9946 JS_PROP_HAS_VALUE | 9947 JS_PROP_HAS_ENUMERABLE | 9948 JS_PROP_HAS_WRITABLE | 9949 JS_PROP_HAS_CONFIGURABLE | 9950 JS_PROP_C_W_E); 9951 JS_FreeValue(ctx, val); 9952 return ret; 9953 } 9954 } 9955 } 9956 9957 /* return true if an element can be added to a fast array without further tests */ 9958 static force_inline BOOL can_extend_fast_array(JSObject *p) 9959 { 9960 JSObject *proto; 9961 if (!p->extensible) 9962 return FALSE; 9963 proto = p->shape->proto; 9964 if (!proto) 9965 return TRUE; 9966 return proto->is_std_array_prototype; 9967 } 9968 9969 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ 9970 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 9971 JSValue prop, JSValue val, int flags) 9972 { 9973 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && 9974 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { 9975 JSObject *p; 9976 uint32_t idx; 9977 double d; 9978 int32_t v; 9979 9980 /* fast path for array access */ 9981 p = JS_VALUE_GET_OBJ(this_obj); 9982 idx = JS_VALUE_GET_INT(prop); 9983 switch(p->class_id) { 9984 case JS_CLASS_ARRAY: 9985 if (unlikely(idx >= (uint32_t)p->u.array.count)) { 9986 /* fast path to add an element to the array */ 9987 if (unlikely(idx != (uint32_t)p->u.array.count || 9988 !p->fast_array || 9989 !can_extend_fast_array(p))) { 9990 goto slow_path; 9991 } 9992 /* add element */ 9993 return add_fast_array_element(ctx, p, val, flags); 9994 } 9995 set_value(ctx, &p->u.array.u.values[idx], val); 9996 break; 9997 case JS_CLASS_ARGUMENTS: 9998 if (unlikely(idx >= (uint32_t)p->u.array.count)) 9999 goto slow_path; 10000 set_value(ctx, &p->u.array.u.values[idx], val); 10001 break; 10002 case JS_CLASS_MAPPED_ARGUMENTS: 10003 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10004 goto slow_path; 10005 set_value(ctx, p->u.array.u.var_refs[idx]->pvalue, val); 10006 break; 10007 case JS_CLASS_UINT8C_ARRAY: 10008 if (JS_ToUint8ClampFree(ctx, &v, val)) 10009 return -1; 10010 /* Note: the conversion can detach the typed array, so the 10011 array bound check must be done after */ 10012 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10013 goto ta_out_of_bound; 10014 p->u.array.u.uint8_ptr[idx] = v; 10015 break; 10016 case JS_CLASS_INT8_ARRAY: 10017 case JS_CLASS_UINT8_ARRAY: 10018 if (JS_ToInt32Free(ctx, &v, val)) 10019 return -1; 10020 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10021 goto ta_out_of_bound; 10022 p->u.array.u.uint8_ptr[idx] = v; 10023 break; 10024 case JS_CLASS_INT16_ARRAY: 10025 case JS_CLASS_UINT16_ARRAY: 10026 if (JS_ToInt32Free(ctx, &v, val)) 10027 return -1; 10028 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10029 goto ta_out_of_bound; 10030 p->u.array.u.uint16_ptr[idx] = v; 10031 break; 10032 case JS_CLASS_INT32_ARRAY: 10033 case JS_CLASS_UINT32_ARRAY: 10034 if (JS_ToInt32Free(ctx, &v, val)) 10035 return -1; 10036 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10037 goto ta_out_of_bound; 10038 p->u.array.u.uint32_ptr[idx] = v; 10039 break; 10040 case JS_CLASS_BIG_INT64_ARRAY: 10041 case JS_CLASS_BIG_UINT64_ARRAY: 10042 /* XXX: need specific conversion function */ 10043 { 10044 int64_t v; 10045 if (JS_ToBigInt64Free(ctx, &v, val)) 10046 return -1; 10047 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10048 goto ta_out_of_bound; 10049 p->u.array.u.uint64_ptr[idx] = v; 10050 } 10051 break; 10052 case JS_CLASS_FLOAT16_ARRAY: 10053 if (JS_ToFloat64Free(ctx, &d, val)) 10054 return -1; 10055 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10056 goto ta_out_of_bound; 10057 p->u.array.u.fp16_ptr[idx] = tofp16(d); 10058 break; 10059 case JS_CLASS_FLOAT32_ARRAY: 10060 if (JS_ToFloat64Free(ctx, &d, val)) 10061 return -1; 10062 if (unlikely(idx >= (uint32_t)p->u.array.count)) 10063 goto ta_out_of_bound; 10064 p->u.array.u.float_ptr[idx] = d; 10065 break; 10066 case JS_CLASS_FLOAT64_ARRAY: 10067 if (JS_ToFloat64Free(ctx, &d, val)) 10068 return -1; 10069 if (unlikely(idx >= (uint32_t)p->u.array.count)) { 10070 ta_out_of_bound: 10071 return TRUE; 10072 } 10073 p->u.array.u.double_ptr[idx] = d; 10074 break; 10075 default: 10076 goto slow_path; 10077 } 10078 return TRUE; 10079 } else { 10080 JSAtom atom; 10081 int ret; 10082 slow_path: 10083 atom = JS_ValueToAtom(ctx, prop); 10084 JS_FreeValue(ctx, prop); 10085 if (unlikely(atom == JS_ATOM_NULL)) { 10086 JS_FreeValue(ctx, val); 10087 return -1; 10088 } 10089 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags); 10090 JS_FreeAtom(ctx, atom); 10091 return ret; 10092 } 10093 } 10094 10095 int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 10096 uint32_t idx, JSValue val) 10097 { 10098 return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, 10099 JS_PROP_THROW); 10100 } 10101 10102 int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, 10103 int64_t idx, JSValue val) 10104 { 10105 JSAtom prop; 10106 int res; 10107 10108 if ((uint64_t)idx <= INT32_MAX) { 10109 /* fast path for fast arrays */ 10110 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, 10111 JS_PROP_THROW); 10112 } 10113 prop = JS_NewAtomInt64(ctx, idx); 10114 if (prop == JS_ATOM_NULL) { 10115 JS_FreeValue(ctx, val); 10116 return -1; 10117 } 10118 res = JS_SetProperty(ctx, this_obj, prop, val); 10119 JS_FreeAtom(ctx, prop); 10120 return res; 10121 } 10122 10123 int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, 10124 const char *prop, JSValue val) 10125 { 10126 JSAtom atom; 10127 int ret; 10128 atom = JS_NewAtom(ctx, prop); 10129 if (atom == JS_ATOM_NULL) { 10130 JS_FreeValue(ctx, val); 10131 return -1; 10132 } 10133 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW); 10134 JS_FreeAtom(ctx, atom); 10135 return ret; 10136 } 10137 10138 /* compute the property flags. For each flag: (JS_PROP_HAS_x forces 10139 it, otherwise def_flags is used) 10140 Note: makes assumption about the bit pattern of the flags 10141 */ 10142 static int get_prop_flags(int flags, int def_flags) 10143 { 10144 int mask; 10145 mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; 10146 return (flags & mask) | (def_flags & ~mask); 10147 } 10148 10149 static int JS_CreateProperty(JSContext *ctx, JSObject *p, 10150 JSAtom prop, JSValueConst val, 10151 JSValueConst getter, JSValueConst setter, 10152 int flags) 10153 { 10154 JSProperty *pr; 10155 int ret, prop_flags; 10156 JSVarRef *var_ref; 10157 JSObject *delete_obj; 10158 10159 /* add a new property or modify an existing exotic one */ 10160 if (p->is_exotic) { 10161 if (p->class_id == JS_CLASS_ARRAY) { 10162 uint32_t idx, len; 10163 10164 if (p->fast_array) { 10165 if (__JS_AtomIsTaggedInt(prop)) { 10166 idx = __JS_AtomToUInt32(prop); 10167 if (idx == p->u.array.count) { 10168 if (!p->extensible) 10169 goto not_extensible; 10170 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) 10171 goto convert_to_array; 10172 prop_flags = get_prop_flags(flags, 0); 10173 if (prop_flags != JS_PROP_C_W_E) 10174 goto convert_to_array; 10175 return add_fast_array_element(ctx, p, 10176 JS_DupValue(ctx, val), flags); 10177 } else { 10178 goto convert_to_array; 10179 } 10180 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { 10181 /* convert the fast array to normal array */ 10182 convert_to_array: 10183 if (convert_fast_array_to_array(ctx, p)) 10184 return -1; 10185 goto generic_array; 10186 } 10187 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { 10188 JSProperty *plen; 10189 JSShapeProperty *pslen; 10190 generic_array: 10191 /* update the length field */ 10192 plen = &p->prop[0]; 10193 JS_ToUint32(ctx, &len, plen->u.value); 10194 if ((idx + 1) > len) { 10195 pslen = get_shape_prop(p->shape); 10196 if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) 10197 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 10198 /* XXX: should update the length after defining 10199 the property */ 10200 len = idx + 1; 10201 set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len)); 10202 } 10203 } 10204 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 10205 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 10206 ret = JS_AtomIsNumericIndex(ctx, prop); 10207 if (ret != 0) { 10208 if (ret < 0) 10209 return -1; 10210 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); 10211 } 10212 } else if (!(flags & JS_PROP_NO_EXOTIC)) { 10213 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 10214 if (em) { 10215 if (em->define_own_property) { 10216 return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), 10217 prop, val, getter, setter, flags); 10218 } 10219 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 10220 if (ret < 0) 10221 return -1; 10222 if (!ret) 10223 goto not_extensible; 10224 } 10225 } 10226 } 10227 10228 if (!p->extensible) { 10229 not_extensible: 10230 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); 10231 } 10232 10233 var_ref = NULL; 10234 delete_obj = NULL; 10235 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10236 prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | 10237 JS_PROP_GETSET; 10238 } else { 10239 prop_flags = flags & JS_PROP_C_W_E; 10240 if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { 10241 JSObject *p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); 10242 JSShapeProperty *prs1; 10243 JSProperty *pr1; 10244 prs1 = find_own_property(&pr1, p1, prop); 10245 if (prs1) { 10246 delete_obj = p1; 10247 var_ref = pr1->u.var_ref; 10248 js_rc(var_ref)->ref_count++; 10249 } else { 10250 var_ref = js_create_var_ref(ctx, FALSE); 10251 if (!var_ref) 10252 return -1; 10253 } 10254 var_ref->is_const = !(prop_flags & JS_PROP_WRITABLE); 10255 prop_flags |= JS_PROP_VARREF; 10256 } 10257 } 10258 pr = add_property(ctx, p, prop, prop_flags); 10259 if (unlikely(!pr)) { 10260 if (var_ref) 10261 free_var_ref(ctx->rt, var_ref); 10262 return -1; 10263 } 10264 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10265 pr->u.getset.getter = NULL; 10266 if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { 10267 pr->u.getset.getter = 10268 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter)); 10269 } 10270 pr->u.getset.setter = NULL; 10271 if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { 10272 pr->u.getset.setter = 10273 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter)); 10274 } 10275 } else if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { 10276 if (delete_obj) 10277 delete_property(ctx, delete_obj, prop); 10278 pr->u.var_ref = var_ref; 10279 if (flags & JS_PROP_HAS_VALUE) { 10280 *var_ref->pvalue = JS_DupValue(ctx, val); 10281 } else { 10282 *var_ref->pvalue = JS_UNDEFINED; 10283 } 10284 } else { 10285 if (flags & JS_PROP_HAS_VALUE) { 10286 pr->u.value = JS_DupValue(ctx, val); 10287 } else { 10288 pr->u.value = JS_UNDEFINED; 10289 } 10290 } 10291 return TRUE; 10292 } 10293 10294 /* return FALSE if not OK */ 10295 static BOOL check_define_prop_flags(int prop_flags, int flags) 10296 { 10297 BOOL has_accessor, is_getset; 10298 10299 if (!(prop_flags & JS_PROP_CONFIGURABLE)) { 10300 if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == 10301 (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { 10302 return FALSE; 10303 } 10304 if ((flags & JS_PROP_HAS_ENUMERABLE) && 10305 (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) 10306 return FALSE; 10307 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | 10308 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10309 has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); 10310 is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); 10311 if (has_accessor != is_getset) 10312 return FALSE; 10313 if (!is_getset && !(prop_flags & JS_PROP_WRITABLE)) { 10314 /* not writable: cannot set the writable bit */ 10315 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == 10316 (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) 10317 return FALSE; 10318 } 10319 } 10320 } 10321 return TRUE; 10322 } 10323 10324 /* ensure that the shape can be safely modified */ 10325 static int js_shape_prepare_update(JSContext *ctx, JSObject *p, 10326 JSShapeProperty **pprs) 10327 { 10328 JSShape *sh; 10329 uint32_t idx = 0; /* prevent warning */ 10330 10331 sh = p->shape; 10332 if (sh->is_hashed) { 10333 if (js_rc(sh)->ref_count != 1) { 10334 if (pprs) 10335 idx = *pprs - get_shape_prop(sh); 10336 /* clone the shape (the resulting one is no longer hashed) */ 10337 sh = js_clone_shape(ctx, sh); 10338 if (!sh) 10339 return -1; 10340 js_free_shape(ctx->rt, p->shape); 10341 p->shape = sh; 10342 if (pprs) 10343 *pprs = get_shape_prop(sh) + idx; 10344 } else { 10345 js_shape_hash_unlink(ctx->rt, sh); 10346 sh->is_hashed = FALSE; 10347 } 10348 } 10349 return 0; 10350 } 10351 10352 static int js_update_property_flags(JSContext *ctx, JSObject *p, 10353 JSShapeProperty **pprs, int flags) 10354 { 10355 if (flags != (*pprs)->flags) { 10356 if (js_shape_prepare_update(ctx, p, pprs)) 10357 return -1; 10358 (*pprs)->flags = flags; 10359 } 10360 return 0; 10361 } 10362 10363 /* allowed flags: 10364 JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE 10365 JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, 10366 JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, 10367 JS_PROP_THROW, JS_PROP_NO_EXOTIC. 10368 If JS_PROP_THROW is set, return an exception instead of FALSE. 10369 if JS_PROP_NO_EXOTIC is set, do not call the exotic 10370 define_own_property callback. 10371 return -1 (exception), FALSE or TRUE. 10372 */ 10373 int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 10374 JSAtom prop, JSValueConst val, 10375 JSValueConst getter, JSValueConst setter, int flags) 10376 { 10377 JSObject *p; 10378 JSShapeProperty *prs; 10379 JSProperty *pr; 10380 int mask, res; 10381 10382 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { 10383 JS_ThrowTypeErrorNotAnObject(ctx); 10384 return -1; 10385 } 10386 p = JS_VALUE_GET_OBJ(this_obj); 10387 10388 redo_prop_update: 10389 prs = find_own_property(&pr, p, prop); 10390 if (prs) { 10391 /* the range of the Array length property is always tested before */ 10392 if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { 10393 uint32_t array_length; 10394 if (JS_ToArrayLengthFree(ctx, &array_length, 10395 JS_DupValue(ctx, val), FALSE)) { 10396 return -1; 10397 } 10398 /* this code relies on the fact that Uint32 are never allocated */ 10399 val = (JSValueConst)JS_NewUint32(ctx, array_length); 10400 /* prs may have been modified */ 10401 prs = find_own_property(&pr, p, prop); 10402 assert(prs != NULL); 10403 } 10404 /* property already exists */ 10405 if (!check_define_prop_flags(prs->flags, flags)) { 10406 not_configurable: 10407 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); 10408 } 10409 10410 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 10411 /* Instantiate property and retry */ 10412 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 10413 return -1; 10414 goto redo_prop_update; 10415 } 10416 10417 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | 10418 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10419 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10420 JSObject *new_getter, *new_setter; 10421 10422 if (JS_IsFunction(ctx, getter)) { 10423 new_getter = JS_VALUE_GET_OBJ(getter); 10424 } else { 10425 new_getter = NULL; 10426 } 10427 if (JS_IsFunction(ctx, setter)) { 10428 new_setter = JS_VALUE_GET_OBJ(setter); 10429 } else { 10430 new_setter = NULL; 10431 } 10432 10433 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { 10434 if (js_shape_prepare_update(ctx, p, &prs)) 10435 return -1; 10436 /* convert to getset */ 10437 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 10438 if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { 10439 if (remove_global_object_property(ctx, p, prs, pr)) 10440 return -1; 10441 } 10442 free_var_ref(ctx->rt, pr->u.var_ref); 10443 } else { 10444 JS_FreeValue(ctx, pr->u.value); 10445 } 10446 prs->flags = (prs->flags & 10447 (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | 10448 JS_PROP_GETSET; 10449 pr->u.getset.getter = NULL; 10450 pr->u.getset.setter = NULL; 10451 } else { 10452 if (!(prs->flags & JS_PROP_CONFIGURABLE)) { 10453 if ((flags & JS_PROP_HAS_GET) && 10454 new_getter != pr->u.getset.getter) { 10455 goto not_configurable; 10456 } 10457 if ((flags & JS_PROP_HAS_SET) && 10458 new_setter != pr->u.getset.setter) { 10459 goto not_configurable; 10460 } 10461 } 10462 } 10463 if (flags & JS_PROP_HAS_GET) { 10464 if (pr->u.getset.getter) 10465 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 10466 if (new_getter) 10467 JS_DupValue(ctx, getter); 10468 pr->u.getset.getter = new_getter; 10469 } 10470 if (flags & JS_PROP_HAS_SET) { 10471 if (pr->u.getset.setter) 10472 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 10473 if (new_setter) 10474 JS_DupValue(ctx, setter); 10475 pr->u.getset.setter = new_setter; 10476 } 10477 } else { 10478 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 10479 /* convert to data descriptor */ 10480 JSVarRef *var_ref; 10481 if (unlikely(p->class_id == JS_CLASS_GLOBAL_OBJECT)) { 10482 var_ref = js_global_object_find_uninitialized_var(ctx, p, prop, FALSE); 10483 if (!var_ref) 10484 return -1; 10485 } else { 10486 var_ref = NULL; 10487 } 10488 if (js_shape_prepare_update(ctx, p, &prs)) { 10489 if (var_ref) 10490 free_var_ref(ctx->rt, var_ref); 10491 return -1; 10492 } 10493 if (pr->u.getset.getter) 10494 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 10495 if (pr->u.getset.setter) 10496 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 10497 if (var_ref) { 10498 prs->flags = (prs->flags & ~JS_PROP_TMASK) | 10499 JS_PROP_VARREF | JS_PROP_WRITABLE; 10500 pr->u.var_ref = var_ref; 10501 } else { 10502 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); 10503 pr->u.value = JS_UNDEFINED; 10504 } 10505 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 10506 /* Note: JS_PROP_VARREF is always writable */ 10507 } else { 10508 if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && 10509 (flags & JS_PROP_HAS_VALUE)) { 10510 if (!js_same_value(ctx, val, pr->u.value)) { 10511 goto not_configurable; 10512 } else { 10513 return TRUE; 10514 } 10515 } 10516 } 10517 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 10518 if (flags & JS_PROP_HAS_VALUE) { 10519 if (p->class_id == JS_CLASS_MODULE_NS) { 10520 /* JS_PROP_WRITABLE is always true for variable 10521 references, but they are write protected in module name 10522 spaces. */ 10523 if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) 10524 goto not_configurable; 10525 } else { 10526 /* update the reference */ 10527 set_value(ctx, pr->u.var_ref->pvalue, 10528 JS_DupValue(ctx, val)); 10529 } 10530 } 10531 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { 10532 JSValue val1; 10533 if (p->class_id == JS_CLASS_MODULE_NS) { 10534 return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false"); 10535 } 10536 if (js_shape_prepare_update(ctx, p, &prs)) 10537 return -1; 10538 if (p->class_id == JS_CLASS_GLOBAL_OBJECT) { 10539 pr->u.var_ref->is_const = TRUE; /* mark as read-only */ 10540 prs->flags &= ~JS_PROP_WRITABLE; 10541 } else { 10542 /* if writable is set to false, no longer a 10543 reference (for mapped arguments) */ 10544 val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); 10545 free_var_ref(ctx->rt, pr->u.var_ref); 10546 pr->u.value = val1; 10547 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); 10548 } 10549 } 10550 } else if (prs->flags & JS_PROP_LENGTH) { 10551 if (flags & JS_PROP_HAS_VALUE) { 10552 /* Note: no JS code is executable because 10553 'val' is guaranted to be a Uint32 */ 10554 res = set_array_length(ctx, p, JS_DupValue(ctx, val), 10555 flags); 10556 } else { 10557 res = TRUE; 10558 } 10559 /* still need to reset the writable flag if 10560 needed. The JS_PROP_LENGTH is kept because the 10561 Uint32 test is still done if the length 10562 property is read-only. */ 10563 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == 10564 JS_PROP_HAS_WRITABLE) { 10565 prs = get_shape_prop(p->shape); 10566 if (js_update_property_flags(ctx, p, &prs, 10567 prs->flags & ~JS_PROP_WRITABLE)) 10568 return -1; 10569 } 10570 return res; 10571 } else { 10572 if (flags & JS_PROP_HAS_VALUE) { 10573 JS_FreeValue(ctx, pr->u.value); 10574 pr->u.value = JS_DupValue(ctx, val); 10575 } 10576 if (flags & JS_PROP_HAS_WRITABLE) { 10577 if (js_update_property_flags(ctx, p, &prs, 10578 (prs->flags & ~JS_PROP_WRITABLE) | 10579 (flags & JS_PROP_WRITABLE))) 10580 return -1; 10581 } 10582 } 10583 } 10584 } 10585 mask = 0; 10586 if (flags & JS_PROP_HAS_CONFIGURABLE) 10587 mask |= JS_PROP_CONFIGURABLE; 10588 if (flags & JS_PROP_HAS_ENUMERABLE) 10589 mask |= JS_PROP_ENUMERABLE; 10590 if (js_update_property_flags(ctx, p, &prs, 10591 (prs->flags & ~mask) | (flags & mask))) 10592 return -1; 10593 return TRUE; 10594 } 10595 10596 /* handle modification of fast array elements */ 10597 if (p->fast_array) { 10598 uint32_t idx; 10599 uint32_t prop_flags; 10600 if (p->class_id == JS_CLASS_ARRAY) { 10601 if (__JS_AtomIsTaggedInt(prop)) { 10602 idx = __JS_AtomToUInt32(prop); 10603 if (idx < p->u.array.count) { 10604 prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); 10605 if (prop_flags != JS_PROP_C_W_E) 10606 goto convert_to_slow_array; 10607 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 10608 convert_to_slow_array: 10609 if (convert_fast_array_to_array(ctx, p)) 10610 return -1; 10611 else 10612 goto redo_prop_update; 10613 } 10614 if (flags & JS_PROP_HAS_VALUE) { 10615 set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); 10616 } 10617 return TRUE; 10618 } 10619 } 10620 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 10621 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 10622 JSValue num; 10623 int ret; 10624 10625 if (!__JS_AtomIsTaggedInt(prop)) { 10626 /* slow path with to handle all numeric indexes */ 10627 num = JS_AtomIsNumericIndex1(ctx, prop); 10628 if (JS_IsUndefined(num)) 10629 goto typed_array_done; 10630 if (JS_IsException(num)) 10631 return -1; 10632 ret = JS_NumberIsInteger(ctx, num); 10633 if (ret < 0) { 10634 JS_FreeValue(ctx, num); 10635 return -1; 10636 } 10637 if (!ret) { 10638 JS_FreeValue(ctx, num); 10639 return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); 10640 } 10641 ret = JS_NumberIsNegativeOrMinusZero(ctx, num); 10642 JS_FreeValue(ctx, num); 10643 if (ret) { 10644 return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); 10645 } 10646 if (!__JS_AtomIsTaggedInt(prop)) 10647 goto typed_array_oob; 10648 } 10649 idx = __JS_AtomToUInt32(prop); 10650 /* if the typed array is detached, p->u.array.count = 0 */ 10651 if (idx >= p->u.array.count) { 10652 typed_array_oob: 10653 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); 10654 } 10655 prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 10656 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || 10657 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { 10658 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); 10659 } 10660 if (flags & JS_PROP_HAS_VALUE) { 10661 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); 10662 } 10663 return TRUE; 10664 typed_array_done: ; 10665 } 10666 } 10667 10668 return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); 10669 } 10670 10671 static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, 10672 JSAtom prop, JSAutoInitIDEnum id, 10673 void *opaque, int flags) 10674 { 10675 JSObject *p; 10676 JSProperty *pr; 10677 10678 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) 10679 return FALSE; 10680 10681 p = JS_VALUE_GET_OBJ(this_obj); 10682 10683 if (find_own_property(&pr, p, prop)) { 10684 /* property already exists */ 10685 abort(); 10686 return FALSE; 10687 } 10688 10689 /* Specialized CreateProperty */ 10690 pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); 10691 if (unlikely(!pr)) 10692 return -1; 10693 pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); 10694 assert((pr->u.init.realm_and_id & 3) == 0); 10695 assert(id <= 3); 10696 pr->u.init.realm_and_id |= id; 10697 pr->u.init.opaque = opaque; 10698 return TRUE; 10699 } 10700 10701 /* shortcut to add or redefine a new property value */ 10702 int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, 10703 JSAtom prop, JSValue val, int flags) 10704 { 10705 int ret; 10706 ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, 10707 flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); 10708 JS_FreeValue(ctx, val); 10709 return ret; 10710 } 10711 10712 int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, 10713 JSValue prop, JSValue val, int flags) 10714 { 10715 JSAtom atom; 10716 int ret; 10717 atom = JS_ValueToAtom(ctx, prop); 10718 JS_FreeValue(ctx, prop); 10719 if (unlikely(atom == JS_ATOM_NULL)) { 10720 JS_FreeValue(ctx, val); 10721 return -1; 10722 } 10723 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); 10724 JS_FreeAtom(ctx, atom); 10725 return ret; 10726 } 10727 10728 int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, 10729 uint32_t idx, JSValue val, int flags) 10730 { 10731 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx), 10732 val, flags); 10733 } 10734 10735 int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, 10736 int64_t idx, JSValue val, int flags) 10737 { 10738 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), 10739 val, flags); 10740 } 10741 10742 int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, 10743 const char *prop, JSValue val, int flags) 10744 { 10745 JSAtom atom; 10746 int ret; 10747 atom = JS_NewAtom(ctx, prop); 10748 if (atom == JS_ATOM_NULL) { 10749 JS_FreeValue(ctx, val); 10750 return -1; 10751 } 10752 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); 10753 JS_FreeAtom(ctx, atom); 10754 return ret; 10755 } 10756 10757 /* shortcut to add getter & setter */ 10758 int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, 10759 JSAtom prop, JSValue getter, JSValue setter, 10760 int flags) 10761 { 10762 int ret; 10763 ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, 10764 flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | 10765 JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); 10766 JS_FreeValue(ctx, getter); 10767 JS_FreeValue(ctx, setter); 10768 return ret; 10769 } 10770 10771 static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, 10772 int64_t idx, JSValue val, int flags) 10773 { 10774 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), 10775 val, flags | JS_PROP_CONFIGURABLE | 10776 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); 10777 } 10778 10779 10780 /* return TRUE if 'obj' has a non empty 'name' string */ 10781 static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) 10782 { 10783 JSProperty *pr; 10784 JSShapeProperty *prs; 10785 JSValueConst val; 10786 JSString *p; 10787 10788 prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); 10789 if (!prs) 10790 return FALSE; 10791 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 10792 return TRUE; 10793 val = pr->u.value; 10794 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 10795 return TRUE; 10796 p = JS_VALUE_GET_STRING(val); 10797 return (p->len != 0); 10798 } 10799 10800 static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, 10801 JSAtom name, int flags) 10802 { 10803 if (name != JS_ATOM_NULL 10804 && JS_IsObject(obj) 10805 && !js_object_has_name(ctx, obj) 10806 && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { 10807 return -1; 10808 } 10809 return 0; 10810 } 10811 10812 static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, 10813 JSValueConst str, int flags) 10814 { 10815 if (JS_IsObject(obj) && 10816 !js_object_has_name(ctx, obj)) { 10817 JSAtom prop; 10818 JSValue name_str; 10819 prop = JS_ValueToAtom(ctx, str); 10820 if (prop == JS_ATOM_NULL) 10821 return -1; 10822 name_str = js_get_function_name(ctx, prop); 10823 JS_FreeAtom(ctx, prop); 10824 if (JS_IsException(name_str)) 10825 return -1; 10826 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) 10827 return -1; 10828 } 10829 return 0; 10830 } 10831 10832 #define DEFINE_GLOBAL_LEX_VAR (1 << 7) 10833 #define DEFINE_GLOBAL_FUNC_VAR (1 << 6) 10834 10835 static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) 10836 { 10837 return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); 10838 } 10839 10840 /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ 10841 /* XXX: could support exotic global object. */ 10842 static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) 10843 { 10844 JSObject *p; 10845 JSShapeProperty *prs; 10846 10847 p = JS_VALUE_GET_OBJ(ctx->global_obj); 10848 prs = find_own_property1(p, prop); 10849 /* XXX: should handle JS_PROP_AUTOINIT */ 10850 if (flags & DEFINE_GLOBAL_LEX_VAR) { 10851 if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) 10852 goto fail_redeclaration; 10853 } else { 10854 if (!prs && !p->extensible) 10855 goto define_error; 10856 if (flags & DEFINE_GLOBAL_FUNC_VAR) { 10857 if (prs) { 10858 if (!(prs->flags & JS_PROP_CONFIGURABLE) && 10859 ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || 10860 ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != 10861 (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { 10862 define_error: 10863 JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", 10864 prop); 10865 return -1; 10866 } 10867 } 10868 } 10869 } 10870 /* check if there already is a lexical declaration */ 10871 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 10872 prs = find_own_property1(p, prop); 10873 if (prs) { 10874 fail_redeclaration: 10875 JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); 10876 return -1; 10877 } 10878 return 0; 10879 } 10880 10881 /* construct a reference to a global variable */ 10882 static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) 10883 { 10884 JSObject *p; 10885 JSShapeProperty *prs; 10886 JSProperty *pr; 10887 10888 /* no exotic behavior is possible in global_var_obj */ 10889 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 10890 prs = find_own_property(&pr, p, prop); 10891 if (prs) { 10892 /* XXX: conformance: do these tests in 10893 OP_put_var_ref/OP_get_var_ref ? */ 10894 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { 10895 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 10896 return -1; 10897 } 10898 if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { 10899 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); 10900 } 10901 sp[0] = JS_DupValue(ctx, ctx->global_var_obj); 10902 } else { 10903 int ret; 10904 ret = JS_HasProperty(ctx, ctx->global_obj, prop); 10905 if (ret < 0) 10906 return -1; 10907 if (ret) { 10908 sp[0] = JS_DupValue(ctx, ctx->global_obj); 10909 } else { 10910 sp[0] = JS_UNDEFINED; 10911 } 10912 } 10913 sp[1] = JS_AtomToValue(ctx, prop); 10914 return 0; 10915 } 10916 10917 /* return -1, FALSE or TRUE */ 10918 static int JS_DeleteGlobalVar(JSContext *ctx, JSAtom prop) 10919 { 10920 JSObject *p; 10921 JSShapeProperty *prs; 10922 JSProperty *pr; 10923 int ret; 10924 10925 /* 9.1.1.4.7 DeleteBinding ( N ) */ 10926 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 10927 prs = find_own_property(&pr, p, prop); 10928 if (prs) 10929 return FALSE; /* lexical variables cannot be deleted */ 10930 ret = JS_HasProperty(ctx, ctx->global_obj, prop); 10931 if (ret < 0) 10932 return -1; 10933 if (ret) { 10934 return JS_DeleteProperty(ctx, ctx->global_obj, prop, 0); 10935 } else { 10936 return TRUE; 10937 } 10938 } 10939 10940 /* return -1, FALSE or TRUE. return FALSE if not configurable or 10941 invalid object. return -1 in case of exception. 10942 flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ 10943 int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) 10944 { 10945 JSValue obj1; 10946 JSObject *p; 10947 int res; 10948 10949 obj1 = JS_ToObject(ctx, obj); 10950 if (JS_IsException(obj1)) 10951 return -1; 10952 p = JS_VALUE_GET_OBJ(obj1); 10953 res = delete_property(ctx, p, prop); 10954 JS_FreeValue(ctx, obj1); 10955 if (res != FALSE) 10956 return res; 10957 if ((flags & JS_PROP_THROW) || 10958 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 10959 JS_ThrowTypeError(ctx, "could not delete property"); 10960 return -1; 10961 } 10962 return FALSE; 10963 } 10964 10965 int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) 10966 { 10967 JSAtom prop; 10968 int res; 10969 10970 if ((uint64_t)idx <= JS_ATOM_MAX_INT) { 10971 /* fast path for fast arrays */ 10972 return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); 10973 } 10974 prop = JS_NewAtomInt64(ctx, idx); 10975 if (prop == JS_ATOM_NULL) 10976 return -1; 10977 res = JS_DeleteProperty(ctx, obj, prop, flags); 10978 JS_FreeAtom(ctx, prop); 10979 return res; 10980 } 10981 10982 BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) 10983 { 10984 JSObject *p; 10985 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 10986 return FALSE; 10987 p = JS_VALUE_GET_OBJ(val); 10988 switch(p->class_id) { 10989 case JS_CLASS_BYTECODE_FUNCTION: 10990 return TRUE; 10991 case JS_CLASS_PROXY: 10992 return p->u.proxy_data->is_func; 10993 default: 10994 return (ctx->rt->class_array[p->class_id].call != NULL); 10995 } 10996 } 10997 10998 BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) 10999 { 11000 JSObject *p; 11001 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 11002 return FALSE; 11003 p = JS_VALUE_GET_OBJ(val); 11004 if (p->class_id == JS_CLASS_C_FUNCTION) 11005 return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); 11006 else 11007 return FALSE; 11008 } 11009 11010 BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val) 11011 { 11012 JSObject *p; 11013 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 11014 return FALSE; 11015 p = JS_VALUE_GET_OBJ(val); 11016 return p->is_constructor; 11017 } 11018 11019 BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val) 11020 { 11021 JSObject *p; 11022 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 11023 return FALSE; 11024 p = JS_VALUE_GET_OBJ(func_obj); 11025 p->is_constructor = val; 11026 return TRUE; 11027 } 11028 11029 BOOL JS_IsError(JSContext *ctx, JSValueConst val) 11030 { 11031 JSObject *p; 11032 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 11033 return FALSE; 11034 p = JS_VALUE_GET_OBJ(val); 11035 return (p->class_id == JS_CLASS_ERROR); 11036 } 11037 11038 /* must be called after JS_Throw() */ 11039 void JS_SetUncatchableException(JSContext *ctx, BOOL flag) 11040 { 11041 ctx->rt->current_exception_is_uncatchable = flag; 11042 } 11043 11044 void JS_SetOpaque(JSValue obj, void *opaque) 11045 { 11046 JSObject *p; 11047 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 11048 p = JS_VALUE_GET_OBJ(obj); 11049 p->u.opaque = opaque; 11050 } 11051 } 11052 11053 /* return NULL if not an object of class class_id */ 11054 void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) 11055 { 11056 JSObject *p; 11057 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 11058 return NULL; 11059 p = JS_VALUE_GET_OBJ(obj); 11060 if (p->class_id != class_id) 11061 return NULL; 11062 return p->u.opaque; 11063 } 11064 11065 void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) 11066 { 11067 void *p = JS_GetOpaque(obj, class_id); 11068 if (unlikely(!p)) { 11069 JS_ThrowTypeErrorInvalidClass(ctx, class_id); 11070 } 11071 return p; 11072 } 11073 11074 void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id) 11075 { 11076 JSObject *p; 11077 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 11078 *class_id = 0; 11079 return NULL; 11080 } 11081 p = JS_VALUE_GET_OBJ(obj); 11082 *class_id = p->class_id; 11083 return p->u.opaque; 11084 } 11085 11086 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) 11087 { 11088 int i; 11089 BOOL force_ordinary; 11090 11091 JSAtom method_name; 11092 JSValue method, ret; 11093 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 11094 return val; 11095 force_ordinary = hint & HINT_FORCE_ORDINARY; 11096 hint &= ~HINT_FORCE_ORDINARY; 11097 if (!force_ordinary) { 11098 method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); 11099 if (JS_IsException(method)) 11100 goto exception; 11101 /* ECMA says *If exoticToPrim is not undefined* but tests in 11102 test262 use null as a non callable converter */ 11103 if (!JS_IsUndefined(method) && !JS_IsNull(method)) { 11104 JSAtom atom; 11105 JSValue arg; 11106 switch(hint) { 11107 case HINT_STRING: 11108 atom = JS_ATOM_string; 11109 break; 11110 case HINT_NUMBER: 11111 atom = JS_ATOM_number; 11112 break; 11113 default: 11114 case HINT_NONE: 11115 atom = JS_ATOM_default; 11116 break; 11117 } 11118 arg = JS_AtomToString(ctx, atom); 11119 ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); 11120 JS_FreeValue(ctx, arg); 11121 if (JS_IsException(ret)) 11122 goto exception; 11123 JS_FreeValue(ctx, val); 11124 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) 11125 return ret; 11126 JS_FreeValue(ctx, ret); 11127 return JS_ThrowTypeError(ctx, "toPrimitive"); 11128 } 11129 } 11130 if (hint != HINT_STRING) 11131 hint = HINT_NUMBER; 11132 for(i = 0; i < 2; i++) { 11133 if ((i ^ hint) == 0) { 11134 method_name = JS_ATOM_toString; 11135 } else { 11136 method_name = JS_ATOM_valueOf; 11137 } 11138 method = JS_GetProperty(ctx, val, method_name); 11139 if (JS_IsException(method)) 11140 goto exception; 11141 if (JS_IsFunction(ctx, method)) { 11142 ret = JS_CallFree(ctx, method, val, 0, NULL); 11143 if (JS_IsException(ret)) 11144 goto exception; 11145 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 11146 JS_FreeValue(ctx, val); 11147 return ret; 11148 } 11149 JS_FreeValue(ctx, ret); 11150 } else { 11151 JS_FreeValue(ctx, method); 11152 } 11153 } 11154 JS_ThrowTypeError(ctx, "toPrimitive"); 11155 exception: 11156 JS_FreeValue(ctx, val); 11157 return JS_EXCEPTION; 11158 } 11159 11160 static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) 11161 { 11162 return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); 11163 } 11164 11165 void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) 11166 { 11167 JSObject *p; 11168 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 11169 return; 11170 p = JS_VALUE_GET_OBJ(obj); 11171 p->is_HTMLDDA = TRUE; 11172 } 11173 11174 static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) 11175 { 11176 JSObject *p; 11177 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 11178 return FALSE; 11179 p = JS_VALUE_GET_OBJ(obj); 11180 return p->is_HTMLDDA; 11181 } 11182 11183 static int JS_ToBoolFree(JSContext *ctx, JSValue val) 11184 { 11185 uint32_t tag = JS_VALUE_GET_TAG(val); 11186 switch(tag) { 11187 case JS_TAG_INT: 11188 return JS_VALUE_GET_INT(val) != 0; 11189 case JS_TAG_BOOL: 11190 case JS_TAG_NULL: 11191 case JS_TAG_UNDEFINED: 11192 return JS_VALUE_GET_INT(val); 11193 case JS_TAG_EXCEPTION: 11194 return -1; 11195 case JS_TAG_STRING: 11196 { 11197 BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; 11198 JS_FreeValue(ctx, val); 11199 return ret; 11200 } 11201 case JS_TAG_STRING_ROPE: 11202 { 11203 BOOL ret = JS_VALUE_GET_STRING_ROPE(val)->len != 0; 11204 JS_FreeValue(ctx, val); 11205 return ret; 11206 } 11207 case JS_TAG_SHORT_BIG_INT: 11208 return JS_VALUE_GET_SHORT_BIG_INT(val) != 0; 11209 case JS_TAG_BIG_INT: 11210 { 11211 JSBigInt *p = JS_VALUE_GET_PTR(val); 11212 BOOL ret; 11213 int i; 11214 11215 /* fail safe: we assume it is not necessarily 11216 normalized. Beginning from the MSB ensures that the 11217 test is fast. */ 11218 ret = FALSE; 11219 for(i = p->len - 1; i >= 0; i--) { 11220 if (p->tab[i] != 0) { 11221 ret = TRUE; 11222 break; 11223 } 11224 } 11225 JS_FreeValue(ctx, val); 11226 return ret; 11227 } 11228 case JS_TAG_OBJECT: 11229 { 11230 JSObject *p = JS_VALUE_GET_OBJ(val); 11231 BOOL ret; 11232 ret = !p->is_HTMLDDA; 11233 JS_FreeValue(ctx, val); 11234 return ret; 11235 } 11236 break; 11237 default: 11238 if (JS_TAG_IS_FLOAT64(tag)) { 11239 double d = JS_VALUE_GET_FLOAT64(val); 11240 return !isnan(d) && d != 0; 11241 } else { 11242 JS_FreeValue(ctx, val); 11243 return TRUE; 11244 } 11245 } 11246 } 11247 11248 int JS_ToBool(JSContext *ctx, JSValueConst val) 11249 { 11250 return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); 11251 } 11252 11253 static int skip_spaces(const char *pc) 11254 { 11255 const uint8_t *p, *p_next, *p_start; 11256 uint32_t c; 11257 11258 p = p_start = (const uint8_t *)pc; 11259 for (;;) { 11260 c = *p; 11261 if (c < 128) { 11262 if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) 11263 break; 11264 p++; 11265 } else { 11266 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 11267 if (!lre_is_space(c)) 11268 break; 11269 p = p_next; 11270 } 11271 } 11272 return p - p_start; 11273 } 11274 11275 static inline int to_digit(int c) 11276 { 11277 if (c >= '0' && c <= '9') 11278 return c - '0'; 11279 else if (c >= 'A' && c <= 'Z') 11280 return c - 'A' + 10; 11281 else if (c >= 'a' && c <= 'z') 11282 return c - 'a' + 10; 11283 else 11284 return 36; 11285 } 11286 11287 /* bigint support */ 11288 11289 #define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */ 11290 11291 /* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */ 11292 #if JS_SHORT_BIG_INT_BITS == 32 11293 #define JS_SHORT_BIG_INT_MIN INT32_MIN 11294 #define JS_SHORT_BIG_INT_MAX INT32_MAX 11295 #elif JS_SHORT_BIG_INT_BITS == 64 11296 #define JS_SHORT_BIG_INT_MIN INT64_MIN 11297 #define JS_SHORT_BIG_INT_MAX INT64_MAX 11298 #else 11299 #error unsupported 11300 #endif 11301 11302 #define ADDC(res, carry_out, op1, op2, carry_in) \ 11303 do { \ 11304 js_limb_t __v, __a, __k, __k1; \ 11305 __v = (op1); \ 11306 __a = __v + (op2); \ 11307 __k1 = __a < __v; \ 11308 __k = (carry_in); \ 11309 __a = __a + __k; \ 11310 carry_out = (__a < __k) | __k1; \ 11311 res = __a; \ 11312 } while (0) 11313 11314 #if JS_LIMB_BITS == 32 11315 /* a != 0 */ 11316 static inline js_limb_t js_limb_clz(js_limb_t a) 11317 { 11318 return clz32(a); 11319 } 11320 #else 11321 static inline js_limb_t js_limb_clz(js_limb_t a) 11322 { 11323 return clz64(a); 11324 } 11325 #endif 11326 11327 /* handle a = 0 too */ 11328 static inline js_limb_t js_limb_safe_clz(js_limb_t a) 11329 { 11330 if (a == 0) 11331 return JS_LIMB_BITS; 11332 else 11333 return js_limb_clz(a); 11334 } 11335 11336 static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, 11337 js_limb_t n, js_limb_t carry) 11338 { 11339 int i; 11340 for(i = 0;i < n; i++) { 11341 ADDC(res[i], carry, op1[i], op2[i], carry); 11342 } 11343 return carry; 11344 } 11345 11346 static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2, 11347 int n, js_limb_t carry) 11348 { 11349 int i; 11350 js_limb_t k, a, v, k1; 11351 11352 k = carry; 11353 for(i=0;i<n;i++) { 11354 v = op1[i]; 11355 a = v - op2[i]; 11356 k1 = a > v; 11357 v = a - k; 11358 k = (v > a) | k1; 11359 res[i] = v; 11360 } 11361 return k; 11362 } 11363 11364 /* compute 0 - op2. carry = 0 or 1. */ 11365 static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n) 11366 { 11367 int i; 11368 js_limb_t v, carry; 11369 11370 carry = 1; 11371 for(i=0;i<n;i++) { 11372 v = ~op2[i] + carry; 11373 carry = v < carry; 11374 res[i] = v; 11375 } 11376 return carry; 11377 } 11378 11379 /* tabr[] = taba[] * b + l. Return the high carry */ 11380 static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, 11381 js_limb_t b, js_limb_t l) 11382 { 11383 js_limb_t i; 11384 js_dlimb_t t; 11385 11386 for(i = 0; i < n; i++) { 11387 t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l; 11388 tabr[i] = t; 11389 l = t >> JS_LIMB_BITS; 11390 } 11391 return l; 11392 } 11393 11394 static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, 11395 js_limb_t b, js_limb_t r) 11396 { 11397 js_slimb_t i; 11398 js_dlimb_t a1; 11399 for(i = n - 1; i >= 0; i--) { 11400 a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; 11401 tabr[i] = a1 / b; 11402 r = a1 % b; 11403 } 11404 return r; 11405 } 11406 11407 /* tabr[] += taba[] * b, return the high word. */ 11408 static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, 11409 js_limb_t b) 11410 { 11411 js_limb_t i, l; 11412 js_dlimb_t t; 11413 11414 l = 0; 11415 for(i = 0; i < n; i++) { 11416 t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i]; 11417 tabr[i] = t; 11418 l = t >> JS_LIMB_BITS; 11419 } 11420 return l; 11421 } 11422 11423 /* size of the result : op1_size + op2_size. */ 11424 static void mp_mul_basecase(js_limb_t *result, 11425 const js_limb_t *op1, js_limb_t op1_size, 11426 const js_limb_t *op2, js_limb_t op2_size) 11427 { 11428 int i; 11429 js_limb_t r; 11430 11431 result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); 11432 for(i=1;i<op2_size;i++) { 11433 r = mp_add_mul1(result + i, op1, op1_size, op2[i]); 11434 result[i + op1_size] = r; 11435 } 11436 } 11437 11438 /* tabr[] -= taba[] * b. Return the value to substract to the high 11439 word. */ 11440 static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, 11441 js_limb_t b) 11442 { 11443 js_limb_t i, l; 11444 js_dlimb_t t; 11445 11446 l = 0; 11447 for(i = 0; i < n; i++) { 11448 t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l; 11449 tabr[i] = t; 11450 l = -(t >> JS_LIMB_BITS); 11451 } 11452 return l; 11453 } 11454 11455 /* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */ 11456 static inline js_limb_t udiv1norm_init(js_limb_t d) 11457 { 11458 js_limb_t a0, a1; 11459 a1 = -d - 1; 11460 a0 = -1; 11461 return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d; 11462 } 11463 11464 /* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0 11465 / d' with 0 <= a1 < d. */ 11466 static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0, 11467 js_limb_t d, js_limb_t d_inv) 11468 { 11469 js_limb_t n1m, n_adj, q, r, ah; 11470 js_dlimb_t a; 11471 n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1)); 11472 n_adj = a0 + (n1m & d); 11473 a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj; 11474 q = (a >> JS_LIMB_BITS) + a1; 11475 /* compute a - q * r and update q so that the remainder is\ 11476 between 0 and d - 1 */ 11477 a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0; 11478 a = a - (js_dlimb_t)q * d - d; 11479 ah = a >> JS_LIMB_BITS; 11480 q += 1 + ah; 11481 r = (js_limb_t)a + (ah & d); 11482 *pr = r; 11483 return q; 11484 } 11485 11486 #define UDIV1NORM_THRESHOLD 3 11487 11488 /* b must be >= 1 << (JS_LIMB_BITS - 1) */ 11489 static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n, 11490 js_limb_t b, js_limb_t r) 11491 { 11492 js_slimb_t i; 11493 11494 if (n >= UDIV1NORM_THRESHOLD) { 11495 js_limb_t b_inv; 11496 b_inv = udiv1norm_init(b); 11497 for(i = n - 1; i >= 0; i--) { 11498 tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); 11499 } 11500 } else { 11501 js_dlimb_t a1; 11502 for(i = n - 1; i >= 0; i--) { 11503 a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i]; 11504 tabr[i] = a1 / b; 11505 r = a1 % b; 11506 } 11507 } 11508 return r; 11509 } 11510 11511 /* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb 11512 - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba' 11513 is modified and contains the remainder (nb limbs). tabq[0..na-nb] 11514 contains the quotient with tabq[na - nb] <= 1. */ 11515 static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na, 11516 const js_limb_t *tabb, js_limb_t nb) 11517 { 11518 js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; 11519 int i, j; 11520 11521 b1 = tabb[nb - 1]; 11522 if (nb == 1) { 11523 taba[0] = mp_div1norm(tabq, taba, na, b1, 0); 11524 return; 11525 } 11526 n = na - nb; 11527 11528 if (n >= UDIV1NORM_THRESHOLD) 11529 b1_inv = udiv1norm_init(b1); 11530 else 11531 b1_inv = 0; 11532 11533 /* first iteration: the quotient is only 0 or 1 */ 11534 q = 1; 11535 for(j = nb - 1; j >= 0; j--) { 11536 if (taba[n + j] != tabb[j]) { 11537 if (taba[n + j] < tabb[j]) 11538 q = 0; 11539 break; 11540 } 11541 } 11542 tabq[n] = q; 11543 if (q) { 11544 mp_sub(taba + n, taba + n, tabb, nb, 0); 11545 } 11546 11547 for(i = n - 1; i >= 0; i--) { 11548 if (unlikely(taba[i + nb] >= b1)) { 11549 q = -1; 11550 } else if (b1_inv) { 11551 q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); 11552 } else { 11553 js_dlimb_t al; 11554 al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1]; 11555 q = al / b1; 11556 r = al % b1; 11557 } 11558 r = mp_sub_mul1(taba + i, tabb, nb, q); 11559 11560 v = taba[i + nb]; 11561 a = v - r; 11562 c = (a > v); 11563 taba[i + nb] = a; 11564 11565 if (c != 0) { 11566 /* negative result */ 11567 for(;;) { 11568 q--; 11569 c = mp_add(taba + i, taba + i, tabb, nb, 0); 11570 /* propagate carry and test if positive result */ 11571 if (c != 0) { 11572 if (++taba[i + nb] == 0) { 11573 break; 11574 } 11575 } 11576 } 11577 } 11578 tabq[i] = q; 11579 } 11580 } 11581 11582 /* 1 <= shift <= JS_LIMB_BITS - 1 */ 11583 static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n, 11584 int shift) 11585 { 11586 int i; 11587 js_limb_t l, v; 11588 l = 0; 11589 for(i = 0; i < n; i++) { 11590 v = taba[i]; 11591 tabr[i] = (v << shift) | l; 11592 l = v >> (JS_LIMB_BITS - shift); 11593 } 11594 return l; 11595 } 11596 11597 /* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). 11598 1 <= shift <= LIMB_BITS - 1 */ 11599 static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n, 11600 int shift, js_limb_t high) 11601 { 11602 int i; 11603 js_limb_t l, a; 11604 11605 l = high; 11606 for(i = n - 1; i >= 0; i--) { 11607 a = tab[i]; 11608 tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift)); 11609 l = a; 11610 } 11611 return l & (((js_limb_t)1 << shift) - 1); 11612 } 11613 11614 static JSBigInt *js_bigint_new(JSContext *ctx, int len) 11615 { 11616 JSBigInt *r; 11617 if (len > JS_BIGINT_MAX_SIZE) { 11618 JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); 11619 return NULL; 11620 } 11621 r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t)); 11622 if (!r) 11623 return NULL; 11624 js_rc(r)->ref_count = 1; 11625 r->len = len; 11626 return r; 11627 } 11628 11629 static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a) 11630 { 11631 JSBigInt *r = (JSBigInt *)buf->big_int_buf; 11632 r->len = 1; 11633 r->tab[0] = a; 11634 return r; 11635 } 11636 11637 static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a) 11638 { 11639 #if JS_LIMB_BITS == 64 11640 return js_bigint_set_si(buf, a); 11641 #else 11642 JSBigInt *r = (JSBigInt *)buf->big_int_buf; 11643 if (a >= INT32_MIN && a <= INT32_MAX) { 11644 r->len = 1; 11645 r->tab[0] = a; 11646 } else { 11647 r->len = 2; 11648 r->tab[0] = a; 11649 r->tab[1] = a >> JS_LIMB_BITS; 11650 } 11651 return r; 11652 #endif 11653 } 11654 11655 /* val must be a short big int */ 11656 static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val) 11657 { 11658 return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val)); 11659 } 11660 11661 static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str, 11662 const js_limb_t *tab, int len) 11663 { 11664 int i; 11665 printf("%s: ", str); 11666 for(i = len - 1; i >= 0; i--) { 11667 #if JS_LIMB_BITS == 32 11668 printf(" %08x", tab[i]); 11669 #else 11670 printf(" %016" PRIx64, tab[i]); 11671 #endif 11672 } 11673 printf("\n"); 11674 } 11675 11676 static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str, 11677 const JSBigInt *p) 11678 { 11679 js_bigint_dump1(ctx, str, p->tab, p->len); 11680 } 11681 11682 static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a) 11683 { 11684 JSBigInt *r; 11685 r = js_bigint_new(ctx, 1); 11686 if (!r) 11687 return NULL; 11688 r->tab[0] = a; 11689 return r; 11690 } 11691 11692 static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a) 11693 { 11694 #if JS_LIMB_BITS == 64 11695 return js_bigint_new_si(ctx, a); 11696 #else 11697 if (a >= INT32_MIN && a <= INT32_MAX) { 11698 return js_bigint_new_si(ctx, a); 11699 } else { 11700 JSBigInt *r; 11701 r = js_bigint_new(ctx, 2); 11702 if (!r) 11703 return NULL; 11704 r->tab[0] = a; 11705 r->tab[1] = a >> 32; 11706 return r; 11707 } 11708 #endif 11709 } 11710 11711 static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a) 11712 { 11713 if (a <= INT64_MAX) { 11714 return js_bigint_new_si64(ctx, a); 11715 } else { 11716 JSBigInt *r; 11717 r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS); 11718 if (!r) 11719 return NULL; 11720 #if JS_LIMB_BITS == 64 11721 r->tab[0] = a; 11722 r->tab[1] = 0; 11723 #else 11724 r->tab[0] = a; 11725 r->tab[1] = a >> 32; 11726 r->tab[2] = 0; 11727 #endif 11728 return r; 11729 } 11730 } 11731 11732 static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a) 11733 { 11734 JSBigInt *r; 11735 if (a == (js_slimb_t)a) { 11736 r = js_bigint_new(ctx, 1); 11737 if (!r) 11738 return NULL; 11739 r->tab[0] = a; 11740 } else { 11741 r = js_bigint_new(ctx, 2); 11742 if (!r) 11743 return NULL; 11744 r->tab[0] = a; 11745 r->tab[1] = a >> JS_LIMB_BITS; 11746 } 11747 return r; 11748 } 11749 11750 /* Remove redundant high order limbs. Warning: 'a' may be 11751 reallocated. Can never fail. 11752 */ 11753 static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l) 11754 { 11755 js_limb_t v; 11756 11757 assert(js_rc(a)->ref_count == 1); 11758 while (l > 1) { 11759 v = a->tab[l - 1]; 11760 if ((v != 0 && v != -1) || 11761 (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) { 11762 break; 11763 } 11764 l--; 11765 } 11766 if (l != a->len) { 11767 JSBigInt *a1; 11768 /* realloc to reduce the size */ 11769 a->len = l; 11770 a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t)); 11771 if (a1) 11772 a = a1; 11773 } 11774 return a; 11775 } 11776 11777 static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a) 11778 { 11779 return js_bigint_normalize1(ctx, a, a->len); 11780 } 11781 11782 /* return 0 or 1 depending on the sign */ 11783 static inline int js_bigint_sign(const JSBigInt *a) 11784 { 11785 return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1); 11786 } 11787 11788 static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a) 11789 { 11790 if (a->len == 1) { 11791 return a->tab[0]; 11792 } else { 11793 #if JS_LIMB_BITS == 32 11794 if (js_bigint_sign(a)) 11795 return INT32_MIN; 11796 else 11797 return INT32_MAX; 11798 #else 11799 if (js_bigint_sign(a)) 11800 return INT64_MIN; 11801 else 11802 return INT64_MAX; 11803 #endif 11804 } 11805 } 11806 11807 /* add the op1 limb */ 11808 static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r, 11809 js_limb_t op1) 11810 { 11811 int n2 = r->len; 11812 if ((op1 != 0 && op1 != -1) || 11813 (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) { 11814 JSBigInt *r1; 11815 r1 = js_realloc(ctx, r, 11816 sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t)); 11817 if (!r1) { 11818 js_free(ctx, r); 11819 return NULL; 11820 } 11821 r = r1; 11822 r->len = n2 + 1; 11823 r->tab[n2] = op1; 11824 } else { 11825 /* otherwise still need to normalize the result */ 11826 r = js_bigint_normalize(ctx, r); 11827 } 11828 return r; 11829 } 11830 11831 /* return NULL in case of error. Compute a + b (b_neg = 0) or a - b 11832 (b_neg = 1) */ 11833 /* XXX: optimize */ 11834 static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a, 11835 const JSBigInt *b, int b_neg) 11836 { 11837 JSBigInt *r; 11838 int n1, n2, i; 11839 js_limb_t carry, op1, op2, a_sign, b_sign; 11840 11841 n2 = max_int(a->len, b->len); 11842 n1 = min_int(a->len, b->len); 11843 r = js_bigint_new(ctx, n2); 11844 if (!r) 11845 return NULL; 11846 /* XXX: optimize */ 11847 /* common part */ 11848 carry = b_neg; 11849 for(i = 0; i < n1; i++) { 11850 op1 = a->tab[i]; 11851 op2 = b->tab[i] ^ (-b_neg); 11852 ADDC(r->tab[i], carry, op1, op2, carry); 11853 } 11854 a_sign = -js_bigint_sign(a); 11855 b_sign = (-js_bigint_sign(b)) ^ (-b_neg); 11856 /* part with sign extension of one operand */ 11857 if (a->len > b->len) { 11858 for(i = n1; i < n2; i++) { 11859 op1 = a->tab[i]; 11860 ADDC(r->tab[i], carry, op1, b_sign, carry); 11861 } 11862 } else if (a->len < b->len) { 11863 for(i = n1; i < n2; i++) { 11864 op2 = b->tab[i] ^ (-b_neg); 11865 ADDC(r->tab[i], carry, a_sign, op2, carry); 11866 } 11867 } 11868 11869 /* part with sign extension for both operands. Extend the result 11870 if necessary */ 11871 return js_bigint_extend(ctx, r, a_sign + b_sign + carry); 11872 } 11873 11874 /* XXX: optimize */ 11875 static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a) 11876 { 11877 JSBigIntBuf buf; 11878 JSBigInt *b; 11879 b = js_bigint_set_si(&buf, 0); 11880 return js_bigint_add(ctx, b, a, 1); 11881 } 11882 11883 static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a, 11884 const JSBigInt *b) 11885 { 11886 JSBigInt *r; 11887 11888 r = js_bigint_new(ctx, a->len + b->len); 11889 if (!r) 11890 return NULL; 11891 mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len); 11892 /* correct the result if negative operands (no overflow is 11893 possible) */ 11894 if (js_bigint_sign(a)) 11895 mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0); 11896 if (js_bigint_sign(b)) 11897 mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0); 11898 return js_bigint_normalize(ctx, r); 11899 } 11900 11901 /* return the division or the remainder. 'b' must be != 0. return NULL 11902 in case of exception (division by zero or memory error) */ 11903 static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a, 11904 const JSBigInt *b, BOOL is_rem) 11905 { 11906 JSBigInt *r, *q; 11907 js_limb_t *tabb, h; 11908 int na, nb, a_sign, b_sign, shift; 11909 11910 if (b->len == 1 && b->tab[0] == 0) { 11911 JS_ThrowRangeError(ctx, "BigInt division by zero"); 11912 return NULL; 11913 } 11914 11915 a_sign = js_bigint_sign(a); 11916 b_sign = js_bigint_sign(b); 11917 na = a->len; 11918 nb = b->len; 11919 11920 r = js_bigint_new(ctx, na + 2); 11921 if (!r) 11922 return NULL; 11923 if (a_sign) { 11924 mp_neg(r->tab, a->tab, na); 11925 } else { 11926 memcpy(r->tab, a->tab, na * sizeof(a->tab[0])); 11927 } 11928 /* normalize */ 11929 while (na > 1 && r->tab[na - 1] == 0) 11930 na--; 11931 11932 tabb = js_malloc(ctx, nb * sizeof(tabb[0])); 11933 if (!tabb) { 11934 js_free(ctx, r); 11935 return NULL; 11936 } 11937 if (b_sign) { 11938 mp_neg(tabb, b->tab, nb); 11939 } else { 11940 memcpy(tabb, b->tab, nb * sizeof(tabb[0])); 11941 } 11942 /* normalize */ 11943 while (nb > 1 && tabb[nb - 1] == 0) 11944 nb--; 11945 11946 /* trivial case if 'a' is small */ 11947 if (na < nb) { 11948 js_free(ctx, r); 11949 js_free(ctx, tabb); 11950 if (is_rem) { 11951 /* r = a */ 11952 r = js_bigint_new(ctx, a->len); 11953 if (!r) 11954 return NULL; 11955 memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); 11956 return r; 11957 } else { 11958 /* q = 0 */ 11959 return js_bigint_new_si(ctx, 0); 11960 } 11961 } 11962 11963 /* normalize 'b' */ 11964 shift = js_limb_clz(tabb[nb - 1]); 11965 if (shift != 0) { 11966 mp_shl(tabb, tabb, nb, shift); 11967 h = mp_shl(r->tab, r->tab, na, shift); 11968 if (h != 0) 11969 r->tab[na++] = h; 11970 } 11971 11972 q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */ 11973 if (!q) { 11974 js_free(ctx, r); 11975 js_free(ctx, tabb); 11976 return NULL; 11977 } 11978 11979 // js_bigint_dump1(ctx, "a", r->tab, na); 11980 // js_bigint_dump1(ctx, "b", tabb, nb); 11981 mp_divnorm(q->tab, r->tab, na, tabb, nb); 11982 js_free(ctx, tabb); 11983 11984 if (is_rem) { 11985 js_free(ctx, q); 11986 if (shift != 0) 11987 mp_shr(r->tab, r->tab, nb, shift, 0); 11988 r->tab[nb++] = 0; 11989 if (a_sign) 11990 mp_neg(r->tab, r->tab, nb); 11991 r = js_bigint_normalize1(ctx, r, nb); 11992 return r; 11993 } else { 11994 js_free(ctx, r); 11995 q->tab[na - nb + 1] = 0; 11996 if (a_sign ^ b_sign) { 11997 mp_neg(q->tab, q->tab, q->len); 11998 } 11999 q = js_bigint_normalize(ctx, q); 12000 return q; 12001 } 12002 } 12003 12004 /* and, or, xor */ 12005 static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a, 12006 const JSBigInt *b, OPCodeEnum op) 12007 { 12008 JSBigInt *r; 12009 js_limb_t b_sign; 12010 int a_len, b_len, i; 12011 12012 if (a->len < b->len) { 12013 const JSBigInt *tmp; 12014 tmp = a; 12015 a = b; 12016 b = tmp; 12017 } 12018 /* a_len >= b_len */ 12019 a_len = a->len; 12020 b_len = b->len; 12021 b_sign = -js_bigint_sign(b); 12022 12023 r = js_bigint_new(ctx, a_len); 12024 if (!r) 12025 return NULL; 12026 switch(op) { 12027 case OP_or: 12028 for(i = 0; i < b_len; i++) { 12029 r->tab[i] = a->tab[i] | b->tab[i]; 12030 } 12031 for(i = b_len; i < a_len; i++) { 12032 r->tab[i] = a->tab[i] | b_sign; 12033 } 12034 break; 12035 case OP_and: 12036 for(i = 0; i < b_len; i++) { 12037 r->tab[i] = a->tab[i] & b->tab[i]; 12038 } 12039 for(i = b_len; i < a_len; i++) { 12040 r->tab[i] = a->tab[i] & b_sign; 12041 } 12042 break; 12043 case OP_xor: 12044 for(i = 0; i < b_len; i++) { 12045 r->tab[i] = a->tab[i] ^ b->tab[i]; 12046 } 12047 for(i = b_len; i < a_len; i++) { 12048 r->tab[i] = a->tab[i] ^ b_sign; 12049 } 12050 break; 12051 default: 12052 abort(); 12053 } 12054 return js_bigint_normalize(ctx, r); 12055 } 12056 12057 static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a) 12058 { 12059 JSBigInt *r; 12060 int i; 12061 12062 r = js_bigint_new(ctx, a->len); 12063 if (!r) 12064 return NULL; 12065 for(i = 0; i < a->len; i++) { 12066 r->tab[i] = ~a->tab[i]; 12067 } 12068 /* no normalization is needed */ 12069 return r; 12070 } 12071 12072 static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a, 12073 unsigned int shift1) 12074 { 12075 int d, i, shift; 12076 JSBigInt *r; 12077 js_limb_t l; 12078 12079 if (a->len == 1 && a->tab[0] == 0) 12080 return js_bigint_new_si(ctx, 0); /* zero case */ 12081 d = shift1 / JS_LIMB_BITS; 12082 shift = shift1 % JS_LIMB_BITS; 12083 r = js_bigint_new(ctx, a->len + d); 12084 if (!r) 12085 return NULL; 12086 for(i = 0; i < d; i++) 12087 r->tab[i] = 0; 12088 if (shift == 0) { 12089 for(i = 0; i < a->len; i++) { 12090 r->tab[i + d] = a->tab[i]; 12091 } 12092 } else { 12093 l = mp_shl(r->tab + d, a->tab, a->len, shift); 12094 if (js_bigint_sign(a)) 12095 l |= (js_limb_t)(-1) << shift; 12096 r = js_bigint_extend(ctx, r, l); 12097 } 12098 return r; 12099 } 12100 12101 static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a, 12102 unsigned int shift1) 12103 { 12104 int d, i, shift, a_sign, n1; 12105 JSBigInt *r; 12106 12107 d = shift1 / JS_LIMB_BITS; 12108 shift = shift1 % JS_LIMB_BITS; 12109 a_sign = js_bigint_sign(a); 12110 if (d >= a->len) 12111 return js_bigint_new_si(ctx, -a_sign); 12112 n1 = a->len - d; 12113 r = js_bigint_new(ctx, n1); 12114 if (!r) 12115 return NULL; 12116 if (shift == 0) { 12117 for(i = 0; i < n1; i++) { 12118 r->tab[i] = a->tab[i + d]; 12119 } 12120 /* no normalization is needed */ 12121 } else { 12122 mp_shr(r->tab, a->tab + d, n1, shift, -a_sign); 12123 r = js_bigint_normalize(ctx, r); 12124 } 12125 return r; 12126 } 12127 12128 static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b) 12129 { 12130 uint32_t e; 12131 int n_bits, i; 12132 JSBigInt *r, *r1; 12133 12134 /* b must be >= 0 */ 12135 if (js_bigint_sign(b)) { 12136 JS_ThrowRangeError(ctx, "BigInt negative exponent"); 12137 return NULL; 12138 } 12139 if (b->len == 1 && b->tab[0] == 0) { 12140 /* a^0 = 1 */ 12141 return js_bigint_new_si(ctx, 1); 12142 } else if (a->len == 1) { 12143 js_limb_t v; 12144 BOOL is_neg; 12145 12146 v = a->tab[0]; 12147 if (v <= 1) 12148 return js_bigint_new_si(ctx, v); 12149 else if (v == -1) 12150 return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1)); 12151 is_neg = (js_slimb_t)v < 0; 12152 if (is_neg) 12153 v = -v; 12154 if ((v & (v - 1)) == 0) { 12155 uint64_t e1; 12156 int n; 12157 /* v = 2^n */ 12158 n = JS_LIMB_BITS - 1 - js_limb_clz(v); 12159 if (b->len > 1) 12160 goto overflow; 12161 if (b->tab[0] > INT32_MAX) 12162 goto overflow; 12163 e = b->tab[0]; 12164 e1 = (uint64_t)e * n; 12165 if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) 12166 goto overflow; 12167 e = e1; 12168 if (is_neg) 12169 is_neg = b->tab[0] & 1; 12170 r = js_bigint_new(ctx, 12171 (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS); 12172 if (!r) 12173 return NULL; 12174 memset(r->tab, 0, sizeof(r->tab[0]) * r->len); 12175 r->tab[e / JS_LIMB_BITS] = 12176 (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS); 12177 return r; 12178 } 12179 } 12180 if (b->len > 1) 12181 goto overflow; 12182 if (b->tab[0] > INT32_MAX) 12183 goto overflow; 12184 e = b->tab[0]; 12185 n_bits = 32 - clz32(e); 12186 12187 r = js_bigint_new(ctx, a->len); 12188 if (!r) 12189 return NULL; 12190 memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0])); 12191 for(i = n_bits - 2; i >= 0; i--) { 12192 r1 = js_bigint_mul(ctx, r, r); 12193 if (!r1) 12194 return NULL; 12195 js_free(ctx, r); 12196 r = r1; 12197 if ((e >> i) & 1) { 12198 r1 = js_bigint_mul(ctx, r, a); 12199 if (!r1) 12200 return NULL; 12201 js_free(ctx, r); 12202 r = r1; 12203 } 12204 } 12205 return r; 12206 overflow: 12207 JS_ThrowRangeError(ctx, "BigInt is too large"); 12208 return NULL; 12209 } 12210 12211 /* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits - 12212 1). a must be != 0. */ 12213 static uint64_t js_bigint_get_mant_exp(JSContext *ctx, 12214 int *pexp, const JSBigInt *a) 12215 { 12216 js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits; 12217 int n1, n2, sgn, shift, i, j, e; 12218 uint64_t a1, a0; 12219 12220 n2 = 4 - JS_LIMB_BITS / 32; 12221 n1 = a->len - n2; 12222 sgn = js_bigint_sign(a); 12223 12224 /* low_bits != 0 if there are a non zero low bit in abs(a) */ 12225 low_bits = 0; 12226 carry = sgn; 12227 for(i = 0; i < n1; i++) { 12228 v = (a->tab[i] ^ (-sgn)) + carry; 12229 carry = v < carry; 12230 low_bits |= v; 12231 } 12232 /* get the n2 high limbs of abs(a) */ 12233 for(j = 0; j < n2; j++) { 12234 i = j + n1; 12235 if (i < 0) { 12236 v = 0; 12237 } else { 12238 v = (a->tab[i] ^ (-sgn)) + carry; 12239 carry = v < carry; 12240 } 12241 t[j] = v; 12242 } 12243 12244 #if JS_LIMB_BITS == 32 12245 a1 = ((uint64_t)t[2] << 32) | t[1]; 12246 a0 = (uint64_t)t[0] << 32; 12247 #else 12248 a1 = t[1]; 12249 a0 = t[0]; 12250 #endif 12251 a0 |= (low_bits != 0); 12252 /* normalize */ 12253 if (a1 == 0) { 12254 /* JS_LIMB_BITS = 64 bit only */ 12255 shift = 64; 12256 a1 = a0; 12257 a0 = 0; 12258 } else { 12259 shift = clz64(a1); 12260 if (shift != 0) { 12261 a1 = (a1 << shift) | (a0 >> (64 - shift)); 12262 a0 <<= shift; 12263 } 12264 } 12265 a1 |= (a0 != 0); /* keep the bits for the final rounding */ 12266 /* compute the exponent */ 12267 e = a->len * JS_LIMB_BITS - shift - 1; 12268 *pexp = e; 12269 return a1; 12270 } 12271 12272 /* shift left with round to nearest, ties to even. n >= 1 */ 12273 static uint64_t shr_rndn(uint64_t a, int n) 12274 { 12275 uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1); 12276 return (a + addend) >> n; 12277 } 12278 12279 /* convert to float64 with round to nearest, ties to even. Return 12280 +/-infinity if too large. */ 12281 static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a) 12282 { 12283 int sgn, e; 12284 uint64_t mant; 12285 12286 if (a->len == 1) { 12287 /* fast case, including zero */ 12288 return (double)(js_slimb_t)a->tab[0]; 12289 } 12290 12291 sgn = js_bigint_sign(a); 12292 mant = js_bigint_get_mant_exp(ctx, &e, a); 12293 if (e > 1023) { 12294 /* overflow: return infinity */ 12295 mant = 0; 12296 e = 1024; 12297 } else { 12298 mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */ 12299 mant = shr_rndn(mant, 10); 12300 /* rounding can cause an overflow */ 12301 if (mant >= ((uint64_t)1 << 53)) { 12302 mant >>= 1; 12303 e++; 12304 } 12305 mant &= (((uint64_t)1 << 52) - 1); 12306 } 12307 return uint64_as_float64(((uint64_t)sgn << 63) | 12308 ((uint64_t)(e + 1023) << 52) | 12309 mant); 12310 } 12311 12312 /* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity, 12313 (0, n) if an integer, (0, NULL) in case of memory error */ 12314 static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1) 12315 { 12316 uint64_t a = float64_as_uint64(a1); 12317 int sgn, e, shift; 12318 uint64_t mant; 12319 JSBigIntBuf buf; 12320 JSBigInt *r; 12321 12322 sgn = a >> 63; 12323 e = (a >> 52) & ((1 << 11) - 1); 12324 mant = a & (((uint64_t)1 << 52) - 1); 12325 if (e == 2047) { 12326 /* NaN, Infinity */ 12327 *pres = 2; 12328 return NULL; 12329 } 12330 if (e == 0 && mant == 0) { 12331 /* zero */ 12332 *pres = 0; 12333 return js_bigint_new_si(ctx, 0); 12334 } 12335 e -= 1023; 12336 /* 0 < a < 1 : not an integer */ 12337 if (e < 0) 12338 goto not_an_integer; 12339 mant |= (uint64_t)1 << 52; 12340 if (e < 52) { 12341 shift = 52 - e; 12342 /* check that there is no fractional part */ 12343 if (mant & (((uint64_t)1 << shift) - 1)) { 12344 not_an_integer: 12345 *pres = 1; 12346 return NULL; 12347 } 12348 mant >>= shift; 12349 e = 0; 12350 } else { 12351 e -= 52; 12352 } 12353 if (sgn) 12354 mant = -mant; 12355 /* the integer is mant*2^e */ 12356 r = js_bigint_set_si64(&buf, (int64_t)mant); 12357 *pres = 0; 12358 return js_bigint_shl(ctx, r, e); 12359 } 12360 12361 /* return -1, 0, 1 or (2) (unordered) */ 12362 static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a, 12363 double b) 12364 { 12365 int b_sign, a_sign, e, f; 12366 uint64_t mant, b1, a_mant; 12367 12368 b1 = float64_as_uint64(b); 12369 b_sign = b1 >> 63; 12370 e = (b1 >> 52) & ((1 << 11) - 1); 12371 mant = b1 & (((uint64_t)1 << 52) - 1); 12372 a_sign = js_bigint_sign(a); 12373 if (e == 2047) { 12374 if (mant != 0) { 12375 /* NaN */ 12376 return 2; 12377 } else { 12378 /* +/- infinity */ 12379 return 2 * b_sign - 1; 12380 } 12381 } else if (e == 0 && mant == 0) { 12382 /* b = +/-0 */ 12383 if (a->len == 1 && a->tab[0] == 0) 12384 return 0; 12385 else 12386 return 1 - 2 * a_sign; 12387 } else if (a->len == 1 && a->tab[0] == 0) { 12388 /* a = 0, b != 0 */ 12389 return 2 * b_sign - 1; 12390 } else if (a_sign != b_sign) { 12391 return 1 - 2 * a_sign; 12392 } else { 12393 e -= 1023; 12394 /* Note: handling denormals is not necessary because we 12395 compare to integers hence f >= 0 */ 12396 /* compute f so that 2^f <= abs(a) < 2^(f+1) */ 12397 a_mant = js_bigint_get_mant_exp(ctx, &f, a); 12398 if (f != e) { 12399 if (f < e) 12400 return -1; 12401 else 12402 return 1; 12403 } else { 12404 mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */ 12405 if (a_mant < mant) 12406 return 2 * a_sign - 1; 12407 else if (a_mant > mant) 12408 return 1 - 2 * a_sign; 12409 else 12410 return 0; 12411 } 12412 } 12413 } 12414 12415 /* return -1, 0 or 1 */ 12416 static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a, 12417 const JSBigInt *b) 12418 { 12419 int a_sign, b_sign, res, i; 12420 a_sign = js_bigint_sign(a); 12421 b_sign = js_bigint_sign(b); 12422 if (a_sign != b_sign) { 12423 res = 1 - 2 * a_sign; 12424 } else { 12425 /* we assume the numbers are normalized */ 12426 if (a->len != b->len) { 12427 if (a->len < b->len) 12428 res = 2 * a_sign - 1; 12429 else 12430 res = 1 - 2 * a_sign; 12431 } else { 12432 res = 0; 12433 for(i = a->len -1; i >= 0; i--) { 12434 if (a->tab[i] != b->tab[i]) { 12435 if (a->tab[i] < b->tab[i]) 12436 res = -1; 12437 else 12438 res = 1; 12439 break; 12440 } 12441 } 12442 } 12443 } 12444 return res; 12445 } 12446 12447 /* contains 10^i */ 12448 static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = { 12449 1U, 12450 10U, 12451 100U, 12452 1000U, 12453 10000U, 12454 100000U, 12455 1000000U, 12456 10000000U, 12457 100000000U, 12458 1000000000U, 12459 #if JS_LIMB_BITS == 64 12460 10000000000U, 12461 100000000000U, 12462 1000000000000U, 12463 10000000000000U, 12464 100000000000000U, 12465 1000000000000000U, 12466 10000000000000000U, 12467 100000000000000000U, 12468 1000000000000000000U, 12469 10000000000000000000U, 12470 #endif 12471 }; 12472 12473 /* syntax: [-]digits in base radix. Return NULL if memory error. radix 12474 = 10, 2, 8 or 16. */ 12475 static JSBigInt *js_bigint_from_string(JSContext *ctx, 12476 const char *str, int radix) 12477 { 12478 const char *p = str; 12479 size_t n_digits1; 12480 int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i; 12481 JSBigInt *r; 12482 js_limb_t v, c, h; 12483 12484 is_neg = 0; 12485 if (*p == '-') { 12486 is_neg = 1; 12487 p++; 12488 } 12489 while (*p == '0') 12490 p++; 12491 n_digits1 = strlen(p); 12492 /* the real check for overflox is done js_bigint_new(). Here 12493 we just avoid integer overflow */ 12494 if (n_digits1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS) { 12495 JS_ThrowRangeError(ctx, "BigInt is too large to allocate"); 12496 return NULL; 12497 } 12498 n_digits = n_digits1; 12499 log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */ 12500 /* compute the maximum number of limbs */ 12501 if (radix == 10) { 12502 n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */ 12503 } else { 12504 n_bits = n_digits * log2_radix; 12505 } 12506 /* we add one extra bit for the sign */ 12507 n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1); 12508 r = js_bigint_new(ctx, n_limbs); 12509 if (!r) 12510 return NULL; 12511 if (radix == 10) { 12512 int digits_per_limb = JS_LIMB_DIGITS; 12513 len = 1; 12514 r->tab[0] = 0; 12515 for(;;) { 12516 /* XXX: slow */ 12517 v = 0; 12518 for(i = 0; i < digits_per_limb; i++) { 12519 c = to_digit(*p); 12520 if (c >= radix) 12521 break; 12522 p++; 12523 v = v * 10 + c; 12524 } 12525 if (i == 0) 12526 break; 12527 if (len == 1 && r->tab[0] == 0) { 12528 r->tab[0] = v; 12529 } else { 12530 h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v); 12531 if (h != 0) { 12532 r->tab[len++] = h; 12533 } 12534 } 12535 } 12536 /* add one extra limb to have the correct sign*/ 12537 if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0) 12538 r->tab[len++] = 0; 12539 r->len = len; 12540 } else { 12541 unsigned int bit_pos, shift, pos; 12542 12543 /* power of two base: no multiplication is needed */ 12544 r->len = n_limbs; 12545 memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs); 12546 for(i = 0; i < n_digits; i++) { 12547 c = to_digit(p[n_digits - 1 - i]); 12548 assert(c < radix); 12549 bit_pos = i * log2_radix; 12550 shift = bit_pos & (JS_LIMB_BITS - 1); 12551 pos = bit_pos / JS_LIMB_BITS; 12552 r->tab[pos] |= c << shift; 12553 /* if log2_radix does not divide JS_LIMB_BITS, needed an 12554 additional op */ 12555 if (shift + log2_radix > JS_LIMB_BITS) { 12556 r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift); 12557 } 12558 } 12559 } 12560 r = js_bigint_normalize(ctx, r); 12561 /* XXX: could do it in place */ 12562 if (is_neg) { 12563 JSBigInt *r1; 12564 r1 = js_bigint_neg(ctx, r); 12565 js_free(ctx, r); 12566 r = r1; 12567 } 12568 return r; 12569 } 12570 12571 /* 2 <= base <= 36 */ 12572 static char const digits[36] = { 12573 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 12574 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 12575 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' 12576 }; 12577 12578 /* special version going backwards */ 12579 /* XXX: use dtoa.c */ 12580 static char *js_u64toa(char *q, int64_t n, unsigned int base) 12581 { 12582 int digit; 12583 if (base == 10) { 12584 /* division by known base uses multiplication */ 12585 do { 12586 digit = (uint64_t)n % 10; 12587 n = (uint64_t)n / 10; 12588 *--q = '0' + digit; 12589 } while (n != 0); 12590 } else { 12591 do { 12592 digit = (uint64_t)n % base; 12593 n = (uint64_t)n / base; 12594 *--q = digits[digit]; 12595 } while (n != 0); 12596 } 12597 return q; 12598 } 12599 12600 /* len >= 1. 2 <= radix <= 36 */ 12601 static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len) 12602 { 12603 int digit, i; 12604 12605 if (radix == 10) { 12606 /* specific case with constant divisor */ 12607 /* XXX: optimize */ 12608 for(i = 0; i < len; i++) { 12609 digit = (js_limb_t)n % 10; 12610 n = (js_limb_t)n / 10; 12611 *--q = digit + '0'; 12612 } 12613 } else { 12614 for(i = 0; i < len; i++) { 12615 digit = (js_limb_t)n % radix; 12616 n = (js_limb_t)n / radix; 12617 *--q = digits[digit]; 12618 } 12619 } 12620 return q; 12621 } 12622 12623 #define JS_RADIX_MAX 36 12624 12625 static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = { 12626 #if JS_LIMB_BITS == 32 12627 32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12628 #else 12629 64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, 12630 #endif 12631 }; 12632 12633 static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = { 12634 #if JS_LIMB_BITS == 32 12635 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395, 12636 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91, 12637 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021, 12638 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571, 12639 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d, 12640 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51, 12641 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899, 12642 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1, 12643 0x5c13d840, 0x6d91b519, 0x81bf1000, 12644 #else 12645 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d, 12646 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821, 12647 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d, 12648 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1, 12649 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49, 12650 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9, 12651 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd, 12652 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981, 12653 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000, 12654 #endif 12655 }; 12656 12657 static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) 12658 { 12659 if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { 12660 char buf[66]; 12661 int len; 12662 len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix); 12663 return js_new_string8_len(ctx, buf, len); 12664 } else { 12665 JSBigInt *r, *tmp = NULL; 12666 char *buf, *q, *buf_end; 12667 int is_neg, n_bits, log2_radix, n_digits; 12668 BOOL is_binary_radix; 12669 JSValue res; 12670 12671 assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT); 12672 r = JS_VALUE_GET_PTR(val); 12673 if (r->len == 1 && r->tab[0] == 0) { 12674 /* '0' case */ 12675 return js_new_string8_len(ctx, "0", 1); 12676 } 12677 is_binary_radix = ((radix & (radix - 1)) == 0); 12678 is_neg = js_bigint_sign(r); 12679 if (is_neg) { 12680 tmp = js_bigint_neg(ctx, r); 12681 if (!tmp) 12682 return JS_EXCEPTION; 12683 r = tmp; 12684 } else if (!is_binary_radix) { 12685 /* need to modify 'r' */ 12686 tmp = js_bigint_new(ctx, r->len); 12687 if (!tmp) 12688 return JS_EXCEPTION; 12689 memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0])); 12690 r = tmp; 12691 } 12692 log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */ 12693 n_bits = r->len * JS_LIMB_BITS - js_limb_safe_clz(r->tab[r->len - 1]); 12694 /* n_digits is exact only if radix is a power of 12695 two. Otherwise it is >= the exact number of digits */ 12696 n_digits = (n_bits + log2_radix - 1) / log2_radix; 12697 /* XXX: could directly build the JSString */ 12698 buf = js_malloc(ctx, n_digits + is_neg + 1); 12699 if (!buf) { 12700 js_free(ctx, tmp); 12701 return JS_EXCEPTION; 12702 } 12703 q = buf + n_digits + is_neg + 1; 12704 *--q = '\0'; 12705 buf_end = q; 12706 if (!is_binary_radix) { 12707 int len; 12708 js_limb_t radix_base, v; 12709 radix_base = radix_base_table[radix - 2]; 12710 len = r->len; 12711 for(;;) { 12712 /* remove leading zero limbs */ 12713 while (len > 1 && r->tab[len - 1] == 0) 12714 len--; 12715 if (len == 1 && r->tab[0] < radix_base) { 12716 v = r->tab[0]; 12717 if (v != 0) { 12718 q = js_u64toa(q, v, radix); 12719 } 12720 break; 12721 } else { 12722 v = mp_div1(r->tab, r->tab, len, radix_base, 0); 12723 q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]); 12724 } 12725 } 12726 } else { 12727 int i, shift; 12728 unsigned int bit_pos, pos, c; 12729 12730 /* radix is a power of two */ 12731 for(i = 0; i < n_digits; i++) { 12732 bit_pos = i * log2_radix; 12733 pos = bit_pos / JS_LIMB_BITS; 12734 shift = bit_pos % JS_LIMB_BITS; 12735 c = r->tab[pos] >> shift; 12736 if ((shift + log2_radix) > JS_LIMB_BITS && 12737 (pos + 1) < r->len) { 12738 c |= r->tab[pos + 1] << (JS_LIMB_BITS - shift); 12739 } 12740 c &= (radix - 1); 12741 *--q = digits[c]; 12742 } 12743 } 12744 if (is_neg) 12745 *--q = '-'; 12746 js_free(ctx, tmp); 12747 res = js_new_string8_len(ctx, q, buf_end - q); 12748 js_free(ctx, buf); 12749 return res; 12750 } 12751 } 12752 12753 /* if possible transform a BigInt to short big and free it, otherwise 12754 return a normal bigint */ 12755 static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p) 12756 { 12757 JSValue res; 12758 if (p->len == 1) { 12759 res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]); 12760 js_free(ctx, p); 12761 return res; 12762 } else { 12763 return JS_MKPTR(JS_TAG_BIG_INT, p); 12764 } 12765 } 12766 12767 #define ATOD_INT_ONLY (1 << 0) 12768 /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ 12769 #define ATOD_ACCEPT_BIN_OCT (1 << 2) 12770 /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ 12771 #define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) 12772 /* accept _ between digits as a digit separator */ 12773 #define ATOD_ACCEPT_UNDERSCORES (1 << 5) 12774 /* allow a suffix to override the type */ 12775 #define ATOD_ACCEPT_SUFFIX (1 << 6) 12776 /* default type */ 12777 #define ATOD_TYPE_MASK (3 << 7) 12778 #define ATOD_TYPE_FLOAT64 (0 << 7) 12779 #define ATOD_TYPE_BIG_INT (1 << 7) 12780 /* accept -0x1 */ 12781 #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) 12782 12783 /* return an exception in case of memory error. Return JS_NAN if 12784 invalid syntax */ 12785 /* XXX: directly use js_atod() */ 12786 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, 12787 int radix, int flags) 12788 { 12789 const char *p, *p_start; 12790 int sep, is_neg; 12791 BOOL is_float, has_legacy_octal; 12792 int atod_type = flags & ATOD_TYPE_MASK; 12793 char buf1[64], *buf; 12794 int i, j, len; 12795 BOOL buf_allocated = FALSE; 12796 JSValue val; 12797 JSATODTempMem atod_mem; 12798 12799 /* optional separator between digits */ 12800 sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; 12801 has_legacy_octal = FALSE; 12802 12803 p = str; 12804 p_start = p; 12805 is_neg = 0; 12806 if (p[0] == '+') { 12807 p++; 12808 p_start++; 12809 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) 12810 goto no_radix_prefix; 12811 } else if (p[0] == '-') { 12812 p++; 12813 p_start++; 12814 is_neg = 1; 12815 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) 12816 goto no_radix_prefix; 12817 } 12818 if (p[0] == '0') { 12819 if ((p[1] == 'x' || p[1] == 'X') && 12820 (radix == 0 || radix == 16)) { 12821 p += 2; 12822 radix = 16; 12823 } else if ((p[1] == 'o' || p[1] == 'O') && 12824 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { 12825 p += 2; 12826 radix = 8; 12827 } else if ((p[1] == 'b' || p[1] == 'B') && 12828 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { 12829 p += 2; 12830 radix = 2; 12831 } else if ((p[1] >= '0' && p[1] <= '9') && 12832 radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { 12833 int i; 12834 has_legacy_octal = TRUE; 12835 sep = 256; 12836 for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) 12837 continue; 12838 if (p[i] == '8' || p[i] == '9') 12839 goto no_prefix; 12840 p += 1; 12841 radix = 8; 12842 } else { 12843 goto no_prefix; 12844 } 12845 /* there must be a digit after the prefix */ 12846 if (to_digit((uint8_t)*p) >= radix) 12847 goto fail; 12848 no_prefix: ; 12849 } else { 12850 no_radix_prefix: 12851 if (!(flags & ATOD_INT_ONLY) && 12852 (atod_type == ATOD_TYPE_FLOAT64) && 12853 strstart(p, "Infinity", &p)) { 12854 double d = 1.0 / 0.0; 12855 if (is_neg) 12856 d = -d; 12857 val = JS_NewFloat64(ctx, d); 12858 goto done; 12859 } 12860 } 12861 if (radix == 0) 12862 radix = 10; 12863 is_float = FALSE; 12864 p_start = p; 12865 while (to_digit((uint8_t)*p) < radix 12866 || (*p == sep && (radix != 10 || 12867 p != p_start + 1 || p[-1] != '0') && 12868 to_digit((uint8_t)p[1]) < radix)) { 12869 p++; 12870 } 12871 if (!(flags & ATOD_INT_ONLY) && radix == 10) { 12872 if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { 12873 is_float = TRUE; 12874 p++; 12875 if (*p == sep) 12876 goto fail; 12877 while (to_digit((uint8_t)*p) < radix || 12878 (*p == sep && to_digit((uint8_t)p[1]) < radix)) 12879 p++; 12880 } 12881 if (p > p_start && (*p == 'e' || *p == 'E')) { 12882 const char *p1 = p + 1; 12883 is_float = TRUE; 12884 if (*p1 == '+') { 12885 p1++; 12886 } else if (*p1 == '-') { 12887 p1++; 12888 } 12889 if (is_digit((uint8_t)*p1)) { 12890 p = p1 + 1; 12891 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) 12892 p++; 12893 } 12894 } 12895 } 12896 if (p == p_start) 12897 goto fail; 12898 12899 buf = buf1; 12900 buf_allocated = FALSE; 12901 len = p - p_start; 12902 if (unlikely((len + 2) > sizeof(buf1))) { 12903 buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ 12904 if (!buf) 12905 goto mem_error; 12906 buf_allocated = TRUE; 12907 } 12908 /* remove the separators and the radix prefixes */ 12909 j = 0; 12910 if (is_neg) 12911 buf[j++] = '-'; 12912 for (i = 0; i < len; i++) { 12913 if (p_start[i] != '_') 12914 buf[j++] = p_start[i]; 12915 } 12916 buf[j] = '\0'; 12917 12918 if ((flags & ATOD_ACCEPT_SUFFIX) && *p == 'n') { 12919 p++; 12920 atod_type = ATOD_TYPE_BIG_INT; 12921 } 12922 12923 switch(atod_type) { 12924 case ATOD_TYPE_FLOAT64: 12925 { 12926 double d; 12927 d = js_atod(buf, NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY, 12928 &atod_mem); 12929 /* return int or float64 */ 12930 val = JS_NewFloat64(ctx, d); 12931 } 12932 break; 12933 case ATOD_TYPE_BIG_INT: 12934 { 12935 JSBigInt *r; 12936 if (has_legacy_octal || is_float) 12937 goto fail; 12938 r = js_bigint_from_string(ctx, buf, radix); 12939 if (!r) { 12940 val = JS_EXCEPTION; 12941 goto done; 12942 } 12943 val = JS_CompactBigInt(ctx, r); 12944 } 12945 break; 12946 default: 12947 abort(); 12948 } 12949 12950 done: 12951 if (buf_allocated) 12952 js_free_rt(ctx->rt, buf); 12953 if (pp) 12954 *pp = p; 12955 return val; 12956 fail: 12957 val = JS_NAN; 12958 goto done; 12959 mem_error: 12960 val = JS_ThrowOutOfMemory(ctx); 12961 goto done; 12962 } 12963 12964 typedef enum JSToNumberHintEnum { 12965 TON_FLAG_NUMBER, 12966 TON_FLAG_NUMERIC, 12967 } JSToNumberHintEnum; 12968 12969 static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, 12970 JSToNumberHintEnum flag) 12971 { 12972 uint32_t tag; 12973 JSValue ret; 12974 12975 redo: 12976 tag = JS_VALUE_GET_NORM_TAG(val); 12977 switch(tag) { 12978 case JS_TAG_BIG_INT: 12979 case JS_TAG_SHORT_BIG_INT: 12980 if (flag != TON_FLAG_NUMERIC) { 12981 JS_FreeValue(ctx, val); 12982 return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); 12983 } 12984 ret = val; 12985 break; 12986 case JS_TAG_FLOAT64: 12987 case JS_TAG_INT: 12988 case JS_TAG_EXCEPTION: 12989 ret = val; 12990 break; 12991 case JS_TAG_BOOL: 12992 case JS_TAG_NULL: 12993 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); 12994 break; 12995 case JS_TAG_UNDEFINED: 12996 ret = JS_NAN; 12997 break; 12998 case JS_TAG_OBJECT: 12999 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 13000 if (JS_IsException(val)) 13001 return JS_EXCEPTION; 13002 goto redo; 13003 case JS_TAG_STRING: 13004 case JS_TAG_STRING_ROPE: 13005 { 13006 const char *str; 13007 const char *p; 13008 size_t len; 13009 13010 str = JS_ToCStringLen(ctx, &len, val); 13011 JS_FreeValue(ctx, val); 13012 if (!str) 13013 return JS_EXCEPTION; 13014 p = str; 13015 p += skip_spaces(p); 13016 if ((p - str) == len) { 13017 ret = JS_NewInt32(ctx, 0); 13018 } else { 13019 int flags = ATOD_ACCEPT_BIN_OCT; 13020 ret = js_atof(ctx, p, &p, 0, flags); 13021 if (!JS_IsException(ret)) { 13022 p += skip_spaces(p); 13023 if ((p - str) != len) { 13024 JS_FreeValue(ctx, ret); 13025 ret = JS_NAN; 13026 } 13027 } 13028 } 13029 JS_FreeCString(ctx, str); 13030 } 13031 break; 13032 case JS_TAG_SYMBOL: 13033 JS_FreeValue(ctx, val); 13034 return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); 13035 default: 13036 JS_FreeValue(ctx, val); 13037 ret = JS_NAN; 13038 break; 13039 } 13040 return ret; 13041 } 13042 13043 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) 13044 { 13045 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); 13046 } 13047 13048 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) 13049 { 13050 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); 13051 } 13052 13053 static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) 13054 { 13055 return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); 13056 } 13057 13058 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, 13059 JSValue val) 13060 { 13061 double d; 13062 uint32_t tag; 13063 13064 val = JS_ToNumberFree(ctx, val); 13065 if (JS_IsException(val)) 13066 goto fail; 13067 tag = JS_VALUE_GET_NORM_TAG(val); 13068 switch(tag) { 13069 case JS_TAG_INT: 13070 d = JS_VALUE_GET_INT(val); 13071 break; 13072 case JS_TAG_FLOAT64: 13073 d = JS_VALUE_GET_FLOAT64(val); 13074 break; 13075 default: 13076 abort(); 13077 } 13078 *pres = d; 13079 return 0; 13080 fail: 13081 *pres = JS_FLOAT64_NAN; 13082 return -1; 13083 } 13084 13085 static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) 13086 { 13087 uint32_t tag; 13088 13089 tag = JS_VALUE_GET_TAG(val); 13090 if (tag <= JS_TAG_NULL) { 13091 *pres = JS_VALUE_GET_INT(val); 13092 return 0; 13093 } else if (JS_TAG_IS_FLOAT64(tag)) { 13094 *pres = JS_VALUE_GET_FLOAT64(val); 13095 return 0; 13096 } else { 13097 return __JS_ToFloat64Free(ctx, pres, val); 13098 } 13099 } 13100 13101 int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) 13102 { 13103 return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); 13104 } 13105 13106 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) 13107 { 13108 return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); 13109 } 13110 13111 /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ 13112 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) 13113 { 13114 uint32_t tag; 13115 JSValue ret; 13116 13117 redo: 13118 tag = JS_VALUE_GET_NORM_TAG(val); 13119 switch(tag) { 13120 case JS_TAG_INT: 13121 case JS_TAG_BOOL: 13122 case JS_TAG_NULL: 13123 case JS_TAG_UNDEFINED: 13124 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); 13125 break; 13126 case JS_TAG_FLOAT64: 13127 { 13128 double d = JS_VALUE_GET_FLOAT64(val); 13129 if (isnan(d)) { 13130 ret = JS_NewInt32(ctx, 0); 13131 } else { 13132 /* convert -0 to +0 */ 13133 d = trunc(d) + 0.0; 13134 ret = JS_NewFloat64(ctx, d); 13135 } 13136 } 13137 break; 13138 default: 13139 val = JS_ToNumberFree(ctx, val); 13140 if (JS_IsException(val)) 13141 return val; 13142 goto redo; 13143 } 13144 return ret; 13145 } 13146 13147 /* Note: the integer value is satured to 32 bits */ 13148 static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) 13149 { 13150 uint32_t tag; 13151 int ret; 13152 13153 redo: 13154 tag = JS_VALUE_GET_NORM_TAG(val); 13155 switch(tag) { 13156 case JS_TAG_INT: 13157 case JS_TAG_BOOL: 13158 case JS_TAG_NULL: 13159 case JS_TAG_UNDEFINED: 13160 ret = JS_VALUE_GET_INT(val); 13161 break; 13162 case JS_TAG_EXCEPTION: 13163 *pres = 0; 13164 return -1; 13165 case JS_TAG_FLOAT64: 13166 { 13167 double d = JS_VALUE_GET_FLOAT64(val); 13168 if (isnan(d)) { 13169 ret = 0; 13170 } else { 13171 if (d < INT32_MIN) 13172 ret = INT32_MIN; 13173 else if (d > INT32_MAX) 13174 ret = INT32_MAX; 13175 else 13176 ret = (int)d; 13177 } 13178 } 13179 break; 13180 default: 13181 val = JS_ToNumberFree(ctx, val); 13182 if (JS_IsException(val)) { 13183 *pres = 0; 13184 return -1; 13185 } 13186 goto redo; 13187 } 13188 *pres = ret; 13189 return 0; 13190 } 13191 13192 int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) 13193 { 13194 return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); 13195 } 13196 13197 int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, 13198 int min, int max, int min_offset) 13199 { 13200 int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); 13201 if (res == 0) { 13202 if (*pres < min) { 13203 *pres += min_offset; 13204 if (*pres < min) 13205 *pres = min; 13206 } else { 13207 if (*pres > max) 13208 *pres = max; 13209 } 13210 } 13211 return res; 13212 } 13213 13214 static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) 13215 { 13216 uint32_t tag; 13217 13218 redo: 13219 tag = JS_VALUE_GET_NORM_TAG(val); 13220 switch(tag) { 13221 case JS_TAG_INT: 13222 case JS_TAG_BOOL: 13223 case JS_TAG_NULL: 13224 case JS_TAG_UNDEFINED: 13225 *pres = JS_VALUE_GET_INT(val); 13226 return 0; 13227 case JS_TAG_EXCEPTION: 13228 *pres = 0; 13229 return -1; 13230 case JS_TAG_FLOAT64: 13231 { 13232 double d = JS_VALUE_GET_FLOAT64(val); 13233 if (isnan(d)) { 13234 *pres = 0; 13235 } else { 13236 if (d < INT64_MIN) 13237 *pres = INT64_MIN; 13238 else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ 13239 *pres = INT64_MAX; 13240 else 13241 *pres = (int64_t)d; 13242 } 13243 } 13244 return 0; 13245 default: 13246 val = JS_ToNumberFree(ctx, val); 13247 if (JS_IsException(val)) { 13248 *pres = 0; 13249 return -1; 13250 } 13251 goto redo; 13252 } 13253 } 13254 13255 int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) 13256 { 13257 return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); 13258 } 13259 13260 int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, 13261 int64_t min, int64_t max, int64_t neg_offset) 13262 { 13263 int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); 13264 if (res == 0) { 13265 if (*pres < 0) 13266 *pres += neg_offset; 13267 if (*pres < min) 13268 *pres = min; 13269 else if (*pres > max) 13270 *pres = max; 13271 } 13272 return res; 13273 } 13274 13275 /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) 13276 in case of exception */ 13277 static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 13278 { 13279 uint32_t tag; 13280 int64_t ret; 13281 13282 redo: 13283 tag = JS_VALUE_GET_NORM_TAG(val); 13284 switch(tag) { 13285 case JS_TAG_INT: 13286 case JS_TAG_BOOL: 13287 case JS_TAG_NULL: 13288 case JS_TAG_UNDEFINED: 13289 ret = JS_VALUE_GET_INT(val); 13290 break; 13291 case JS_TAG_FLOAT64: 13292 { 13293 JSFloat64Union u; 13294 double d; 13295 int e; 13296 d = JS_VALUE_GET_FLOAT64(val); 13297 u.d = d; 13298 /* we avoid doing fmod(x, 2^64) */ 13299 e = (u.u64 >> 52) & 0x7ff; 13300 if (likely(e <= (1023 + 62))) { 13301 /* fast case */ 13302 ret = (int64_t)d; 13303 } else if (e <= (1023 + 62 + 53)) { 13304 uint64_t v; 13305 /* remainder modulo 2^64 */ 13306 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); 13307 ret = v << ((e - 1023) - 52); 13308 /* take the sign into account */ 13309 if (u.u64 >> 63) 13310 ret = -ret; 13311 } else { 13312 ret = 0; /* also handles NaN and +inf */ 13313 } 13314 } 13315 break; 13316 default: 13317 val = JS_ToNumberFree(ctx, val); 13318 if (JS_IsException(val)) { 13319 *pres = 0; 13320 return -1; 13321 } 13322 goto redo; 13323 } 13324 *pres = ret; 13325 return 0; 13326 } 13327 13328 int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) 13329 { 13330 return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); 13331 } 13332 13333 int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) 13334 { 13335 if (JS_IsBigInt(ctx, val)) 13336 return JS_ToBigInt64(ctx, pres, val); 13337 else 13338 return JS_ToInt64(ctx, pres, val); 13339 } 13340 13341 /* return (<0, 0) in case of exception */ 13342 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) 13343 { 13344 uint32_t tag; 13345 int32_t ret; 13346 13347 redo: 13348 tag = JS_VALUE_GET_NORM_TAG(val); 13349 switch(tag) { 13350 case JS_TAG_INT: 13351 case JS_TAG_BOOL: 13352 case JS_TAG_NULL: 13353 case JS_TAG_UNDEFINED: 13354 ret = JS_VALUE_GET_INT(val); 13355 break; 13356 case JS_TAG_FLOAT64: 13357 { 13358 JSFloat64Union u; 13359 double d; 13360 int e; 13361 d = JS_VALUE_GET_FLOAT64(val); 13362 u.d = d; 13363 /* we avoid doing fmod(x, 2^32) */ 13364 e = (u.u64 >> 52) & 0x7ff; 13365 if (likely(e <= (1023 + 30))) { 13366 /* fast case */ 13367 ret = (int32_t)d; 13368 } else if (e <= (1023 + 30 + 53)) { 13369 uint64_t v; 13370 /* remainder modulo 2^32 */ 13371 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); 13372 v = v << ((e - 1023) - 52 + 32); 13373 ret = v >> 32; 13374 /* take the sign into account */ 13375 if (u.u64 >> 63) 13376 ret = -ret; 13377 } else { 13378 ret = 0; /* also handles NaN and +inf */ 13379 } 13380 } 13381 break; 13382 default: 13383 val = JS_ToNumberFree(ctx, val); 13384 if (JS_IsException(val)) { 13385 *pres = 0; 13386 return -1; 13387 } 13388 goto redo; 13389 } 13390 *pres = ret; 13391 return 0; 13392 } 13393 13394 int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) 13395 { 13396 return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); 13397 } 13398 13399 static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) 13400 { 13401 return JS_ToInt32Free(ctx, (int32_t *)pres, val); 13402 } 13403 13404 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) 13405 { 13406 uint32_t tag; 13407 int res; 13408 13409 redo: 13410 tag = JS_VALUE_GET_NORM_TAG(val); 13411 switch(tag) { 13412 case JS_TAG_INT: 13413 case JS_TAG_BOOL: 13414 case JS_TAG_NULL: 13415 case JS_TAG_UNDEFINED: 13416 res = JS_VALUE_GET_INT(val); 13417 res = max_int(0, min_int(255, res)); 13418 break; 13419 case JS_TAG_FLOAT64: 13420 { 13421 double d = JS_VALUE_GET_FLOAT64(val); 13422 if (isnan(d)) { 13423 res = 0; 13424 } else { 13425 if (d < 0) 13426 res = 0; 13427 else if (d > 255) 13428 res = 255; 13429 else 13430 res = lrint(d); 13431 } 13432 } 13433 break; 13434 default: 13435 val = JS_ToNumberFree(ctx, val); 13436 if (JS_IsException(val)) { 13437 *pres = 0; 13438 return -1; 13439 } 13440 goto redo; 13441 } 13442 *pres = res; 13443 return 0; 13444 } 13445 13446 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, 13447 JSValue val, BOOL is_array_ctor) 13448 { 13449 uint32_t tag, len; 13450 13451 tag = JS_VALUE_GET_TAG(val); 13452 switch(tag) { 13453 case JS_TAG_INT: 13454 case JS_TAG_BOOL: 13455 case JS_TAG_NULL: 13456 { 13457 int v; 13458 v = JS_VALUE_GET_INT(val); 13459 if (v < 0) 13460 goto fail; 13461 len = v; 13462 } 13463 break; 13464 default: 13465 if (JS_TAG_IS_FLOAT64(tag)) { 13466 double d; 13467 d = JS_VALUE_GET_FLOAT64(val); 13468 if (!(d >= 0 && d <= UINT32_MAX)) 13469 goto fail; 13470 len = (uint32_t)d; 13471 if (len != d) 13472 goto fail; 13473 } else { 13474 uint32_t len1; 13475 13476 if (is_array_ctor) { 13477 val = JS_ToNumberFree(ctx, val); 13478 if (JS_IsException(val)) 13479 return -1; 13480 /* cannot recurse because val is a number */ 13481 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) 13482 return -1; 13483 } else { 13484 /* legacy behavior: must do the conversion twice and compare */ 13485 if (JS_ToUint32(ctx, &len, val)) { 13486 JS_FreeValue(ctx, val); 13487 return -1; 13488 } 13489 val = JS_ToNumberFree(ctx, val); 13490 if (JS_IsException(val)) 13491 return -1; 13492 /* cannot recurse because val is a number */ 13493 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) 13494 return -1; 13495 if (len1 != len) { 13496 fail: 13497 JS_ThrowRangeError(ctx, "invalid array length"); 13498 return -1; 13499 } 13500 } 13501 } 13502 break; 13503 } 13504 *plen = len; 13505 return 0; 13506 } 13507 13508 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) 13509 13510 static BOOL is_safe_integer(double d) 13511 { 13512 return isfinite(d) && floor(d) == d && 13513 fabs(d) <= (double)MAX_SAFE_INTEGER; 13514 } 13515 13516 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) 13517 { 13518 int64_t v; 13519 if (JS_ToInt64Sat(ctx, &v, val)) 13520 return -1; 13521 if (v < 0 || v > MAX_SAFE_INTEGER) { 13522 JS_ThrowRangeError(ctx, "invalid array index"); 13523 *plen = 0; 13524 return -1; 13525 } 13526 *plen = v; 13527 return 0; 13528 } 13529 13530 /* convert a value to a length between 0 and MAX_SAFE_INTEGER. 13531 return -1 for exception */ 13532 static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, 13533 JSValue val) 13534 { 13535 int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); 13536 JS_FreeValue(ctx, val); 13537 return res; 13538 } 13539 13540 /* Note: can return an exception */ 13541 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) 13542 { 13543 double d; 13544 if (!JS_IsNumber(val)) 13545 return FALSE; 13546 if (unlikely(JS_ToFloat64(ctx, &d, val))) 13547 return -1; 13548 return isfinite(d) && floor(d) == d; 13549 } 13550 13551 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) 13552 { 13553 uint32_t tag; 13554 13555 tag = JS_VALUE_GET_NORM_TAG(val); 13556 switch(tag) { 13557 case JS_TAG_INT: 13558 { 13559 int v; 13560 v = JS_VALUE_GET_INT(val); 13561 return (v < 0); 13562 } 13563 case JS_TAG_FLOAT64: 13564 { 13565 JSFloat64Union u; 13566 u.d = JS_VALUE_GET_FLOAT64(val); 13567 return (u.u64 >> 63); 13568 } 13569 case JS_TAG_SHORT_BIG_INT: 13570 return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0); 13571 case JS_TAG_BIG_INT: 13572 { 13573 JSBigInt *p = JS_VALUE_GET_PTR(val); 13574 return js_bigint_sign(p); 13575 } 13576 default: 13577 return FALSE; 13578 } 13579 } 13580 13581 static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) 13582 { 13583 return js_bigint_to_string1(ctx, val, 10); 13584 } 13585 13586 static JSValue js_dtoa2(JSContext *ctx, 13587 double d, int radix, int n_digits, int flags) 13588 { 13589 char static_buf[128], *buf, *tmp_buf; 13590 int len, len_max; 13591 JSValue res; 13592 JSDTOATempMem dtoa_mem; 13593 len_max = js_dtoa_max_len(d, radix, n_digits, flags); 13594 13595 /* longer buffer may be used if radix != 10 */ 13596 if (len_max > sizeof(static_buf) - 1) { 13597 tmp_buf = js_malloc(ctx, len_max + 1); 13598 if (!tmp_buf) 13599 return JS_EXCEPTION; 13600 buf = tmp_buf; 13601 } else { 13602 tmp_buf = NULL; 13603 buf = static_buf; 13604 } 13605 len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem); 13606 res = js_new_string8_len(ctx, buf, len); 13607 js_free(ctx, tmp_buf); 13608 return res; 13609 } 13610 13611 static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) 13612 { 13613 uint32_t tag; 13614 char buf[32]; 13615 13616 tag = JS_VALUE_GET_NORM_TAG(val); 13617 switch(tag) { 13618 case JS_TAG_STRING: 13619 return JS_DupValue(ctx, val); 13620 case JS_TAG_STRING_ROPE: 13621 return js_linearize_string_rope(ctx, JS_DupValue(ctx, val)); 13622 case JS_TAG_INT: 13623 { 13624 size_t len; 13625 len = i32toa(buf, JS_VALUE_GET_INT(val)); 13626 return js_new_string8_len(ctx, buf, len); 13627 } 13628 break; 13629 case JS_TAG_BOOL: 13630 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? 13631 JS_ATOM_true : JS_ATOM_false); 13632 case JS_TAG_NULL: 13633 return JS_AtomToString(ctx, JS_ATOM_null); 13634 case JS_TAG_UNDEFINED: 13635 return JS_AtomToString(ctx, JS_ATOM_undefined); 13636 case JS_TAG_EXCEPTION: 13637 return JS_EXCEPTION; 13638 case JS_TAG_OBJECT: 13639 { 13640 JSValue val1, ret; 13641 val1 = JS_ToPrimitive(ctx, val, HINT_STRING); 13642 if (JS_IsException(val1)) 13643 return val1; 13644 ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); 13645 JS_FreeValue(ctx, val1); 13646 return ret; 13647 } 13648 break; 13649 case JS_TAG_FUNCTION_BYTECODE: 13650 return js_new_string8(ctx, "[function bytecode]"); 13651 case JS_TAG_SYMBOL: 13652 if (is_ToPropertyKey) { 13653 return JS_DupValue(ctx, val); 13654 } else { 13655 return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); 13656 } 13657 case JS_TAG_FLOAT64: 13658 return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, 13659 JS_DTOA_FORMAT_FREE); 13660 case JS_TAG_SHORT_BIG_INT: 13661 case JS_TAG_BIG_INT: 13662 return js_bigint_to_string(ctx, val); 13663 default: 13664 return js_new_string8(ctx, "[unsupported type]"); 13665 } 13666 } 13667 13668 JSValue JS_ToString(JSContext *ctx, JSValueConst val) 13669 { 13670 return JS_ToStringInternal(ctx, val, FALSE); 13671 } 13672 13673 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) 13674 { 13675 JSValue ret; 13676 ret = JS_ToString(ctx, val); 13677 JS_FreeValue(ctx, val); 13678 return ret; 13679 } 13680 13681 static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) 13682 { 13683 if (JS_IsUndefined(val) || JS_IsNull(val)) 13684 return JS_ToStringFree(ctx, val); 13685 return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); 13686 } 13687 13688 JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) 13689 { 13690 return JS_ToStringInternal(ctx, val, TRUE); 13691 } 13692 13693 static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) 13694 { 13695 uint32_t tag = JS_VALUE_GET_TAG(val); 13696 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) 13697 return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); 13698 return JS_ToString(ctx, val); 13699 } 13700 13701 #define JS_PRINT_MAX_DEPTH 8 13702 13703 typedef struct { 13704 JSRuntime *rt; 13705 JSContext *ctx; /* may be NULL */ 13706 JSPrintValueOptions options; 13707 JSPrintValueWrite *write_func; 13708 void *write_opaque; 13709 int level; 13710 JSObject *print_stack[JS_PRINT_MAX_DEPTH]; /* level values */ 13711 } JSPrintValueState; 13712 13713 static void js_print_value(JSPrintValueState *s, JSValueConst val); 13714 13715 static void js_putc(JSPrintValueState *s, char c) 13716 { 13717 s->write_func(s->write_opaque, &c, 1); 13718 } 13719 13720 static void js_puts(JSPrintValueState *s, const char *str) 13721 { 13722 s->write_func(s->write_opaque, str, strlen(str)); 13723 } 13724 13725 static void __attribute__((format(printf, 2, 3))) js_printf(JSPrintValueState *s, const char *fmt, ...) 13726 { 13727 va_list ap; 13728 char buf[256]; 13729 13730 va_start(ap, fmt); 13731 vsnprintf(buf, sizeof(buf), fmt, ap); 13732 va_end(ap); 13733 s->write_func(s->write_opaque, buf, strlen(buf)); 13734 } 13735 13736 static void js_print_float64(JSPrintValueState *s, double d) 13737 { 13738 JSDTOATempMem dtoa_mem; 13739 char buf[32]; 13740 int len; 13741 len = js_dtoa(buf, d, 10, 0, JS_DTOA_FORMAT_FREE | JS_DTOA_MINUS_ZERO, &dtoa_mem); 13742 s->write_func(s->write_opaque, buf, len); 13743 } 13744 13745 static uint32_t js_string_get_length(JSValueConst val) 13746 { 13747 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 13748 JSString *p = JS_VALUE_GET_STRING(val); 13749 return p->len; 13750 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { 13751 JSStringRope *r = JS_VALUE_GET_PTR(val); 13752 return r->len; 13753 } else { 13754 return 0; 13755 } 13756 } 13757 13758 /* pretty print the first 'len' characters of 'p' */ 13759 static void js_print_string1(JSPrintValueState *s, JSString *p, int len, int sep) 13760 { 13761 uint8_t buf[UTF8_CHAR_LEN_MAX]; 13762 int l, i, c, c1; 13763 13764 for(i = 0; i < len; i++) { 13765 c = string_get(p, i); 13766 switch(c) { 13767 case '\t': 13768 c = 't'; 13769 goto quote; 13770 case '\r': 13771 c = 'r'; 13772 goto quote; 13773 case '\n': 13774 c = 'n'; 13775 goto quote; 13776 case '\b': 13777 c = 'b'; 13778 goto quote; 13779 case '\f': 13780 c = 'f'; 13781 goto quote; 13782 case '\\': 13783 quote: 13784 js_putc(s, '\\'); 13785 js_putc(s, c); 13786 break; 13787 default: 13788 if (c == sep) 13789 goto quote; 13790 if (c >= 32 && c <= 126) { 13791 js_putc(s, c); 13792 } else if (c < 32 || 13793 (c >= 0x7f && c <= 0x9f)) { 13794 escape: 13795 js_printf(s, "\\u%04x", c); 13796 } else { 13797 if (is_hi_surrogate(c)) { 13798 if ((i + 1) >= len) 13799 goto escape; 13800 c1 = string_get(p, i + 1); 13801 if (!is_lo_surrogate(c1)) 13802 goto escape; 13803 i++; 13804 c = from_surrogate(c, c1); 13805 } else if (is_lo_surrogate(c)) { 13806 goto escape; 13807 } 13808 l = unicode_to_utf8(buf, c); 13809 s->write_func(s->write_opaque, (char *)buf, l); 13810 } 13811 break; 13812 } 13813 } 13814 } 13815 13816 static void js_print_string_rec(JSPrintValueState *s, JSValueConst val, 13817 int sep, uint32_t pos) 13818 { 13819 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 13820 JSString *p = JS_VALUE_GET_STRING(val); 13821 uint32_t len; 13822 if (pos < s->options.max_string_length) { 13823 len = min_uint32(p->len, s->options.max_string_length - pos); 13824 js_print_string1(s, p, len, sep); 13825 } 13826 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING_ROPE) { 13827 JSStringRope *r = JS_VALUE_GET_PTR(val); 13828 js_print_string_rec(s, r->left, sep, pos); 13829 js_print_string_rec(s, r->right, sep, pos + js_string_get_length(r->left)); 13830 } else { 13831 js_printf(s, "<invalid string tag %d>", (int)JS_VALUE_GET_TAG(val)); 13832 } 13833 } 13834 13835 static void js_print_string(JSPrintValueState *s, JSValueConst val) 13836 { 13837 int sep; 13838 if (s->options.raw_dump && JS_VALUE_GET_TAG(val) == JS_TAG_STRING) { 13839 JSString *p = JS_VALUE_GET_STRING(val); 13840 js_printf(s, "%d", js_rc(p)->ref_count); 13841 sep = (js_rc(p)->ref_count == 1) ? '\"' : '\''; 13842 } else { 13843 sep = '\"'; 13844 } 13845 js_putc(s, sep); 13846 js_print_string_rec(s, val, sep, 0); 13847 js_putc(s, sep); 13848 if (js_string_get_length(val) > s->options.max_string_length) { 13849 uint32_t n = js_string_get_length(val) - s->options.max_string_length; 13850 js_printf(s, "... %u more character%s", n, n > 1 ? "s" : ""); 13851 } 13852 } 13853 13854 static void js_print_raw_string(JSPrintValueState *s, JSValueConst val) 13855 { 13856 const char *cstr; 13857 size_t len; 13858 cstr = JS_ToCStringLen(s->ctx, &len, val); 13859 if (cstr) { 13860 s->write_func(s->write_opaque, cstr, len); 13861 JS_FreeCString(s->ctx, cstr); 13862 } 13863 } 13864 13865 static BOOL is_ascii_ident(const JSString *p) 13866 { 13867 int i, c; 13868 13869 if (p->len == 0) 13870 return FALSE; 13871 for(i = 0; i < p->len; i++) { 13872 c = string_get(p, i); 13873 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 13874 (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) 13875 return FALSE; 13876 } 13877 return TRUE; 13878 } 13879 13880 static void js_print_atom(JSPrintValueState *s, JSAtom atom) 13881 { 13882 int i; 13883 if (__JS_AtomIsTaggedInt(atom)) { 13884 js_printf(s, "%u", __JS_AtomToUInt32(atom)); 13885 } else if (atom == JS_ATOM_NULL) { 13886 js_puts(s, "<null>"); 13887 } else { 13888 assert(atom < s->rt->atom_size); 13889 JSString *p; 13890 p = s->rt->atom_array[atom]; 13891 if (is_ascii_ident(p)) { 13892 for(i = 0; i < p->len; i++) { 13893 js_putc(s, string_get(p, i)); 13894 } 13895 } else { 13896 js_putc(s, '"'); 13897 js_print_string1(s, p, p->len, '\"'); 13898 js_putc(s, '"'); 13899 } 13900 } 13901 } 13902 13903 /* return 0 if invalid length */ 13904 static uint32_t js_print_array_get_length(JSObject *p) 13905 { 13906 JSProperty *pr; 13907 JSShapeProperty *prs; 13908 JSValueConst val; 13909 13910 prs = find_own_property(&pr, p, JS_ATOM_length); 13911 if (!prs) 13912 return 0; 13913 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 13914 return 0; 13915 val = pr->u.value; 13916 switch(JS_VALUE_GET_NORM_TAG(val)) { 13917 case JS_TAG_INT: 13918 return JS_VALUE_GET_INT(val); 13919 case JS_TAG_FLOAT64: 13920 return (uint32_t)JS_VALUE_GET_FLOAT64(val); 13921 default: 13922 return 0; 13923 } 13924 } 13925 13926 static void js_print_comma(JSPrintValueState *s, int *pcomma_state) 13927 { 13928 switch(*pcomma_state) { 13929 case 0: 13930 break; 13931 case 1: 13932 js_printf(s, ", "); 13933 break; 13934 case 2: 13935 js_printf(s, " { "); 13936 break; 13937 } 13938 *pcomma_state = 1; 13939 } 13940 13941 static void js_print_more_items(JSPrintValueState *s, int *pcomma_state, 13942 uint32_t n) 13943 { 13944 js_print_comma(s, pcomma_state); 13945 js_printf(s, "... %u more item%s", n, n > 1 ? "s" : ""); 13946 } 13947 13948 /* similar to js_regexp_toString() but without side effect */ 13949 static void js_print_regexp(JSPrintValueState *s, JSObject *p1) 13950 { 13951 JSRegExp *re = &p1->u.regexp; 13952 JSString *p; 13953 int i, n, c, c2, bra, flags; 13954 static const char regexp_flags[] = { 'g', 'i', 'm', 's', 'u', 'y', 'd', 'v' }; 13955 13956 if (!re->pattern || !re->bytecode) { 13957 /* the regexp fields are zeroed at init */ 13958 js_puts(s, "[uninitialized_regexp]"); 13959 return; 13960 } 13961 p = re->pattern; 13962 js_putc(s, '/'); 13963 if (p->len == 0) { 13964 js_puts(s, "(?:)"); 13965 } else { 13966 bra = 0; 13967 for (i = 0, n = p->len; i < n;) { 13968 c2 = -1; 13969 switch (c = string_get(p, i++)) { 13970 case '\\': 13971 if (i < n) 13972 c2 = string_get(p, i++); 13973 break; 13974 case ']': 13975 bra = 0; 13976 break; 13977 case '[': 13978 if (!bra) { 13979 if (i < n && string_get(p, i) == ']') 13980 c2 = string_get(p, i++); 13981 bra = 1; 13982 } 13983 break; 13984 case '\n': 13985 c = '\\'; 13986 c2 = 'n'; 13987 break; 13988 case '\r': 13989 c = '\\'; 13990 c2 = 'r'; 13991 break; 13992 case '/': 13993 if (!bra) { 13994 c = '\\'; 13995 c2 = '/'; 13996 } 13997 break; 13998 } 13999 js_putc(s, c); 14000 if (c2 >= 0) 14001 js_putc(s, c2); 14002 } 14003 } 14004 js_putc(s, '/'); 14005 14006 flags = lre_get_flags(re->bytecode->u.str8); 14007 for(i = 0; i < countof(regexp_flags); i++) { 14008 if ((flags >> i) & 1) { 14009 js_putc(s, regexp_flags[i]); 14010 } 14011 } 14012 } 14013 14014 /* similar to js_error_toString() but without side effect */ 14015 static void js_print_error(JSPrintValueState *s, JSObject *p) 14016 { 14017 const char *str; 14018 size_t len; 14019 14020 str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name); 14021 if (!str) { 14022 js_puts(s, "Error"); 14023 } else { 14024 js_puts(s, str); 14025 JS_FreeCString(s->ctx, str); 14026 } 14027 14028 str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_message); 14029 if (str && str[0] != '\0') { 14030 js_puts(s, ": "); 14031 js_puts(s, str); 14032 } 14033 JS_FreeCString(s->ctx, str); 14034 14035 /* dump the stack if present */ 14036 str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_stack); 14037 if (str) { 14038 js_putc(s, '\n'); 14039 14040 /* XXX: should remove the last '\n' in stack as 14041 v8. SpiderMonkey does not do it */ 14042 len = strlen(str); 14043 if (len > 0 && str[len - 1] == '\n') 14044 len--; 14045 s->write_func(s->write_opaque, str, len); 14046 14047 JS_FreeCString(s->ctx, str); 14048 } 14049 } 14050 14051 static void js_print_object(JSPrintValueState *s, JSObject *p) 14052 { 14053 JSRuntime *rt = s->rt; 14054 JSShape *sh; 14055 JSShapeProperty *prs; 14056 JSProperty *pr; 14057 int comma_state; 14058 BOOL is_array; 14059 uint32_t i; 14060 14061 comma_state = 0; 14062 is_array = FALSE; 14063 if (p->class_id == JS_CLASS_ARRAY) { 14064 is_array = TRUE; 14065 js_printf(s, "[ "); 14066 /* XXX: print array like properties even if not fast array */ 14067 if (p->fast_array) { 14068 uint32_t len, n, len1; 14069 len = js_print_array_get_length(p); 14070 14071 len1 = min_uint32(p->u.array.count, s->options.max_item_count); 14072 for(i = 0; i < len1; i++) { 14073 js_print_comma(s, &comma_state); 14074 js_print_value(s, p->u.array.u.values[i]); 14075 } 14076 if (len1 < p->u.array.count) 14077 js_print_more_items(s, &comma_state, p->u.array.count - len1); 14078 if (p->u.array.count < len) { 14079 n = len - p->u.array.count; 14080 js_print_comma(s, &comma_state); 14081 js_printf(s, "<%u empty item%s>", n, n > 1 ? "s" : ""); 14082 } 14083 } 14084 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 14085 uint32_t size = 1 << typed_array_size_log2(p->class_id); 14086 uint32_t len1; 14087 int64_t v; 14088 14089 js_print_atom(s, rt->class_array[p->class_id].class_name); 14090 js_printf(s, "(%u) [ ", p->u.array.count); 14091 14092 is_array = TRUE; 14093 len1 = min_uint32(p->u.array.count, s->options.max_item_count); 14094 for(i = 0; i < len1; i++) { 14095 const uint8_t *ptr = p->u.array.u.uint8_ptr + i * size; 14096 js_print_comma(s, &comma_state); 14097 switch(p->class_id) { 14098 case JS_CLASS_UINT8C_ARRAY: 14099 case JS_CLASS_UINT8_ARRAY: 14100 v = *ptr; 14101 goto ta_int64; 14102 case JS_CLASS_INT8_ARRAY: 14103 v = *(int8_t *)ptr; 14104 goto ta_int64; 14105 case JS_CLASS_INT16_ARRAY: 14106 v = *(int16_t *)ptr; 14107 goto ta_int64; 14108 case JS_CLASS_UINT16_ARRAY: 14109 v = *(uint16_t *)ptr; 14110 goto ta_int64; 14111 case JS_CLASS_INT32_ARRAY: 14112 v = *(int32_t *)ptr; 14113 goto ta_int64; 14114 case JS_CLASS_UINT32_ARRAY: 14115 v = *(uint32_t *)ptr; 14116 goto ta_int64; 14117 case JS_CLASS_BIG_INT64_ARRAY: 14118 v = *(int64_t *)ptr; 14119 ta_int64: 14120 js_printf(s, "%" PRId64, v); 14121 break; 14122 case JS_CLASS_BIG_UINT64_ARRAY: 14123 js_printf(s, "%" PRIu64, *(uint64_t *)ptr); 14124 break; 14125 case JS_CLASS_FLOAT16_ARRAY: 14126 js_print_float64(s, fromfp16(*(uint16_t *)ptr)); 14127 break; 14128 case JS_CLASS_FLOAT32_ARRAY: 14129 js_print_float64(s, *(float *)ptr); 14130 break; 14131 case JS_CLASS_FLOAT64_ARRAY: 14132 js_print_float64(s, *(double *)ptr); 14133 break; 14134 } 14135 } 14136 if (len1 < p->u.array.count) 14137 js_print_more_items(s, &comma_state, p->u.array.count - len1); 14138 } else if (p->class_id == JS_CLASS_BYTECODE_FUNCTION || 14139 (rt->class_array[p->class_id].call != NULL && 14140 p->class_id != JS_CLASS_PROXY)) { 14141 js_printf(s, "[Function"); 14142 /* XXX: allow dump without ctx */ 14143 if (!s->options.raw_dump && s->ctx) { 14144 const char *func_name_str; 14145 js_putc(s, ' '); 14146 func_name_str = get_prop_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), JS_ATOM_name); 14147 if (!func_name_str || func_name_str[0] == '\0') 14148 js_puts(s, "(anonymous)"); 14149 else 14150 js_puts(s, func_name_str); 14151 JS_FreeCString(s->ctx, func_name_str); 14152 } 14153 js_printf(s, "]"); 14154 comma_state = 2; 14155 } else if (p->class_id == JS_CLASS_MAP || p->class_id == JS_CLASS_SET) { 14156 JSMapState *ms = p->u.opaque; 14157 struct list_head *el; 14158 14159 if (!ms) 14160 goto default_obj; 14161 js_print_atom(s, rt->class_array[p->class_id].class_name); 14162 js_printf(s, "(%u) { ", ms->record_count); 14163 i = 0; 14164 list_for_each(el, &ms->records) { 14165 JSMapRecord *mr = list_entry(el, JSMapRecord, link); 14166 js_print_comma(s, &comma_state); 14167 if (mr->empty) 14168 continue; 14169 js_print_value(s, mr->key); 14170 if (p->class_id == JS_CLASS_MAP) { 14171 js_printf(s, " => "); 14172 js_print_value(s, mr->value); 14173 } 14174 i++; 14175 if (i >= s->options.max_item_count) 14176 break; 14177 } 14178 if (i < ms->record_count) 14179 js_print_more_items(s, &comma_state, ms->record_count - i); 14180 } else if (p->class_id == JS_CLASS_REGEXP && s->ctx) { 14181 js_print_regexp(s, p); 14182 comma_state = 2; 14183 } else if (p->class_id == JS_CLASS_DATE && s->ctx) { 14184 /* get_date_string() has no side effect */ 14185 JSValue str = get_date_string(s->ctx, JS_MKPTR(JS_TAG_OBJECT, p), 0, NULL, 0x23); /* toISOString() */ 14186 if (JS_IsException(str)) 14187 goto default_obj; 14188 js_print_raw_string(s, str); 14189 JS_FreeValueRT(s->rt, str); 14190 comma_state = 2; 14191 } else if (p->class_id == JS_CLASS_ERROR && s->ctx) { 14192 js_print_error(s, p); 14193 comma_state = 2; 14194 } else { 14195 default_obj: 14196 if (p->class_id != JS_CLASS_OBJECT) { 14197 js_print_atom(s, rt->class_array[p->class_id].class_name); 14198 js_printf(s, " "); 14199 } 14200 js_printf(s, "{ "); 14201 } 14202 14203 sh = p->shape; /* the shape can be NULL while freeing an object */ 14204 if (sh) { 14205 uint32_t j; 14206 14207 j = 0; 14208 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 14209 if (prs->atom != JS_ATOM_NULL) { 14210 if (!(prs->flags & JS_PROP_ENUMERABLE) && 14211 !s->options.show_hidden) { 14212 continue; 14213 } 14214 if (j < s->options.max_item_count) { 14215 pr = &p->prop[i]; 14216 js_print_comma(s, &comma_state); 14217 js_print_atom(s, prs->atom); 14218 js_printf(s, ": "); 14219 14220 /* XXX: autoinit property */ 14221 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 14222 if (s->options.raw_dump) { 14223 js_printf(s, "[Getter %p Setter %p]", 14224 pr->u.getset.getter, pr->u.getset.setter); 14225 } else { 14226 if (pr->u.getset.getter && pr->u.getset.setter) { 14227 js_printf(s, "[Getter/Setter]"); 14228 } else if (pr->u.getset.setter) { 14229 js_printf(s, "[Setter]"); 14230 } else { 14231 js_printf(s, "[Getter]"); 14232 } 14233 } 14234 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 14235 if (s->options.raw_dump) { 14236 js_printf(s, "[varref %p]", (void *)pr->u.var_ref); 14237 } else { 14238 js_print_value(s, *pr->u.var_ref->pvalue); 14239 } 14240 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 14241 if (s->options.raw_dump) { 14242 js_printf(s, "[autoinit %p %d %p]", 14243 (void *)js_autoinit_get_realm(pr), 14244 js_autoinit_get_id(pr), 14245 (void *)pr->u.init.opaque); 14246 } else { 14247 /* XXX: could autoinit but need to restart 14248 the iteration */ 14249 js_printf(s, "[autoinit]"); 14250 } 14251 } else { 14252 js_print_value(s, pr->u.value); 14253 } 14254 } 14255 j++; 14256 } 14257 } 14258 if (j > s->options.max_item_count) 14259 js_print_more_items(s, &comma_state, j - s->options.max_item_count); 14260 } 14261 if (s->options.raw_dump && js_class_has_bytecode(p->class_id)) { 14262 JSFunctionBytecode *b = p->u.func.function_bytecode; 14263 if (b->closure_var_count) { 14264 JSVarRef **var_refs; 14265 var_refs = p->u.func.var_refs; 14266 14267 js_print_comma(s, &comma_state); 14268 js_printf(s, "[[Closure]]: ["); 14269 for(i = 0; i < b->closure_var_count; i++) { 14270 if (i != 0) 14271 js_printf(s, ", "); 14272 js_print_value(s, var_refs[i]->value); 14273 } 14274 js_printf(s, " ]"); 14275 } 14276 if (p->u.func.home_object) { 14277 js_print_comma(s, &comma_state); 14278 js_printf(s, "[[HomeObject]]: "); 14279 js_print_value(s, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); 14280 } 14281 } 14282 14283 if (!is_array) { 14284 if (comma_state != 2) { 14285 js_printf(s, " }"); 14286 } 14287 } else { 14288 js_printf(s, " ]"); 14289 } 14290 } 14291 14292 static int js_print_stack_index(JSPrintValueState *s, JSObject *p) 14293 { 14294 int i; 14295 for(i = 0; i < s->level; i++) 14296 if (s->print_stack[i] == p) 14297 return i; 14298 return -1; 14299 } 14300 14301 static void js_print_value(JSPrintValueState *s, JSValueConst val) 14302 { 14303 uint32_t tag = JS_VALUE_GET_NORM_TAG(val); 14304 const char *str; 14305 14306 switch(tag) { 14307 case JS_TAG_INT: 14308 js_printf(s, "%d", JS_VALUE_GET_INT(val)); 14309 break; 14310 case JS_TAG_BOOL: 14311 if (JS_VALUE_GET_BOOL(val)) 14312 str = "true"; 14313 else 14314 str = "false"; 14315 goto print_str; 14316 case JS_TAG_NULL: 14317 str = "null"; 14318 goto print_str; 14319 case JS_TAG_EXCEPTION: 14320 str = "exception"; 14321 goto print_str; 14322 case JS_TAG_UNINITIALIZED: 14323 str = "uninitialized"; 14324 goto print_str; 14325 case JS_TAG_UNDEFINED: 14326 str = "undefined"; 14327 print_str: 14328 js_puts(s, str); 14329 break; 14330 case JS_TAG_FLOAT64: 14331 js_print_float64(s, JS_VALUE_GET_FLOAT64(val)); 14332 break; 14333 case JS_TAG_SHORT_BIG_INT: 14334 js_printf(s, "%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val)); 14335 break; 14336 case JS_TAG_BIG_INT: 14337 if (!s->options.raw_dump && s->ctx) { 14338 JSValue str = js_bigint_to_string(s->ctx, val); 14339 if (JS_IsException(str)) 14340 goto raw_bigint; 14341 js_print_raw_string(s, str); 14342 js_putc(s, 'n'); 14343 JS_FreeValueRT(s->rt, str); 14344 } else { 14345 JSBigInt *p; 14346 int sgn, i; 14347 raw_bigint: 14348 p = JS_VALUE_GET_PTR(val); 14349 /* In order to avoid allocations we just dump the limbs */ 14350 sgn = js_bigint_sign(p); 14351 if (sgn) 14352 js_printf(s, "BigInt.asIntN(%d,", p->len * JS_LIMB_BITS); 14353 js_printf(s, "0x"); 14354 for(i = p->len - 1; i >= 0; i--) { 14355 if (i != p->len - 1) 14356 js_putc(s, '_'); 14357 #if JS_LIMB_BITS == 32 14358 js_printf(s, "%08x", p->tab[i]); 14359 #else 14360 js_printf(s, "%016" PRIx64, p->tab[i]); 14361 #endif 14362 } 14363 js_putc(s, 'n'); 14364 if (sgn) 14365 js_putc(s, ')'); 14366 } 14367 break; 14368 case JS_TAG_STRING: 14369 case JS_TAG_STRING_ROPE: 14370 if (s->options.raw_dump && tag == JS_TAG_STRING_ROPE) { 14371 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val); 14372 js_printf(s, "[rope len=%d depth=%d]", r->len, r->depth); 14373 } else { 14374 js_print_string(s, val); 14375 } 14376 break; 14377 case JS_TAG_FUNCTION_BYTECODE: 14378 { 14379 JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); 14380 js_puts(s, "[bytecode "); 14381 js_print_atom(s, b->func_name); 14382 js_putc(s, ']'); 14383 } 14384 break; 14385 case JS_TAG_OBJECT: 14386 { 14387 JSObject *p = JS_VALUE_GET_OBJ(val); 14388 int idx; 14389 idx = js_print_stack_index(s, p); 14390 if (idx >= 0) { 14391 js_printf(s, "[circular %d]", idx); 14392 } else if (s->level < s->options.max_depth) { 14393 s->print_stack[s->level++] = p; 14394 js_print_object(s, JS_VALUE_GET_OBJ(val)); 14395 s->level--; 14396 } else { 14397 JSAtom atom = s->rt->class_array[p->class_id].class_name; 14398 js_putc(s, '['); 14399 js_print_atom(s, atom); 14400 if (s->options.raw_dump) { 14401 js_printf(s, " %p", (void *)p); 14402 } 14403 js_putc(s, ']'); 14404 } 14405 } 14406 break; 14407 case JS_TAG_SYMBOL: 14408 { 14409 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 14410 js_puts(s, "Symbol("); 14411 js_print_atom(s, js_get_atom_index(s->rt, p)); 14412 js_putc(s, ')'); 14413 } 14414 break; 14415 case JS_TAG_MODULE: 14416 js_puts(s, "[module]"); 14417 break; 14418 default: 14419 js_printf(s, "[unknown tag %d]", tag); 14420 break; 14421 } 14422 } 14423 14424 void JS_PrintValueSetDefaultOptions(JSPrintValueOptions *options) 14425 { 14426 memset(options, 0, sizeof(*options)); 14427 options->max_depth = 2; 14428 options->max_string_length = 1000; 14429 options->max_item_count = 100; 14430 } 14431 14432 static void JS_PrintValueInternal(JSRuntime *rt, JSContext *ctx, 14433 JSPrintValueWrite *write_func, void *write_opaque, 14434 JSValueConst val, const JSPrintValueOptions *options) 14435 { 14436 JSPrintValueState ss, *s = &ss; 14437 if (options) 14438 s->options = *options; 14439 else 14440 JS_PrintValueSetDefaultOptions(&s->options); 14441 if (s->options.max_depth <= 0) 14442 s->options.max_depth = JS_PRINT_MAX_DEPTH; 14443 else 14444 s->options.max_depth = min_int(s->options.max_depth, JS_PRINT_MAX_DEPTH); 14445 if (s->options.max_string_length == 0) 14446 s->options.max_string_length = UINT32_MAX; 14447 if (s->options.max_item_count == 0) 14448 s->options.max_item_count = UINT32_MAX; 14449 s->rt = rt; 14450 s->ctx = ctx; 14451 s->write_func = write_func; 14452 s->write_opaque = write_opaque; 14453 s->level = 0; 14454 js_print_value(s, val); 14455 } 14456 14457 void JS_PrintValueRT(JSRuntime *rt, JSPrintValueWrite *write_func, void *write_opaque, 14458 JSValueConst val, const JSPrintValueOptions *options) 14459 { 14460 JS_PrintValueInternal(rt, NULL, write_func, write_opaque, val, options); 14461 } 14462 14463 void JS_PrintValue(JSContext *ctx, JSPrintValueWrite *write_func, void *write_opaque, 14464 JSValueConst val, const JSPrintValueOptions *options) 14465 { 14466 JS_PrintValueInternal(ctx->rt, ctx, write_func, write_opaque, val, options); 14467 } 14468 14469 static void js_dump_value_write(void *opaque, const char *buf, size_t len) 14470 { 14471 FILE *fo = opaque; 14472 fwrite(buf, 1, len, fo); 14473 } 14474 14475 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) 14476 { 14477 JSPrintValueState ss, *s = &ss; 14478 memset(s, 0, sizeof(*s)); 14479 s->rt = ctx->rt; 14480 s->ctx = ctx; 14481 s->write_func = js_dump_value_write; 14482 s->write_opaque = stdout; 14483 js_print_atom(s, atom); 14484 } 14485 14486 static __maybe_unused void JS_DumpAtom(JSContext *ctx, const char *str, JSAtom atom) 14487 { 14488 printf("%s=", str); 14489 print_atom(ctx, atom); 14490 printf("\n"); 14491 } 14492 14493 static __maybe_unused void JS_DumpValue(JSContext *ctx, const char *str, JSValueConst val) 14494 { 14495 printf("%s=", str); 14496 JS_PrintValue(ctx, js_dump_value_write, stdout, val, NULL); 14497 printf("\n"); 14498 } 14499 14500 static __maybe_unused void JS_DumpValueRT(JSRuntime *rt, const char *str, JSValueConst val) 14501 { 14502 printf("%s=", str); 14503 JS_PrintValueRT(rt, js_dump_value_write, stdout, val, NULL); 14504 printf("\n"); 14505 } 14506 14507 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) 14508 { 14509 printf("%14s %4s %4s %14s %s\n", 14510 "ADDRESS", "REFS", "SHRF", "PROTO", "CONTENT"); 14511 } 14512 14513 /* for debug only: dump an object without side effect */ 14514 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) 14515 { 14516 JSShape *sh; 14517 JSPrintValueOptions options; 14518 14519 /* XXX: should encode atoms with special characters */ 14520 sh = p->shape; /* the shape can be NULL while freeing an object */ 14521 printf("%14p %4d ", 14522 (void *)p, 14523 js_rc(p)->ref_count); 14524 if (sh) { 14525 printf("%3d%c %14p ", 14526 js_rc(sh)->ref_count, 14527 " *"[sh->is_hashed], 14528 (void *)sh->proto); 14529 } else { 14530 printf("%3s %14s ", "-", "-"); 14531 } 14532 14533 JS_PrintValueSetDefaultOptions(&options); 14534 options.max_depth = 1; 14535 options.show_hidden = TRUE; 14536 options.raw_dump = TRUE; 14537 JS_PrintValueRT(rt, js_dump_value_write, stdout, JS_MKPTR(JS_TAG_OBJECT, p), &options); 14538 14539 printf("\n"); 14540 } 14541 14542 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) 14543 { 14544 if (js_rc(p)->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 14545 JS_DumpObject(rt, (JSObject *)p); 14546 } else { 14547 printf("%14p %4d ", 14548 (void *)p, 14549 js_rc(p)->ref_count); 14550 switch(js_rc(p)->gc_obj_type) { 14551 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 14552 printf("[function bytecode]"); 14553 break; 14554 case JS_GC_OBJ_TYPE_SHAPE: 14555 printf("[shape]"); 14556 break; 14557 case JS_GC_OBJ_TYPE_VAR_REF: 14558 printf("[var_ref]"); 14559 break; 14560 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 14561 printf("[async_function]"); 14562 break; 14563 case JS_GC_OBJ_TYPE_JS_CONTEXT: 14564 printf("[js_context]"); 14565 break; 14566 case JS_GC_OBJ_TYPE_MODULE: 14567 printf("[module]"); 14568 break; 14569 default: 14570 printf("[unknown %d]", js_rc(p)->gc_obj_type); 14571 break; 14572 } 14573 printf("\n"); 14574 } 14575 } 14576 14577 /* return -1 if exception (proxy case) or TRUE/FALSE */ 14578 // TODO: should take flags to make proxy resolution and exceptions optional 14579 int JS_IsArray(JSContext *ctx, JSValueConst val) 14580 { 14581 if (js_resolve_proxy(ctx, &val, TRUE)) 14582 return -1; 14583 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { 14584 JSObject *p = JS_VALUE_GET_OBJ(val); 14585 return p->class_id == JS_CLASS_ARRAY; 14586 } else { 14587 return FALSE; 14588 } 14589 } 14590 14591 static double js_pow(double a, double b) 14592 { 14593 if (unlikely(!isfinite(b)) && fabs(a) == 1) { 14594 /* not compatible with IEEE 754 */ 14595 return JS_FLOAT64_NAN; 14596 } else { 14597 return pow(a, b); 14598 } 14599 } 14600 14601 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) 14602 { 14603 #if JS_SHORT_BIG_INT_BITS == 64 14604 return __JS_NewShortBigInt(ctx, v); 14605 #else 14606 if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) { 14607 return __JS_NewShortBigInt(ctx, v); 14608 } else { 14609 JSBigInt *p; 14610 p = js_bigint_new_si64(ctx, v); 14611 if (!p) 14612 return JS_EXCEPTION; 14613 return JS_MKPTR(JS_TAG_BIG_INT, p); 14614 } 14615 #endif 14616 } 14617 14618 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) 14619 { 14620 if (v <= JS_SHORT_BIG_INT_MAX) { 14621 return __JS_NewShortBigInt(ctx, v); 14622 } else { 14623 JSBigInt *p; 14624 p = js_bigint_new_ui64(ctx, v); 14625 if (!p) 14626 return JS_EXCEPTION; 14627 return JS_MKPTR(JS_TAG_BIG_INT, p); 14628 } 14629 } 14630 14631 /* return NaN if bad bigint literal */ 14632 static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) 14633 { 14634 const char *str, *p; 14635 size_t len; 14636 int flags; 14637 14638 str = JS_ToCStringLen(ctx, &len, val); 14639 JS_FreeValue(ctx, val); 14640 if (!str) 14641 return JS_EXCEPTION; 14642 p = str; 14643 p += skip_spaces(p); 14644 if ((p - str) == len) { 14645 val = JS_NewBigInt64(ctx, 0); 14646 } else { 14647 flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; 14648 val = js_atof(ctx, p, &p, 0, flags); 14649 p += skip_spaces(p); 14650 if (!JS_IsException(val)) { 14651 if ((p - str) != len) { 14652 JS_FreeValue(ctx, val); 14653 val = JS_NAN; 14654 } 14655 } 14656 } 14657 JS_FreeCString(ctx, str); 14658 return val; 14659 } 14660 14661 static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) 14662 { 14663 val = JS_StringToBigInt(ctx, val); 14664 if (JS_VALUE_IS_NAN(val)) 14665 return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); 14666 return val; 14667 } 14668 14669 /* JS Numbers are not allowed */ 14670 static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val) 14671 { 14672 uint32_t tag; 14673 14674 redo: 14675 tag = JS_VALUE_GET_NORM_TAG(val); 14676 switch(tag) { 14677 case JS_TAG_SHORT_BIG_INT: 14678 case JS_TAG_BIG_INT: 14679 break; 14680 case JS_TAG_INT: 14681 case JS_TAG_NULL: 14682 case JS_TAG_UNDEFINED: 14683 case JS_TAG_FLOAT64: 14684 goto fail; 14685 case JS_TAG_BOOL: 14686 val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val)); 14687 break; 14688 case JS_TAG_STRING: 14689 case JS_TAG_STRING_ROPE: 14690 val = JS_StringToBigIntErr(ctx, val); 14691 if (JS_IsException(val)) 14692 return val; 14693 goto redo; 14694 case JS_TAG_OBJECT: 14695 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 14696 if (JS_IsException(val)) 14697 return val; 14698 goto redo; 14699 default: 14700 fail: 14701 JS_FreeValue(ctx, val); 14702 return JS_ThrowTypeError(ctx, "cannot convert to bigint"); 14703 } 14704 return val; 14705 } 14706 14707 static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val) 14708 { 14709 return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val)); 14710 } 14711 14712 /* XXX: merge with JS_ToInt64Free with a specific flag ? */ 14713 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 14714 { 14715 uint64_t res; 14716 14717 val = JS_ToBigIntFree(ctx, val); 14718 if (JS_IsException(val)) { 14719 *pres = 0; 14720 return -1; 14721 } 14722 if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { 14723 res = JS_VALUE_GET_SHORT_BIG_INT(val); 14724 } else { 14725 JSBigInt *p = JS_VALUE_GET_PTR(val); 14726 /* return the value mod 2^64 */ 14727 res = p->tab[0]; 14728 #if JS_LIMB_BITS == 32 14729 if (p->len >= 2) 14730 res |= (uint64_t)p->tab[1] << 32; 14731 #endif 14732 JS_FreeValue(ctx, val); 14733 } 14734 *pres = res; 14735 return 0; 14736 } 14737 14738 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) 14739 { 14740 return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); 14741 } 14742 14743 static no_inline __exception int js_unary_arith_slow(JSContext *ctx, 14744 JSValue *sp, 14745 OPCodeEnum op) 14746 { 14747 JSValue op1; 14748 int v; 14749 uint32_t tag; 14750 JSBigIntBuf buf1; 14751 JSBigInt *p1; 14752 14753 op1 = sp[-1]; 14754 /* fast path for float64 */ 14755 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) 14756 goto handle_float64; 14757 op1 = JS_ToNumericFree(ctx, op1); 14758 if (JS_IsException(op1)) 14759 goto exception; 14760 tag = JS_VALUE_GET_TAG(op1); 14761 switch(tag) { 14762 case JS_TAG_INT: 14763 { 14764 int64_t v64; 14765 v64 = JS_VALUE_GET_INT(op1); 14766 switch(op) { 14767 case OP_inc: 14768 case OP_dec: 14769 v = 2 * (op - OP_dec) - 1; 14770 v64 += v; 14771 break; 14772 case OP_plus: 14773 break; 14774 case OP_neg: 14775 if (v64 == 0) { 14776 sp[-1] = __JS_NewFloat64(ctx, -0.0); 14777 return 0; 14778 } else { 14779 v64 = -v64; 14780 } 14781 break; 14782 default: 14783 abort(); 14784 } 14785 sp[-1] = JS_NewInt64(ctx, v64); 14786 } 14787 break; 14788 case JS_TAG_SHORT_BIG_INT: 14789 { 14790 int64_t v; 14791 v = JS_VALUE_GET_SHORT_BIG_INT(op1); 14792 switch(op) { 14793 case OP_plus: 14794 JS_ThrowTypeError(ctx, "bigint argument with unary +"); 14795 goto exception; 14796 case OP_inc: 14797 if (v == JS_SHORT_BIG_INT_MAX) 14798 goto bigint_slow_case; 14799 sp[-1] = __JS_NewShortBigInt(ctx, v + 1); 14800 break; 14801 case OP_dec: 14802 if (v == JS_SHORT_BIG_INT_MIN) 14803 goto bigint_slow_case; 14804 sp[-1] = __JS_NewShortBigInt(ctx, v - 1); 14805 break; 14806 case OP_neg: 14807 v = JS_VALUE_GET_SHORT_BIG_INT(op1); 14808 if (v == JS_SHORT_BIG_INT_MIN) { 14809 bigint_slow_case: 14810 p1 = js_bigint_set_short(&buf1, op1); 14811 goto bigint_slow_case1; 14812 } 14813 sp[-1] = __JS_NewShortBigInt(ctx, -v); 14814 break; 14815 default: 14816 abort(); 14817 } 14818 } 14819 break; 14820 case JS_TAG_BIG_INT: 14821 { 14822 JSBigInt *r; 14823 p1 = JS_VALUE_GET_PTR(op1); 14824 bigint_slow_case1: 14825 switch(op) { 14826 case OP_plus: 14827 JS_ThrowTypeError(ctx, "bigint argument with unary +"); 14828 JS_FreeValue(ctx, op1); 14829 goto exception; 14830 case OP_inc: 14831 case OP_dec: 14832 { 14833 JSBigIntBuf buf2; 14834 JSBigInt *p2; 14835 p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1); 14836 r = js_bigint_add(ctx, p1, p2, 0); 14837 } 14838 break; 14839 case OP_neg: 14840 r = js_bigint_neg(ctx, p1); 14841 break; 14842 case OP_not: 14843 r = js_bigint_not(ctx, p1); 14844 break; 14845 default: 14846 abort(); 14847 } 14848 JS_FreeValue(ctx, op1); 14849 if (!r) 14850 goto exception; 14851 sp[-1] = JS_CompactBigInt(ctx, r); 14852 } 14853 break; 14854 default: 14855 handle_float64: 14856 { 14857 double d; 14858 d = JS_VALUE_GET_FLOAT64(op1); 14859 switch(op) { 14860 case OP_inc: 14861 case OP_dec: 14862 v = 2 * (op - OP_dec) - 1; 14863 d += v; 14864 break; 14865 case OP_plus: 14866 break; 14867 case OP_neg: 14868 d = -d; 14869 break; 14870 default: 14871 abort(); 14872 } 14873 sp[-1] = __JS_NewFloat64(ctx, d); 14874 } 14875 break; 14876 } 14877 return 0; 14878 exception: 14879 sp[-1] = JS_UNDEFINED; 14880 return -1; 14881 } 14882 14883 static __exception int js_post_inc_slow(JSContext *ctx, 14884 JSValue *sp, OPCodeEnum op) 14885 { 14886 JSValue op1; 14887 14888 /* XXX: allow custom operators */ 14889 op1 = sp[-1]; 14890 op1 = JS_ToNumericFree(ctx, op1); 14891 if (JS_IsException(op1)) { 14892 sp[-1] = JS_UNDEFINED; 14893 return -1; 14894 } 14895 sp[-1] = op1; 14896 sp[0] = JS_DupValue(ctx, op1); 14897 return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); 14898 } 14899 14900 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) 14901 { 14902 JSValue op1; 14903 14904 op1 = sp[-1]; 14905 op1 = JS_ToNumericFree(ctx, op1); 14906 if (JS_IsException(op1)) 14907 goto exception; 14908 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) { 14909 sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1)); 14910 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { 14911 JSBigInt *r; 14912 r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1)); 14913 JS_FreeValue(ctx, op1); 14914 if (!r) 14915 goto exception; 14916 sp[-1] = JS_CompactBigInt(ctx, r); 14917 } else { 14918 int32_t v1; 14919 if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) 14920 goto exception; 14921 sp[-1] = JS_NewInt32(ctx, ~v1); 14922 } 14923 return 0; 14924 exception: 14925 sp[-1] = JS_UNDEFINED; 14926 return -1; 14927 } 14928 14929 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, 14930 OPCodeEnum op) 14931 { 14932 JSValue op1, op2; 14933 uint32_t tag1, tag2; 14934 double d1, d2; 14935 14936 op1 = sp[-2]; 14937 op2 = sp[-1]; 14938 tag1 = JS_VALUE_GET_NORM_TAG(op1); 14939 tag2 = JS_VALUE_GET_NORM_TAG(op2); 14940 /* fast path for float operations */ 14941 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { 14942 d1 = JS_VALUE_GET_FLOAT64(op1); 14943 d2 = JS_VALUE_GET_FLOAT64(op2); 14944 goto handle_float64; 14945 } 14946 /* fast path for short big int operations */ 14947 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { 14948 js_slimb_t v1, v2; 14949 js_sdlimb_t v; 14950 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); 14951 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); 14952 switch(op) { 14953 case OP_sub: 14954 v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2; 14955 break; 14956 case OP_mul: 14957 v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2; 14958 break; 14959 case OP_div: 14960 if (v2 == 0 || 14961 ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && 14962 v2 == -1)) { 14963 goto slow_big_int; 14964 } 14965 sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2); 14966 return 0; 14967 case OP_mod: 14968 if (v2 == 0 || 14969 ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) && 14970 v2 == -1)) { 14971 goto slow_big_int; 14972 } 14973 sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2); 14974 return 0; 14975 case OP_pow: 14976 goto slow_big_int; 14977 default: 14978 abort(); 14979 } 14980 if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { 14981 sp[-2] = __JS_NewShortBigInt(ctx, v); 14982 } else { 14983 JSBigInt *r = js_bigint_new_di(ctx, v); 14984 if (!r) 14985 goto exception; 14986 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); 14987 } 14988 return 0; 14989 } 14990 op1 = JS_ToNumericFree(ctx, op1); 14991 if (JS_IsException(op1)) { 14992 JS_FreeValue(ctx, op2); 14993 goto exception; 14994 } 14995 op2 = JS_ToNumericFree(ctx, op2); 14996 if (JS_IsException(op2)) { 14997 JS_FreeValue(ctx, op1); 14998 goto exception; 14999 } 15000 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15001 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15002 15003 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 15004 int32_t v1, v2; 15005 int64_t v; 15006 v1 = JS_VALUE_GET_INT(op1); 15007 v2 = JS_VALUE_GET_INT(op2); 15008 switch(op) { 15009 case OP_sub: 15010 v = (int64_t)v1 - (int64_t)v2; 15011 break; 15012 case OP_mul: 15013 v = (int64_t)v1 * (int64_t)v2; 15014 if (v == 0 && (v1 | v2) < 0) { 15015 sp[-2] = __JS_NewFloat64(ctx, -0.0); 15016 return 0; 15017 } 15018 break; 15019 case OP_div: 15020 sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); 15021 return 0; 15022 case OP_mod: 15023 if (v1 < 0 || v2 <= 0) { 15024 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); 15025 return 0; 15026 } else { 15027 v = (int64_t)v1 % (int64_t)v2; 15028 } 15029 break; 15030 case OP_pow: 15031 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); 15032 return 0; 15033 default: 15034 abort(); 15035 } 15036 sp[-2] = JS_NewInt64(ctx, v); 15037 } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) && 15038 (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) { 15039 JSBigInt *p1, *p2, *r; 15040 JSBigIntBuf buf1, buf2; 15041 slow_big_int: 15042 /* bigint result */ 15043 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) 15044 p1 = js_bigint_set_short(&buf1, op1); 15045 else 15046 p1 = JS_VALUE_GET_PTR(op1); 15047 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) 15048 p2 = js_bigint_set_short(&buf2, op2); 15049 else 15050 p2 = JS_VALUE_GET_PTR(op2); 15051 switch(op) { 15052 case OP_add: 15053 r = js_bigint_add(ctx, p1, p2, 0); 15054 break; 15055 case OP_sub: 15056 r = js_bigint_add(ctx, p1, p2, 1); 15057 break; 15058 case OP_mul: 15059 r = js_bigint_mul(ctx, p1, p2); 15060 break; 15061 case OP_div: 15062 r = js_bigint_divrem(ctx, p1, p2, FALSE); 15063 break; 15064 case OP_mod: 15065 r = js_bigint_divrem(ctx, p1, p2, TRUE); 15066 break; 15067 case OP_pow: 15068 r = js_bigint_pow(ctx, p1, p2); 15069 break; 15070 default: 15071 abort(); 15072 } 15073 JS_FreeValue(ctx, op1); 15074 JS_FreeValue(ctx, op2); 15075 if (!r) 15076 goto exception; 15077 sp[-2] = JS_CompactBigInt(ctx, r); 15078 } else { 15079 double dr; 15080 /* float64 result */ 15081 if (JS_ToFloat64Free(ctx, &d1, op1)) { 15082 JS_FreeValue(ctx, op2); 15083 goto exception; 15084 } 15085 if (JS_ToFloat64Free(ctx, &d2, op2)) 15086 goto exception; 15087 handle_float64: 15088 switch(op) { 15089 case OP_sub: 15090 dr = d1 - d2; 15091 break; 15092 case OP_mul: 15093 dr = d1 * d2; 15094 break; 15095 case OP_div: 15096 dr = d1 / d2; 15097 break; 15098 case OP_mod: 15099 dr = fmod(d1, d2); 15100 break; 15101 case OP_pow: 15102 dr = js_pow(d1, d2); 15103 break; 15104 default: 15105 abort(); 15106 } 15107 sp[-2] = __JS_NewFloat64(ctx, dr); 15108 } 15109 return 0; 15110 exception: 15111 sp[-2] = JS_UNDEFINED; 15112 sp[-1] = JS_UNDEFINED; 15113 return -1; 15114 } 15115 15116 static inline BOOL tag_is_string(uint32_t tag) 15117 { 15118 return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE; 15119 } 15120 15121 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) 15122 { 15123 JSValue op1, op2; 15124 uint32_t tag1, tag2; 15125 15126 op1 = sp[-2]; 15127 op2 = sp[-1]; 15128 15129 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15130 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15131 /* fast path for float64 */ 15132 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { 15133 double d1, d2; 15134 d1 = JS_VALUE_GET_FLOAT64(op1); 15135 d2 = JS_VALUE_GET_FLOAT64(op2); 15136 sp[-2] = __JS_NewFloat64(ctx, d1 + d2); 15137 return 0; 15138 } 15139 /* fast path for short bigint */ 15140 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { 15141 js_slimb_t v1, v2; 15142 js_sdlimb_t v; 15143 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); 15144 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); 15145 v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2; 15146 if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) { 15147 sp[-2] = __JS_NewShortBigInt(ctx, v); 15148 } else { 15149 JSBigInt *r = js_bigint_new_di(ctx, v); 15150 if (!r) 15151 goto exception; 15152 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); 15153 } 15154 return 0; 15155 } 15156 15157 if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { 15158 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); 15159 if (JS_IsException(op1)) { 15160 JS_FreeValue(ctx, op2); 15161 goto exception; 15162 } 15163 15164 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); 15165 if (JS_IsException(op2)) { 15166 JS_FreeValue(ctx, op1); 15167 goto exception; 15168 } 15169 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15170 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15171 } 15172 15173 if (tag_is_string(tag1) || tag_is_string(tag2)) { 15174 sp[-2] = JS_ConcatString(ctx, op1, op2); 15175 if (JS_IsException(sp[-2])) 15176 goto exception; 15177 return 0; 15178 } 15179 15180 op1 = JS_ToNumericFree(ctx, op1); 15181 if (JS_IsException(op1)) { 15182 JS_FreeValue(ctx, op2); 15183 goto exception; 15184 } 15185 op2 = JS_ToNumericFree(ctx, op2); 15186 if (JS_IsException(op2)) { 15187 JS_FreeValue(ctx, op1); 15188 goto exception; 15189 } 15190 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15191 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15192 15193 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 15194 int32_t v1, v2; 15195 int64_t v; 15196 v1 = JS_VALUE_GET_INT(op1); 15197 v2 = JS_VALUE_GET_INT(op2); 15198 v = (int64_t)v1 + (int64_t)v2; 15199 sp[-2] = JS_NewInt64(ctx, v); 15200 } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && 15201 (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { 15202 JSBigInt *p1, *p2, *r; 15203 JSBigIntBuf buf1, buf2; 15204 /* bigint result */ 15205 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) 15206 p1 = js_bigint_set_short(&buf1, op1); 15207 else 15208 p1 = JS_VALUE_GET_PTR(op1); 15209 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) 15210 p2 = js_bigint_set_short(&buf2, op2); 15211 else 15212 p2 = JS_VALUE_GET_PTR(op2); 15213 r = js_bigint_add(ctx, p1, p2, 0); 15214 JS_FreeValue(ctx, op1); 15215 JS_FreeValue(ctx, op2); 15216 if (!r) 15217 goto exception; 15218 sp[-2] = JS_CompactBigInt(ctx, r); 15219 } else { 15220 double d1, d2; 15221 /* float64 result */ 15222 if (JS_ToFloat64Free(ctx, &d1, op1)) { 15223 JS_FreeValue(ctx, op2); 15224 goto exception; 15225 } 15226 if (JS_ToFloat64Free(ctx, &d2, op2)) 15227 goto exception; 15228 sp[-2] = __JS_NewFloat64(ctx, d1 + d2); 15229 } 15230 return 0; 15231 exception: 15232 sp[-2] = JS_UNDEFINED; 15233 sp[-1] = JS_UNDEFINED; 15234 return -1; 15235 } 15236 15237 static no_inline __exception int js_binary_logic_slow(JSContext *ctx, 15238 JSValue *sp, 15239 OPCodeEnum op) 15240 { 15241 JSValue op1, op2; 15242 uint32_t tag1, tag2; 15243 uint32_t v1, v2, r; 15244 15245 op1 = sp[-2]; 15246 op2 = sp[-1]; 15247 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15248 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15249 15250 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) { 15251 js_slimb_t v1, v2, v; 15252 js_sdlimb_t vd; 15253 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); 15254 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); 15255 /* bigint fast path */ 15256 switch(op) { 15257 case OP_and: 15258 v = v1 & v2; 15259 break; 15260 case OP_or: 15261 v = v1 | v2; 15262 break; 15263 case OP_xor: 15264 v = v1 ^ v2; 15265 break; 15266 case OP_sar: 15267 if (v2 > (JS_LIMB_BITS - 1)) { 15268 goto slow_big_int; 15269 } else if (v2 < 0) { 15270 if (v2 < -(JS_LIMB_BITS - 1)) 15271 goto slow_big_int; 15272 v2 = -v2; 15273 goto bigint_shl; 15274 } 15275 bigint_sar: 15276 v = v1 >> v2; 15277 break; 15278 case OP_shl: 15279 if (v2 > (JS_LIMB_BITS - 1)) { 15280 goto slow_big_int; 15281 } else if (v2 < 0) { 15282 if (v2 < -(JS_LIMB_BITS - 1)) 15283 goto slow_big_int; 15284 v2 = -v2; 15285 goto bigint_sar; 15286 } 15287 bigint_shl: 15288 vd = (js_dlimb_t)v1 << v2; 15289 if (likely(vd >= JS_SHORT_BIG_INT_MIN && 15290 vd <= JS_SHORT_BIG_INT_MAX)) { 15291 v = vd; 15292 } else { 15293 JSBigInt *r = js_bigint_new_di(ctx, vd); 15294 if (!r) 15295 goto exception; 15296 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r); 15297 return 0; 15298 } 15299 break; 15300 default: 15301 abort(); 15302 } 15303 sp[-2] = __JS_NewShortBigInt(ctx, v); 15304 return 0; 15305 } 15306 op1 = JS_ToNumericFree(ctx, op1); 15307 if (JS_IsException(op1)) { 15308 JS_FreeValue(ctx, op2); 15309 goto exception; 15310 } 15311 op2 = JS_ToNumericFree(ctx, op2); 15312 if (JS_IsException(op2)) { 15313 JS_FreeValue(ctx, op1); 15314 goto exception; 15315 } 15316 15317 tag1 = JS_VALUE_GET_TAG(op1); 15318 tag2 = JS_VALUE_GET_TAG(op2); 15319 if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && 15320 (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) { 15321 JSBigInt *p1, *p2, *r; 15322 JSBigIntBuf buf1, buf2; 15323 slow_big_int: 15324 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) 15325 p1 = js_bigint_set_short(&buf1, op1); 15326 else 15327 p1 = JS_VALUE_GET_PTR(op1); 15328 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) 15329 p2 = js_bigint_set_short(&buf2, op2); 15330 else 15331 p2 = JS_VALUE_GET_PTR(op2); 15332 switch(op) { 15333 case OP_and: 15334 case OP_or: 15335 case OP_xor: 15336 r = js_bigint_logic(ctx, p1, p2, op); 15337 break; 15338 case OP_shl: 15339 case OP_sar: 15340 { 15341 js_slimb_t shift; 15342 shift = js_bigint_get_si_sat(p2); 15343 if (shift > INT32_MAX) 15344 shift = INT32_MAX; 15345 else if (shift < -INT32_MAX) 15346 shift = -INT32_MAX; 15347 if (op == OP_sar) 15348 shift = -shift; 15349 if (shift >= 0) 15350 r = js_bigint_shl(ctx, p1, shift); 15351 else 15352 r = js_bigint_shr(ctx, p1, -shift); 15353 } 15354 break; 15355 default: 15356 abort(); 15357 } 15358 JS_FreeValue(ctx, op1); 15359 JS_FreeValue(ctx, op2); 15360 if (!r) 15361 goto exception; 15362 sp[-2] = JS_CompactBigInt(ctx, r); 15363 } else { 15364 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { 15365 JS_FreeValue(ctx, op2); 15366 goto exception; 15367 } 15368 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) 15369 goto exception; 15370 switch(op) { 15371 case OP_shl: 15372 r = v1 << (v2 & 0x1f); 15373 break; 15374 case OP_sar: 15375 r = (int)v1 >> (v2 & 0x1f); 15376 break; 15377 case OP_and: 15378 r = v1 & v2; 15379 break; 15380 case OP_or: 15381 r = v1 | v2; 15382 break; 15383 case OP_xor: 15384 r = v1 ^ v2; 15385 break; 15386 default: 15387 abort(); 15388 } 15389 sp[-2] = JS_NewInt32(ctx, r); 15390 } 15391 return 0; 15392 exception: 15393 sp[-2] = JS_UNDEFINED; 15394 sp[-1] = JS_UNDEFINED; 15395 return -1; 15396 } 15397 15398 /* op1 must be a bigint or int. */ 15399 static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1, 15400 JSValue op1) 15401 { 15402 JSBigInt *p1; 15403 15404 switch(JS_VALUE_GET_TAG(op1)) { 15405 case JS_TAG_INT: 15406 p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1)); 15407 break; 15408 case JS_TAG_SHORT_BIG_INT: 15409 p1 = js_bigint_set_short(buf1, op1); 15410 break; 15411 case JS_TAG_BIG_INT: 15412 p1 = JS_VALUE_GET_PTR(op1); 15413 break; 15414 default: 15415 abort(); 15416 } 15417 return p1; 15418 } 15419 15420 /* op1 and op2 must be numeric types and at least one must be a 15421 bigint. No exception is generated. */ 15422 static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, 15423 JSValue op1, JSValue op2) 15424 { 15425 int res, val, tag1, tag2; 15426 JSBigIntBuf buf1, buf2; 15427 JSBigInt *p1, *p2; 15428 15429 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15430 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15431 if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) && 15432 (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) { 15433 /* fast path */ 15434 js_slimb_t v1, v2; 15435 if (tag1 == JS_TAG_INT) 15436 v1 = JS_VALUE_GET_INT(op1); 15437 else 15438 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1); 15439 if (tag2 == JS_TAG_INT) 15440 v2 = JS_VALUE_GET_INT(op2); 15441 else 15442 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2); 15443 val = (v1 > v2) - (v1 < v2); 15444 } else { 15445 if (tag1 == JS_TAG_FLOAT64) { 15446 p2 = JS_ToBigIntBuf(ctx, &buf2, op2); 15447 val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1)); 15448 if (val == 2) 15449 goto unordered; 15450 val = -val; 15451 } else if (tag2 == JS_TAG_FLOAT64) { 15452 p1 = JS_ToBigIntBuf(ctx, &buf1, op1); 15453 val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2)); 15454 if (val == 2) { 15455 unordered: 15456 JS_FreeValue(ctx, op1); 15457 JS_FreeValue(ctx, op2); 15458 return FALSE; 15459 } 15460 } else { 15461 p1 = JS_ToBigIntBuf(ctx, &buf1, op1); 15462 p2 = JS_ToBigIntBuf(ctx, &buf2, op2); 15463 val = js_bigint_cmp(ctx, p1, p2); 15464 } 15465 JS_FreeValue(ctx, op1); 15466 JS_FreeValue(ctx, op2); 15467 } 15468 15469 switch(op) { 15470 case OP_lt: 15471 res = val < 0; 15472 break; 15473 case OP_lte: 15474 res = val <= 0; 15475 break; 15476 case OP_gt: 15477 res = val > 0; 15478 break; 15479 case OP_gte: 15480 res = val >= 0; 15481 break; 15482 case OP_eq: 15483 res = val == 0; 15484 break; 15485 default: 15486 abort(); 15487 } 15488 return res; 15489 } 15490 15491 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, 15492 OPCodeEnum op) 15493 { 15494 JSValue op1, op2; 15495 int res; 15496 uint32_t tag1, tag2; 15497 15498 op1 = sp[-2]; 15499 op2 = sp[-1]; 15500 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15501 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15502 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); 15503 if (JS_IsException(op1)) { 15504 JS_FreeValue(ctx, op2); 15505 goto exception; 15506 } 15507 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); 15508 if (JS_IsException(op2)) { 15509 JS_FreeValue(ctx, op1); 15510 goto exception; 15511 } 15512 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15513 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15514 15515 if (tag_is_string(tag1) && tag_is_string(tag2)) { 15516 if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { 15517 res = js_string_compare(ctx, JS_VALUE_GET_STRING(op1), 15518 JS_VALUE_GET_STRING(op2)); 15519 } else { 15520 res = js_string_rope_compare(ctx, op1, op2, FALSE); 15521 } 15522 switch(op) { 15523 case OP_lt: 15524 res = (res < 0); 15525 break; 15526 case OP_lte: 15527 res = (res <= 0); 15528 break; 15529 case OP_gt: 15530 res = (res > 0); 15531 break; 15532 default: 15533 case OP_gte: 15534 res = (res >= 0); 15535 break; 15536 } 15537 JS_FreeValue(ctx, op1); 15538 JS_FreeValue(ctx, op2); 15539 } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && 15540 (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { 15541 /* fast path for float64/int */ 15542 goto float64_compare; 15543 } else { 15544 if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) && 15545 tag_is_string(tag2)) || 15546 ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) && 15547 tag_is_string(tag1)))) { 15548 if (tag_is_string(tag1)) { 15549 op1 = JS_StringToBigInt(ctx, op1); 15550 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && 15551 JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) 15552 goto invalid_bigint_string; 15553 } 15554 if (tag_is_string(tag2)) { 15555 op2 = JS_StringToBigInt(ctx, op2); 15556 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && 15557 JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) { 15558 invalid_bigint_string: 15559 JS_FreeValue(ctx, op1); 15560 JS_FreeValue(ctx, op2); 15561 res = FALSE; 15562 goto done; 15563 } 15564 } 15565 } else { 15566 op1 = JS_ToNumericFree(ctx, op1); 15567 if (JS_IsException(op1)) { 15568 JS_FreeValue(ctx, op2); 15569 goto exception; 15570 } 15571 op2 = JS_ToNumericFree(ctx, op2); 15572 if (JS_IsException(op2)) { 15573 JS_FreeValue(ctx, op1); 15574 goto exception; 15575 } 15576 } 15577 15578 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15579 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15580 15581 if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || 15582 tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { 15583 res = js_compare_bigint(ctx, op, op1, op2); 15584 } else { 15585 double d1, d2; 15586 15587 float64_compare: 15588 /* can use floating point comparison */ 15589 if (tag1 == JS_TAG_FLOAT64) { 15590 d1 = JS_VALUE_GET_FLOAT64(op1); 15591 } else { 15592 d1 = JS_VALUE_GET_INT(op1); 15593 } 15594 if (tag2 == JS_TAG_FLOAT64) { 15595 d2 = JS_VALUE_GET_FLOAT64(op2); 15596 } else { 15597 d2 = JS_VALUE_GET_INT(op2); 15598 } 15599 switch(op) { 15600 case OP_lt: 15601 res = (d1 < d2); /* if NaN return false */ 15602 break; 15603 case OP_lte: 15604 res = (d1 <= d2); /* if NaN return false */ 15605 break; 15606 case OP_gt: 15607 res = (d1 > d2); /* if NaN return false */ 15608 break; 15609 default: 15610 case OP_gte: 15611 res = (d1 >= d2); /* if NaN return false */ 15612 break; 15613 } 15614 } 15615 } 15616 done: 15617 sp[-2] = JS_NewBool(ctx, res); 15618 return 0; 15619 exception: 15620 sp[-2] = JS_UNDEFINED; 15621 sp[-1] = JS_UNDEFINED; 15622 return -1; 15623 } 15624 15625 static BOOL tag_is_number(uint32_t tag) 15626 { 15627 return (tag == JS_TAG_INT || 15628 tag == JS_TAG_FLOAT64 || 15629 tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT); 15630 } 15631 15632 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, 15633 BOOL is_neq) 15634 { 15635 JSValue op1, op2; 15636 int res; 15637 uint32_t tag1, tag2; 15638 15639 op1 = sp[-2]; 15640 op2 = sp[-1]; 15641 redo: 15642 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15643 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15644 if (tag_is_number(tag1) && tag_is_number(tag2)) { 15645 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 15646 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); 15647 } else if ((tag1 == JS_TAG_FLOAT64 && 15648 (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || 15649 (tag2 == JS_TAG_FLOAT64 && 15650 (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { 15651 double d1, d2; 15652 if (tag1 == JS_TAG_FLOAT64) { 15653 d1 = JS_VALUE_GET_FLOAT64(op1); 15654 } else { 15655 d1 = JS_VALUE_GET_INT(op1); 15656 } 15657 if (tag2 == JS_TAG_FLOAT64) { 15658 d2 = JS_VALUE_GET_FLOAT64(op2); 15659 } else { 15660 d2 = JS_VALUE_GET_INT(op2); 15661 } 15662 res = (d1 == d2); 15663 } else { 15664 res = js_compare_bigint(ctx, OP_eq, op1, op2); 15665 } 15666 } else if (tag1 == tag2) { 15667 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); 15668 } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || 15669 (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { 15670 res = TRUE; 15671 } else if (tag_is_string(tag1) && tag_is_string(tag2)) { 15672 /* needed when comparing strings and ropes */ 15673 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); 15674 } else if ((tag_is_string(tag1) && tag_is_number(tag2)) || 15675 (tag_is_string(tag2) && tag_is_number(tag1))) { 15676 15677 if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT || 15678 tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) { 15679 if (tag_is_string(tag1)) { 15680 op1 = JS_StringToBigInt(ctx, op1); 15681 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT && 15682 JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT) 15683 goto invalid_bigint_string; 15684 } 15685 if (tag_is_string(tag2)) { 15686 op2 = JS_StringToBigInt(ctx, op2); 15687 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT && 15688 JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) { 15689 invalid_bigint_string: 15690 JS_FreeValue(ctx, op1); 15691 JS_FreeValue(ctx, op2); 15692 res = FALSE; 15693 goto done; 15694 } 15695 } 15696 } else { 15697 op1 = JS_ToNumericFree(ctx, op1); 15698 if (JS_IsException(op1)) { 15699 JS_FreeValue(ctx, op2); 15700 goto exception; 15701 } 15702 op2 = JS_ToNumericFree(ctx, op2); 15703 if (JS_IsException(op2)) { 15704 JS_FreeValue(ctx, op1); 15705 goto exception; 15706 } 15707 } 15708 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); 15709 } else if (tag1 == JS_TAG_BOOL) { 15710 op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); 15711 goto redo; 15712 } else if (tag2 == JS_TAG_BOOL) { 15713 op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); 15714 goto redo; 15715 } else if ((tag1 == JS_TAG_OBJECT && 15716 (tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) || 15717 (tag2 == JS_TAG_OBJECT && 15718 (tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) { 15719 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); 15720 if (JS_IsException(op1)) { 15721 JS_FreeValue(ctx, op2); 15722 goto exception; 15723 } 15724 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); 15725 if (JS_IsException(op2)) { 15726 JS_FreeValue(ctx, op1); 15727 goto exception; 15728 } 15729 goto redo; 15730 } else { 15731 /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ 15732 if ((JS_IsHTMLDDA(ctx, op1) && 15733 (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || 15734 (JS_IsHTMLDDA(ctx, op2) && 15735 (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { 15736 res = TRUE; 15737 } else { 15738 res = FALSE; 15739 } 15740 JS_FreeValue(ctx, op1); 15741 JS_FreeValue(ctx, op2); 15742 } 15743 done: 15744 sp[-2] = JS_NewBool(ctx, res ^ is_neq); 15745 return 0; 15746 exception: 15747 sp[-2] = JS_UNDEFINED; 15748 sp[-1] = JS_UNDEFINED; 15749 return -1; 15750 } 15751 15752 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) 15753 { 15754 JSValue op1, op2; 15755 uint32_t v1, v2, r; 15756 15757 op1 = sp[-2]; 15758 op2 = sp[-1]; 15759 op1 = JS_ToNumericFree(ctx, op1); 15760 if (JS_IsException(op1)) { 15761 JS_FreeValue(ctx, op2); 15762 goto exception; 15763 } 15764 op2 = JS_ToNumericFree(ctx, op2); 15765 if (JS_IsException(op2)) { 15766 JS_FreeValue(ctx, op1); 15767 goto exception; 15768 } 15769 if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || 15770 JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT || 15771 JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT || 15772 JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) { 15773 JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); 15774 JS_FreeValue(ctx, op1); 15775 JS_FreeValue(ctx, op2); 15776 goto exception; 15777 } 15778 /* cannot give an exception */ 15779 JS_ToUint32Free(ctx, &v1, op1); 15780 JS_ToUint32Free(ctx, &v2, op2); 15781 r = v1 >> (v2 & 0x1f); 15782 sp[-2] = JS_NewUint32(ctx, r); 15783 return 0; 15784 exception: 15785 sp[-2] = JS_UNDEFINED; 15786 sp[-1] = JS_UNDEFINED; 15787 return -1; 15788 } 15789 15790 /* XXX: Should take JSValueConst arguments */ 15791 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 15792 JSStrictEqModeEnum eq_mode) 15793 { 15794 BOOL res; 15795 int tag1, tag2; 15796 double d1, d2; 15797 15798 tag1 = JS_VALUE_GET_NORM_TAG(op1); 15799 tag2 = JS_VALUE_GET_NORM_TAG(op2); 15800 switch(tag1) { 15801 case JS_TAG_BOOL: 15802 if (tag1 != tag2) { 15803 res = FALSE; 15804 } else { 15805 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); 15806 goto done_no_free; 15807 } 15808 break; 15809 case JS_TAG_NULL: 15810 case JS_TAG_UNDEFINED: 15811 res = (tag1 == tag2); 15812 break; 15813 case JS_TAG_STRING: 15814 case JS_TAG_STRING_ROPE: 15815 { 15816 if (!tag_is_string(tag2)) { 15817 res = FALSE; 15818 } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { 15819 res = js_string_eq(ctx, JS_VALUE_GET_STRING(op1), 15820 JS_VALUE_GET_STRING(op2)); 15821 } else { 15822 res = (js_string_rope_compare(ctx, op1, op2, TRUE) == 0); 15823 } 15824 } 15825 break; 15826 case JS_TAG_SYMBOL: 15827 { 15828 JSAtomStruct *p1, *p2; 15829 if (tag1 != tag2) { 15830 res = FALSE; 15831 } else { 15832 p1 = JS_VALUE_GET_PTR(op1); 15833 p2 = JS_VALUE_GET_PTR(op2); 15834 res = (p1 == p2); 15835 } 15836 } 15837 break; 15838 case JS_TAG_OBJECT: 15839 if (tag1 != tag2) 15840 res = FALSE; 15841 else 15842 res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); 15843 break; 15844 case JS_TAG_INT: 15845 d1 = JS_VALUE_GET_INT(op1); 15846 if (tag2 == JS_TAG_INT) { 15847 d2 = JS_VALUE_GET_INT(op2); 15848 goto number_test; 15849 } else if (tag2 == JS_TAG_FLOAT64) { 15850 d2 = JS_VALUE_GET_FLOAT64(op2); 15851 goto number_test; 15852 } else { 15853 res = FALSE; 15854 } 15855 break; 15856 case JS_TAG_FLOAT64: 15857 d1 = JS_VALUE_GET_FLOAT64(op1); 15858 if (tag2 == JS_TAG_FLOAT64) { 15859 d2 = JS_VALUE_GET_FLOAT64(op2); 15860 } else if (tag2 == JS_TAG_INT) { 15861 d2 = JS_VALUE_GET_INT(op2); 15862 } else { 15863 res = FALSE; 15864 break; 15865 } 15866 number_test: 15867 if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { 15868 JSFloat64Union u1, u2; 15869 /* NaN is not always normalized, so this test is necessary */ 15870 if (isnan(d1) || isnan(d2)) { 15871 res = isnan(d1) == isnan(d2); 15872 } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { 15873 res = (d1 == d2); /* +0 == -0 */ 15874 } else { 15875 u1.d = d1; 15876 u2.d = d2; 15877 res = (u1.u64 == u2.u64); /* +0 != -0 */ 15878 } 15879 } else { 15880 res = (d1 == d2); /* if NaN return false and +0 == -0 */ 15881 } 15882 goto done_no_free; 15883 case JS_TAG_SHORT_BIG_INT: 15884 case JS_TAG_BIG_INT: 15885 { 15886 JSBigIntBuf buf1, buf2; 15887 JSBigInt *p1, *p2; 15888 15889 if (tag2 != JS_TAG_SHORT_BIG_INT && 15890 tag2 != JS_TAG_BIG_INT) { 15891 res = FALSE; 15892 break; 15893 } 15894 15895 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) 15896 p1 = js_bigint_set_short(&buf1, op1); 15897 else 15898 p1 = JS_VALUE_GET_PTR(op1); 15899 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) 15900 p2 = js_bigint_set_short(&buf2, op2); 15901 else 15902 p2 = JS_VALUE_GET_PTR(op2); 15903 res = (js_bigint_cmp(ctx, p1, p2) == 0); 15904 } 15905 break; 15906 default: 15907 res = FALSE; 15908 break; 15909 } 15910 JS_FreeValue(ctx, op1); 15911 JS_FreeValue(ctx, op2); 15912 done_no_free: 15913 return res; 15914 } 15915 15916 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15917 { 15918 return js_strict_eq2(ctx, 15919 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 15920 JS_EQ_STRICT); 15921 } 15922 15923 BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15924 { 15925 return js_strict_eq(ctx, op1, op2); 15926 } 15927 15928 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15929 { 15930 return js_strict_eq2(ctx, 15931 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 15932 JS_EQ_SAME_VALUE); 15933 } 15934 15935 BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15936 { 15937 return js_same_value(ctx, op1, op2); 15938 } 15939 15940 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15941 { 15942 return js_strict_eq2(ctx, 15943 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 15944 JS_EQ_SAME_VALUE_ZERO); 15945 } 15946 15947 BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) 15948 { 15949 return js_same_value_zero(ctx, op1, op2); 15950 } 15951 15952 static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, 15953 BOOL is_neq) 15954 { 15955 BOOL res; 15956 res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); 15957 sp[-2] = JS_NewBool(ctx, res ^ is_neq); 15958 return 0; 15959 } 15960 15961 static __exception int js_operator_in(JSContext *ctx, JSValue *sp) 15962 { 15963 JSValue op1, op2; 15964 JSAtom atom; 15965 int ret; 15966 15967 op1 = sp[-2]; 15968 op2 = sp[-1]; 15969 15970 if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { 15971 JS_ThrowTypeError(ctx, "invalid 'in' operand"); 15972 return -1; 15973 } 15974 atom = JS_ValueToAtom(ctx, op1); 15975 if (unlikely(atom == JS_ATOM_NULL)) 15976 return -1; 15977 ret = JS_HasProperty(ctx, op2, atom); 15978 JS_FreeAtom(ctx, atom); 15979 if (ret < 0) 15980 return -1; 15981 JS_FreeValue(ctx, op1); 15982 JS_FreeValue(ctx, op2); 15983 sp[-2] = JS_NewBool(ctx, ret); 15984 return 0; 15985 } 15986 15987 static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) 15988 { 15989 JSValue op1, op2; 15990 int ret; 15991 15992 op1 = sp[-2]; /* object */ 15993 op2 = sp[-1]; /* field name or method function */ 15994 15995 if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { 15996 JS_ThrowTypeError(ctx, "invalid 'in' operand"); 15997 return -1; 15998 } 15999 if (JS_IsObject(op2)) { 16000 /* method: use the brand */ 16001 ret = JS_CheckBrand(ctx, op1, op2); 16002 if (ret < 0) 16003 return -1; 16004 } else { 16005 JSAtom atom; 16006 JSObject *p; 16007 JSShapeProperty *prs; 16008 JSProperty *pr; 16009 /* field */ 16010 atom = JS_ValueToAtom(ctx, op2); 16011 if (unlikely(atom == JS_ATOM_NULL)) 16012 return -1; 16013 p = JS_VALUE_GET_OBJ(op1); 16014 prs = find_own_property(&pr, p, atom); 16015 JS_FreeAtom(ctx, atom); 16016 ret = (prs != NULL); 16017 } 16018 JS_FreeValue(ctx, op1); 16019 JS_FreeValue(ctx, op2); 16020 sp[-2] = JS_NewBool(ctx, ret); 16021 return 0; 16022 } 16023 16024 static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, 16025 JSAtom atom) 16026 { 16027 JSValue arr, val; 16028 int ret; 16029 16030 arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); 16031 if (JS_IsException(arr)) 16032 return -1; 16033 ret = 0; 16034 if (JS_IsObject(arr)) { 16035 val = JS_GetProperty(ctx, arr, atom); 16036 ret = JS_ToBoolFree(ctx, val); 16037 } 16038 JS_FreeValue(ctx, arr); 16039 return ret; 16040 } 16041 16042 static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) 16043 { 16044 JSValue op1, op2; 16045 BOOL ret; 16046 16047 op1 = sp[-2]; 16048 op2 = sp[-1]; 16049 ret = JS_IsInstanceOf(ctx, op1, op2); 16050 if (ret < 0) 16051 return ret; 16052 JS_FreeValue(ctx, op1); 16053 JS_FreeValue(ctx, op2); 16054 sp[-2] = JS_NewBool(ctx, ret); 16055 return 0; 16056 } 16057 16058 static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) 16059 { 16060 JSAtom atom; 16061 uint32_t tag; 16062 16063 tag = JS_VALUE_GET_NORM_TAG(op1); 16064 switch(tag) { 16065 case JS_TAG_SHORT_BIG_INT: 16066 case JS_TAG_BIG_INT: 16067 atom = JS_ATOM_bigint; 16068 break; 16069 case JS_TAG_INT: 16070 case JS_TAG_FLOAT64: 16071 atom = JS_ATOM_number; 16072 break; 16073 case JS_TAG_UNDEFINED: 16074 atom = JS_ATOM_undefined; 16075 break; 16076 case JS_TAG_BOOL: 16077 atom = JS_ATOM_boolean; 16078 break; 16079 case JS_TAG_STRING: 16080 case JS_TAG_STRING_ROPE: 16081 atom = JS_ATOM_string; 16082 break; 16083 case JS_TAG_OBJECT: 16084 { 16085 JSObject *p; 16086 p = JS_VALUE_GET_OBJ(op1); 16087 if (unlikely(p->is_HTMLDDA)) 16088 atom = JS_ATOM_undefined; 16089 else if (JS_IsFunction(ctx, op1)) 16090 atom = JS_ATOM_function; 16091 else 16092 goto obj_type; 16093 } 16094 break; 16095 case JS_TAG_NULL: 16096 obj_type: 16097 atom = JS_ATOM_object; 16098 break; 16099 case JS_TAG_SYMBOL: 16100 atom = JS_ATOM_symbol; 16101 break; 16102 default: 16103 atom = JS_ATOM_unknown; 16104 break; 16105 } 16106 return atom; 16107 } 16108 16109 static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) 16110 { 16111 JSValue op1, op2; 16112 JSAtom atom; 16113 int ret; 16114 16115 op1 = sp[-2]; 16116 op2 = sp[-1]; 16117 atom = JS_ValueToAtom(ctx, op2); 16118 if (unlikely(atom == JS_ATOM_NULL)) 16119 return -1; 16120 ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); 16121 JS_FreeAtom(ctx, atom); 16122 if (unlikely(ret < 0)) 16123 return -1; 16124 JS_FreeValue(ctx, op1); 16125 JS_FreeValue(ctx, op2); 16126 sp[-2] = JS_NewBool(ctx, ret); 16127 return 0; 16128 } 16129 16130 /* XXX: not 100% compatible, but mozilla seems to use a similar 16131 implementation to ensure that caller in non strict mode does not 16132 throw (ES5 compatibility) */ 16133 static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, 16134 int argc, JSValueConst *argv) 16135 { 16136 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 16137 if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) { 16138 return JS_ThrowTypeError(ctx, "invalid property access"); 16139 } 16140 return JS_UNDEFINED; 16141 } 16142 16143 static JSValue js_function_proto_fileName(JSContext *ctx, 16144 JSValueConst this_val) 16145 { 16146 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 16147 if (b && b->has_debug) { 16148 return JS_AtomToString(ctx, b->debug.filename); 16149 } 16150 return JS_UNDEFINED; 16151 } 16152 16153 static JSValue js_function_proto_lineNumber(JSContext *ctx, 16154 JSValueConst this_val, int is_col) 16155 { 16156 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 16157 if (b && b->has_debug) { 16158 int line_num, col_num; 16159 line_num = find_line_num(ctx, b, -1, &col_num); 16160 if (is_col) 16161 return JS_NewInt32(ctx, col_num); 16162 else 16163 return JS_NewInt32(ctx, line_num); 16164 } 16165 return JS_UNDEFINED; 16166 } 16167 16168 static int js_arguments_define_own_property(JSContext *ctx, 16169 JSValueConst this_obj, 16170 JSAtom prop, JSValueConst val, 16171 JSValueConst getter, JSValueConst setter, int flags) 16172 { 16173 JSObject *p; 16174 uint32_t idx; 16175 p = JS_VALUE_GET_OBJ(this_obj); 16176 /* convert to normal array when redefining an existing numeric field */ 16177 if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && 16178 idx < p->u.array.count) { 16179 if (convert_fast_array_to_array(ctx, p)) 16180 return -1; 16181 } 16182 /* run the default define own property */ 16183 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, 16184 flags | JS_PROP_NO_EXOTIC); 16185 } 16186 16187 static const JSClassExoticMethods js_arguments_exotic_methods = { 16188 .define_own_property = js_arguments_define_own_property, 16189 }; 16190 16191 static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) 16192 { 16193 JSValue val, *tab; 16194 JSProperty props[3]; 16195 JSObject *p; 16196 int i; 16197 16198 props[0].u.value = JS_NewInt32(ctx, argc); /* length */ 16199 props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */ 16200 props[2].u.getset.getter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */ 16201 props[2].u.getset.setter = JS_VALUE_GET_OBJ(JS_DupValue(ctx, ctx->throw_type_error)); /* callee */ 16202 16203 val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->arguments_shape), 16204 JS_CLASS_ARGUMENTS, props); 16205 if (JS_IsException(val)) 16206 return val; 16207 p = JS_VALUE_GET_OBJ(val); 16208 16209 /* initialize the fast array part */ 16210 tab = NULL; 16211 if (argc > 0) { 16212 tab = js_malloc(ctx, sizeof(tab[0]) * argc); 16213 if (!tab) 16214 goto fail; 16215 for(i = 0; i < argc; i++) { 16216 tab[i] = JS_DupValue(ctx, argv[i]); 16217 } 16218 } 16219 p->u.array.u.values = tab; 16220 p->u.array.count = argc; 16221 return val; 16222 fail: 16223 JS_FreeValue(ctx, val); 16224 return JS_EXCEPTION; 16225 } 16226 16227 #define GLOBAL_VAR_OFFSET 0x40000000 16228 #define ARGUMENT_VAR_OFFSET 0x20000000 16229 16230 static void js_mapped_arguments_finalizer(JSRuntime *rt, JSValue val) 16231 { 16232 JSObject *p = JS_VALUE_GET_OBJ(val); 16233 JSVarRef **var_refs = p->u.array.u.var_refs; 16234 int i; 16235 for(i = 0; i < p->u.array.count; i++) 16236 free_var_ref(rt, var_refs[i]); 16237 js_free_rt(rt, var_refs); 16238 } 16239 16240 static void js_mapped_arguments_mark(JSRuntime *rt, JSValueConst val, 16241 JS_MarkFunc *mark_func) 16242 { 16243 JSObject *p = JS_VALUE_GET_OBJ(val); 16244 JSVarRef **var_refs = p->u.array.u.var_refs; 16245 int i; 16246 16247 for(i = 0; i < p->u.array.count; i++) 16248 mark_func(rt, &var_refs[i]->header); 16249 } 16250 16251 /* legacy arguments object: add references to the function arguments */ 16252 static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, 16253 JSValueConst *argv, 16254 JSStackFrame *sf, int arg_count) 16255 { 16256 JSValue val; 16257 JSProperty props[3]; 16258 JSVarRef **tab, *var_ref; 16259 JSObject *p; 16260 int i, j; 16261 16262 props[0].u.value = JS_NewInt32(ctx, argc); /* length */ 16263 props[1].u.value = JS_DupValue(ctx, ctx->array_proto_values); /* Symbol.iterator */ 16264 props[2].u.value = JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func); /* callee */ 16265 16266 val = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->mapped_arguments_shape), 16267 JS_CLASS_MAPPED_ARGUMENTS, props); 16268 if (JS_IsException(val)) 16269 return val; 16270 p = JS_VALUE_GET_OBJ(val); 16271 16272 /* initialize the fast array part */ 16273 tab = NULL; 16274 if (argc > 0) { 16275 tab = js_malloc(ctx, sizeof(tab[0]) * argc); 16276 if (!tab) 16277 goto fail; 16278 for(i = 0; i < arg_count; i++) { 16279 var_ref = get_var_ref(ctx, sf, i, TRUE); 16280 if (!var_ref) 16281 goto fail1; 16282 tab[i] = var_ref; 16283 } 16284 for(i = arg_count; i < argc; i++) { 16285 var_ref = js_create_var_ref(ctx, FALSE); 16286 if (!var_ref) { 16287 fail1: 16288 for(j = 0; j < i; j++) 16289 free_var_ref(ctx->rt, tab[j]); 16290 js_free(ctx, tab); 16291 goto fail; 16292 } 16293 var_ref->value = JS_DupValue(ctx, argv[i]); 16294 tab[i] = var_ref; 16295 } 16296 } 16297 p->u.array.u.var_refs = tab; 16298 p->u.array.count = argc; 16299 return val; 16300 fail: 16301 JS_FreeValue(ctx, val); 16302 return JS_EXCEPTION; 16303 } 16304 16305 static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) 16306 { 16307 JSObject *p, *p1; 16308 JSPropertyEnum *tab_atom; 16309 int i; 16310 JSValue enum_obj; 16311 JSForInIterator *it; 16312 uint32_t tag, tab_atom_count; 16313 16314 tag = JS_VALUE_GET_TAG(obj); 16315 if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { 16316 obj = JS_ToObjectFree(ctx, obj); 16317 } 16318 16319 it = js_malloc(ctx, sizeof(*it)); 16320 if (!it) { 16321 JS_FreeValue(ctx, obj); 16322 return JS_EXCEPTION; 16323 } 16324 enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); 16325 if (JS_IsException(enum_obj)) { 16326 js_free(ctx, it); 16327 JS_FreeValue(ctx, obj); 16328 return JS_EXCEPTION; 16329 } 16330 it->is_array = FALSE; 16331 it->obj = obj; 16332 it->idx = 0; 16333 it->tab_atom = NULL; 16334 it->atom_count = 0; 16335 it->in_prototype_chain = FALSE; 16336 p1 = JS_VALUE_GET_OBJ(enum_obj); 16337 p1->u.for_in_iterator = it; 16338 16339 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) 16340 return enum_obj; 16341 16342 p = JS_VALUE_GET_OBJ(obj); 16343 if (p->fast_array) { 16344 JSShape *sh; 16345 JSShapeProperty *prs; 16346 /* check that there are no enumerable normal fields */ 16347 sh = p->shape; 16348 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 16349 if (prs->flags & JS_PROP_ENUMERABLE) 16350 goto normal_case; 16351 } 16352 /* for fast arrays, we only store the number of elements */ 16353 it->is_array = TRUE; 16354 it->atom_count = p->u.array.count; 16355 } else { 16356 normal_case: 16357 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, 16358 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 16359 JS_FreeValue(ctx, enum_obj); 16360 return JS_EXCEPTION; 16361 } 16362 it->tab_atom = tab_atom; 16363 it->atom_count = tab_atom_count; 16364 } 16365 return enum_obj; 16366 } 16367 16368 /* obj -> enum_obj */ 16369 static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) 16370 { 16371 sp[-1] = build_for_in_iterator(ctx, sp[-1]); 16372 if (JS_IsException(sp[-1])) 16373 return -1; 16374 return 0; 16375 } 16376 16377 /* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */ 16378 static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx, 16379 JSValueConst enum_obj) 16380 { 16381 JSObject *p; 16382 JSForInIterator *it; 16383 JSPropertyEnum *tab_atom; 16384 uint32_t tab_atom_count, i; 16385 JSValue obj1; 16386 16387 p = JS_VALUE_GET_OBJ(enum_obj); 16388 it = p->u.for_in_iterator; 16389 16390 /* check if there are enumerable properties in the prototype chain (fast path) */ 16391 obj1 = JS_DupValue(ctx, it->obj); 16392 for(;;) { 16393 obj1 = JS_GetPrototypeFree(ctx, obj1); 16394 if (JS_IsNull(obj1)) 16395 break; 16396 if (JS_IsException(obj1)) 16397 goto fail; 16398 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 16399 JS_VALUE_GET_OBJ(obj1), 16400 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { 16401 JS_FreeValue(ctx, obj1); 16402 goto fail; 16403 } 16404 JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); 16405 if (tab_atom_count != 0) { 16406 JS_FreeValue(ctx, obj1); 16407 goto slow_path; 16408 } 16409 /* must check for timeout to avoid infinite loop */ 16410 if (js_poll_interrupts(ctx)) { 16411 JS_FreeValue(ctx, obj1); 16412 goto fail; 16413 } 16414 } 16415 JS_FreeValue(ctx, obj1); 16416 return 1; 16417 16418 slow_path: 16419 /* add the visited properties, even if they are not enumerable */ 16420 if (it->is_array) { 16421 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 16422 JS_VALUE_GET_OBJ(it->obj), 16423 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 16424 goto fail; 16425 } 16426 it->is_array = FALSE; 16427 it->tab_atom = tab_atom; 16428 it->atom_count = tab_atom_count; 16429 } 16430 16431 for(i = 0; i < it->atom_count; i++) { 16432 if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0) 16433 goto fail; 16434 } 16435 return 0; 16436 fail: 16437 return -1; 16438 } 16439 16440 /* enum_obj -> enum_obj value done */ 16441 static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) 16442 { 16443 JSValueConst enum_obj; 16444 JSObject *p; 16445 JSAtom prop; 16446 JSForInIterator *it; 16447 JSPropertyEnum *tab_atom; 16448 uint32_t tab_atom_count; 16449 int ret; 16450 16451 enum_obj = sp[-1]; 16452 /* fail safe */ 16453 if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) 16454 goto done; 16455 p = JS_VALUE_GET_OBJ(enum_obj); 16456 if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) 16457 goto done; 16458 it = p->u.for_in_iterator; 16459 16460 for(;;) { 16461 if (it->idx >= it->atom_count) { 16462 if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj)) 16463 goto done; /* not an object */ 16464 /* no more property in the current object: look in the prototype */ 16465 if (!it->in_prototype_chain) { 16466 ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj); 16467 if (ret < 0) 16468 return -1; 16469 if (ret) 16470 goto done; 16471 it->in_prototype_chain = TRUE; 16472 } 16473 it->obj = JS_GetPrototypeFree(ctx, it->obj); 16474 if (JS_IsException(it->obj)) 16475 return -1; 16476 if (JS_IsNull(it->obj)) 16477 goto done; /* no more prototype */ 16478 16479 /* must check for timeout to avoid infinite loop */ 16480 if (js_poll_interrupts(ctx)) 16481 return -1; 16482 16483 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 16484 JS_VALUE_GET_OBJ(it->obj), 16485 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 16486 return -1; 16487 } 16488 JS_FreePropertyEnum(ctx, it->tab_atom, it->atom_count); 16489 it->tab_atom = tab_atom; 16490 it->atom_count = tab_atom_count; 16491 it->idx = 0; 16492 } else { 16493 if (it->is_array) { 16494 prop = __JS_AtomFromUInt32(it->idx); 16495 it->idx++; 16496 } else { 16497 BOOL is_enumerable; 16498 prop = it->tab_atom[it->idx].atom; 16499 is_enumerable = it->tab_atom[it->idx].is_enumerable; 16500 it->idx++; 16501 if (it->in_prototype_chain) { 16502 /* slow case: we are in the prototype chain */ 16503 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop); 16504 if (ret < 0) 16505 return ret; 16506 if (ret) 16507 continue; /* already visited */ 16508 /* add to the visited property list */ 16509 if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL, 16510 JS_PROP_ENUMERABLE) < 0) 16511 return -1; 16512 } 16513 if (!is_enumerable) 16514 continue; 16515 } 16516 /* check if the property was deleted */ 16517 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop); 16518 if (ret < 0) 16519 return ret; 16520 if (ret) 16521 break; 16522 } 16523 } 16524 /* return the property */ 16525 sp[0] = JS_AtomToValue(ctx, prop); 16526 sp[1] = JS_FALSE; 16527 return 0; 16528 done: 16529 /* return the end */ 16530 sp[0] = JS_UNDEFINED; 16531 sp[1] = JS_TRUE; 16532 return 0; 16533 } 16534 16535 static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, 16536 JSValueConst method) 16537 { 16538 JSValue enum_obj; 16539 16540 enum_obj = JS_Call(ctx, method, obj, 0, NULL); 16541 if (JS_IsException(enum_obj)) 16542 return enum_obj; 16543 if (!JS_IsObject(enum_obj)) { 16544 JS_FreeValue(ctx, enum_obj); 16545 return JS_ThrowTypeErrorNotAnObject(ctx); 16546 } 16547 return enum_obj; 16548 } 16549 16550 static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) 16551 { 16552 JSValue method, ret, sync_iter; 16553 16554 if (is_async) { 16555 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); 16556 if (JS_IsException(method)) 16557 return method; 16558 if (JS_IsUndefined(method) || JS_IsNull(method)) { 16559 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 16560 if (JS_IsException(method)) 16561 return method; 16562 sync_iter = JS_GetIterator2(ctx, obj, method); 16563 JS_FreeValue(ctx, method); 16564 if (JS_IsException(sync_iter)) 16565 return sync_iter; 16566 ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); 16567 JS_FreeValue(ctx, sync_iter); 16568 return ret; 16569 } 16570 } else { 16571 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 16572 if (JS_IsException(method)) 16573 return method; 16574 } 16575 if (!JS_IsFunction(ctx, method)) { 16576 JS_FreeValue(ctx, method); 16577 return JS_ThrowTypeError(ctx, "value is not iterable"); 16578 } 16579 ret = JS_GetIterator2(ctx, obj, method); 16580 JS_FreeValue(ctx, method); 16581 return ret; 16582 } 16583 16584 /* return *pdone = 2 if the iterator object is not parsed */ 16585 static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, 16586 JSValueConst method, 16587 int argc, JSValueConst *argv, int *pdone) 16588 { 16589 JSValue obj; 16590 16591 /* fast path for the built-in iterators (avoid creating the 16592 intermediate result object) */ 16593 if (JS_IsObject(method)) { 16594 JSObject *p = JS_VALUE_GET_OBJ(method); 16595 if (p->class_id == JS_CLASS_C_FUNCTION && 16596 p->u.cfunc.cproto == JS_CFUNC_iterator_next) { 16597 JSCFunctionType func; 16598 JSValueConst args[1]; 16599 16600 /* in case the function expects one argument */ 16601 if (argc == 0) { 16602 args[0] = JS_UNDEFINED; 16603 argv = args; 16604 } 16605 func = p->u.cfunc.c_function; 16606 return func.iterator_next(ctx, enum_obj, argc, argv, 16607 pdone, p->u.cfunc.magic); 16608 } 16609 } 16610 obj = JS_Call(ctx, method, enum_obj, argc, argv); 16611 if (JS_IsException(obj)) 16612 goto fail; 16613 if (!JS_IsObject(obj)) { 16614 JS_FreeValue(ctx, obj); 16615 JS_ThrowTypeError(ctx, "iterator must return an object"); 16616 goto fail; 16617 } 16618 *pdone = 2; 16619 return obj; 16620 fail: 16621 *pdone = FALSE; 16622 return JS_EXCEPTION; 16623 } 16624 16625 /* Note: always return JS_UNDEFINED when *pdone = TRUE. */ 16626 static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, 16627 JSValueConst method, 16628 int argc, JSValueConst *argv, BOOL *pdone) 16629 { 16630 JSValue obj, value, done_val; 16631 int done; 16632 16633 obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); 16634 if (JS_IsException(obj)) 16635 goto fail; 16636 if (likely(done == 0)) { 16637 *pdone = FALSE; 16638 return obj; 16639 } else if (done != 2) { 16640 JS_FreeValue(ctx, obj); 16641 *pdone = TRUE; 16642 return JS_UNDEFINED; 16643 } else { 16644 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); 16645 if (JS_IsException(done_val)) 16646 goto fail; 16647 *pdone = JS_ToBoolFree(ctx, done_val); 16648 value = JS_UNDEFINED; 16649 if (!*pdone) { 16650 value = JS_GetProperty(ctx, obj, JS_ATOM_value); 16651 } 16652 JS_FreeValue(ctx, obj); 16653 return value; 16654 } 16655 fail: 16656 JS_FreeValue(ctx, obj); 16657 *pdone = FALSE; 16658 return JS_EXCEPTION; 16659 } 16660 16661 /* return < 0 in case of exception */ 16662 static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, 16663 BOOL is_exception_pending) 16664 { 16665 JSValue method, ret, ex_obj; 16666 int res; 16667 16668 if (is_exception_pending) { 16669 ex_obj = ctx->rt->current_exception; 16670 ctx->rt->current_exception = JS_UNINITIALIZED; 16671 res = -1; 16672 } else { 16673 ex_obj = JS_UNDEFINED; 16674 res = 0; 16675 } 16676 method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); 16677 if (JS_IsException(method)) { 16678 res = -1; 16679 goto done; 16680 } 16681 if (JS_IsUndefined(method) || JS_IsNull(method)) { 16682 goto done; 16683 } 16684 ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); 16685 if (!is_exception_pending) { 16686 if (JS_IsException(ret)) { 16687 res = -1; 16688 } else if (!JS_IsObject(ret)) { 16689 JS_ThrowTypeErrorNotAnObject(ctx); 16690 res = -1; 16691 } 16692 } 16693 JS_FreeValue(ctx, ret); 16694 done: 16695 if (is_exception_pending) { 16696 JS_Throw(ctx, ex_obj); 16697 } 16698 return res; 16699 } 16700 16701 /* obj -> enum_rec (3 slots) */ 16702 static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, 16703 BOOL is_async) 16704 { 16705 JSValue op1, obj, method; 16706 op1 = sp[-1]; 16707 obj = JS_GetIterator(ctx, op1, is_async); 16708 if (JS_IsException(obj)) 16709 return -1; 16710 JS_FreeValue(ctx, op1); 16711 sp[-1] = obj; 16712 method = JS_GetProperty(ctx, obj, JS_ATOM_next); 16713 if (JS_IsException(method)) 16714 return -1; 16715 sp[0] = method; 16716 return 0; 16717 } 16718 16719 /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' 16720 objs. If 'done' is true or in case of exception, 'enum_rec' is set 16721 to undefined. If 'done' is true, 'value' is always set to 16722 undefined. */ 16723 static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) 16724 { 16725 JSValue value = JS_UNDEFINED; 16726 int done = 1; 16727 16728 if (likely(!JS_IsUndefined(sp[offset]))) { 16729 value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); 16730 if (JS_IsException(value)) 16731 done = -1; 16732 if (done) { 16733 /* value is JS_UNDEFINED or JS_EXCEPTION */ 16734 /* replace the iteration object with undefined */ 16735 JS_FreeValue(ctx, sp[offset]); 16736 sp[offset] = JS_UNDEFINED; 16737 if (done < 0) { 16738 return -1; 16739 } else { 16740 JS_FreeValue(ctx, value); 16741 value = JS_UNDEFINED; 16742 } 16743 } 16744 } 16745 sp[0] = value; 16746 sp[1] = JS_NewBool(ctx, done); 16747 return 0; 16748 } 16749 16750 static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp) 16751 { 16752 JSValue obj, iter, next; 16753 16754 sp[-1] = JS_UNDEFINED; /* disable the catch offset so that 16755 exceptions do not close the iterator */ 16756 iter = sp[-3]; 16757 next = sp[-2]; 16758 obj = JS_Call(ctx, next, iter, 0, NULL); 16759 if (JS_IsException(obj)) 16760 return -1; 16761 sp[0] = obj; 16762 return 0; 16763 } 16764 16765 static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, 16766 BOOL *pdone) 16767 { 16768 JSValue done_val, value; 16769 BOOL done; 16770 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); 16771 if (JS_IsException(done_val)) 16772 goto fail; 16773 done = JS_ToBoolFree(ctx, done_val); 16774 value = JS_GetProperty(ctx, obj, JS_ATOM_value); 16775 if (JS_IsException(value)) 16776 goto fail; 16777 *pdone = done; 16778 return value; 16779 fail: 16780 *pdone = FALSE; 16781 return JS_EXCEPTION; 16782 } 16783 16784 static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) 16785 { 16786 JSValue obj, value; 16787 BOOL done; 16788 obj = sp[-1]; 16789 if (!JS_IsObject(obj)) { 16790 JS_ThrowTypeError(ctx, "iterator must return an object"); 16791 return -1; 16792 } 16793 value = JS_IteratorGetCompleteValue(ctx, obj, &done); 16794 if (JS_IsException(value)) 16795 return -1; 16796 JS_FreeValue(ctx, obj); 16797 /* put again the catch offset so that exceptions close the 16798 iterator */ 16799 sp[-2] = JS_NewCatchOffset(ctx, 0); 16800 sp[-1] = value; 16801 sp[0] = JS_NewBool(ctx, done); 16802 return 0; 16803 } 16804 16805 static JSValue js_create_iterator_result(JSContext *ctx, 16806 JSValue val, 16807 BOOL done) 16808 { 16809 JSValue obj; 16810 obj = JS_NewObject(ctx); 16811 if (JS_IsException(obj)) { 16812 JS_FreeValue(ctx, val); 16813 return obj; 16814 } 16815 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, 16816 val, JS_PROP_C_W_E) < 0) { 16817 goto fail; 16818 } 16819 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, 16820 JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { 16821 fail: 16822 JS_FreeValue(ctx, obj); 16823 return JS_EXCEPTION; 16824 } 16825 return obj; 16826 } 16827 16828 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, 16829 int argc, JSValueConst *argv, 16830 BOOL *pdone, int magic); 16831 16832 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, 16833 int argc, JSValueConst *argv, int magic); 16834 16835 static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) 16836 { 16837 /* Try and handle fast arrays explicitly */ 16838 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 16839 JSObject *p = JS_VALUE_GET_OBJ(obj); 16840 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { 16841 return TRUE; 16842 } 16843 } 16844 return FALSE; 16845 } 16846 16847 /* Access an Array's internal JSValue array if available */ 16848 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, 16849 JSValue **arrpp, uint32_t *countp) 16850 { 16851 /* Try and handle fast arrays explicitly */ 16852 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 16853 JSObject *p = JS_VALUE_GET_OBJ(obj); 16854 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { 16855 *countp = p->u.array.count; 16856 *arrpp = p->u.array.u.values; 16857 return TRUE; 16858 } 16859 } 16860 return FALSE; 16861 } 16862 16863 static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) 16864 { 16865 JSValue iterator, enumobj, method, value; 16866 int is_array_iterator; 16867 JSValue *arrp; 16868 uint32_t i, count32, pos; 16869 JSCFunctionType ft; 16870 16871 if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { 16872 JS_ThrowInternalError(ctx, "invalid index for append"); 16873 return -1; 16874 } 16875 16876 pos = JS_VALUE_GET_INT(sp[-2]); 16877 16878 /* XXX: further optimisations: 16879 - use ctx->array_proto_values? 16880 - check if array_iterator_prototype next method is built-in and 16881 avoid constructing actual iterator object? 16882 - build this into js_for_of_start and use in all `for (x of o)` loops 16883 */ 16884 iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); 16885 if (JS_IsException(iterator)) 16886 return -1; 16887 ft.generic_magic = js_create_array_iterator; 16888 is_array_iterator = JS_IsCFunction(ctx, iterator, ft.generic, 16889 JS_ITERATOR_KIND_VALUE); 16890 JS_FreeValue(ctx, iterator); 16891 16892 enumobj = JS_GetIterator(ctx, sp[-1], FALSE); 16893 if (JS_IsException(enumobj)) 16894 return -1; 16895 method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); 16896 if (JS_IsException(method)) { 16897 JS_FreeValue(ctx, enumobj); 16898 return -1; 16899 } 16900 16901 ft.iterator_next = js_array_iterator_next; 16902 if (is_array_iterator 16903 && JS_IsCFunction(ctx, method, ft.generic, 0) 16904 && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { 16905 uint32_t len; 16906 if (js_get_length32(ctx, &len, sp[-1])) 16907 goto exception; 16908 /* if len > count32, the elements >= count32 might be read in 16909 the prototypes and might have side effects */ 16910 if (len != count32) 16911 goto general_case; 16912 /* Handle fast arrays explicitly */ 16913 for (i = 0; i < count32; i++) { 16914 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, 16915 JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) 16916 goto exception; 16917 } 16918 } else { 16919 general_case: 16920 for (;;) { 16921 BOOL done; 16922 value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); 16923 if (JS_IsException(value)) 16924 goto exception; 16925 if (done) { 16926 /* value is JS_UNDEFINED */ 16927 break; 16928 } 16929 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) 16930 goto exception; 16931 } 16932 } 16933 /* Note: could raise an error if too many elements */ 16934 sp[-2] = JS_NewInt32(ctx, pos); 16935 JS_FreeValue(ctx, enumobj); 16936 JS_FreeValue(ctx, method); 16937 return 0; 16938 16939 exception: 16940 JS_IteratorClose(ctx, enumobj, TRUE); 16941 JS_FreeValue(ctx, enumobj); 16942 JS_FreeValue(ctx, method); 16943 return -1; 16944 } 16945 16946 static __exception int JS_CopyDataProperties(JSContext *ctx, 16947 JSValueConst target, 16948 JSValueConst source, 16949 JSValueConst excluded, 16950 BOOL setprop) 16951 { 16952 JSPropertyEnum *tab_atom; 16953 JSValue val; 16954 uint32_t i, tab_atom_count; 16955 JSObject *p; 16956 JSObject *pexcl = NULL; 16957 int ret, gpn_flags; 16958 JSPropertyDescriptor desc; 16959 BOOL is_enumerable; 16960 16961 if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) 16962 return 0; 16963 16964 if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) 16965 pexcl = JS_VALUE_GET_OBJ(excluded); 16966 16967 p = JS_VALUE_GET_OBJ(source); 16968 16969 gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; 16970 if (p->is_exotic) { 16971 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 16972 /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it 16973 introduces a visible change */ 16974 if (em && em->get_own_property_names) { 16975 gpn_flags &= ~JS_GPN_ENUM_ONLY; 16976 } 16977 } 16978 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, 16979 gpn_flags)) 16980 return -1; 16981 16982 for (i = 0; i < tab_atom_count; i++) { 16983 if (pexcl) { 16984 ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); 16985 if (ret) { 16986 if (ret < 0) 16987 goto exception; 16988 continue; 16989 } 16990 } 16991 if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { 16992 /* test if the property is enumerable */ 16993 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); 16994 if (ret < 0) 16995 goto exception; 16996 if (!ret) 16997 continue; 16998 is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; 16999 js_free_desc(ctx, &desc); 17000 if (!is_enumerable) 17001 continue; 17002 } 17003 val = JS_GetProperty(ctx, source, tab_atom[i].atom); 17004 if (JS_IsException(val)) 17005 goto exception; 17006 if (setprop) 17007 ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); 17008 else 17009 ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, 17010 JS_PROP_C_W_E); 17011 if (ret < 0) 17012 goto exception; 17013 } 17014 JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); 17015 return 0; 17016 exception: 17017 JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count); 17018 return -1; 17019 } 17020 17021 /* only valid inside C functions */ 17022 static JSValueConst JS_GetActiveFunction(JSContext *ctx) 17023 { 17024 return ctx->rt->current_stack_frame->cur_func; 17025 } 17026 17027 static JSVarRef *js_create_var_ref(JSContext *ctx, BOOL is_lexical) 17028 { 17029 JSVarRef *var_ref; 17030 var_ref = js_malloc(ctx, sizeof(JSVarRef)); 17031 if (!var_ref) 17032 return NULL; 17033 js_rc(var_ref)->ref_count = 1; 17034 if (is_lexical) 17035 var_ref->value = JS_UNINITIALIZED; 17036 else 17037 var_ref->value = JS_UNDEFINED; 17038 var_ref->pvalue = &var_ref->value; 17039 var_ref->is_detached = TRUE; 17040 var_ref->is_lexical = FALSE; 17041 var_ref->is_const = FALSE; 17042 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); 17043 return var_ref; 17044 } 17045 17046 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, 17047 BOOL is_arg) 17048 { 17049 JSObject *p; 17050 JSFunctionBytecode *b; 17051 JSVarRef *var_ref; 17052 JSValue *pvalue; 17053 int var_ref_idx; 17054 JSBytecodeVarDef *vd; 17055 17056 p = JS_VALUE_GET_OBJ(sf->cur_func); 17057 b = p->u.func.function_bytecode; 17058 17059 if (is_arg) { 17060 vd = &b->vardefs[var_idx]; 17061 pvalue = &sf->arg_buf[var_idx]; 17062 } else { 17063 vd = &b->vardefs[b->arg_count + var_idx]; 17064 pvalue = &sf->var_buf[var_idx]; 17065 } 17066 assert(vd->is_captured); 17067 var_ref_idx = vd->var_ref_idx; 17068 assert(var_ref_idx < b->var_ref_count); 17069 var_ref = sf->var_refs[var_ref_idx]; 17070 if (var_ref) { 17071 /* reference to the already created local variable */ 17072 assert(var_ref->pvalue == pvalue); 17073 js_rc(var_ref)->ref_count++; 17074 return var_ref; 17075 } 17076 17077 /* create a new one */ 17078 var_ref = js_malloc(ctx, sizeof(JSVarRef)); 17079 if (!var_ref) 17080 return NULL; 17081 js_rc(var_ref)->ref_count = 1; 17082 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); 17083 var_ref->is_detached = FALSE; 17084 var_ref->is_lexical = FALSE; 17085 var_ref->is_const = FALSE; 17086 var_ref->var_ref_idx = var_ref_idx; 17087 var_ref->stack_frame = sf; 17088 sf->var_refs[var_ref_idx] = var_ref; 17089 if (sf->js_mode & JS_MODE_ASYNC) { 17090 JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); 17091 /* The stack frame is detached and may be destroyed at any 17092 time so its reference count must be increased. Calling 17093 close_var_refs() when destroying the stack frame is not 17094 possible because it would change the graph between the GC 17095 objects. Another solution could be to temporarily detach 17096 the JSVarRef of async functions during the GC. It would 17097 have the advantage of allowing the release of unused stack 17098 frames in a cycle. */ 17099 js_rc(async_func)->ref_count++; 17100 } 17101 var_ref->pvalue = pvalue; 17102 return var_ref; 17103 } 17104 17105 static void js_global_object_finalizer(JSRuntime *rt, JSValue obj) 17106 { 17107 JSObject *p = JS_VALUE_GET_OBJ(obj); 17108 JS_FreeValueRT(rt, p->u.global_object.uninitialized_vars); 17109 } 17110 17111 static void js_global_object_mark(JSRuntime *rt, JSValueConst val, 17112 JS_MarkFunc *mark_func) 17113 { 17114 JSObject *p = JS_VALUE_GET_OBJ(val); 17115 JS_MarkValue(rt, p->u.global_object.uninitialized_vars, mark_func); 17116 } 17117 17118 static JSVarRef *js_global_object_get_uninitialized_var(JSContext *ctx, JSObject *p1, 17119 JSAtom atom) 17120 { 17121 JSObject *p = JS_VALUE_GET_OBJ(p1->u.global_object.uninitialized_vars); 17122 JSShapeProperty *prs; 17123 JSProperty *pr; 17124 JSVarRef *var_ref; 17125 17126 prs = find_own_property(&pr, p, atom); 17127 if (prs) { 17128 assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); 17129 var_ref = pr->u.var_ref; 17130 js_rc(var_ref)->ref_count++; 17131 return var_ref; 17132 } 17133 17134 var_ref = js_create_var_ref(ctx, TRUE); 17135 if (!var_ref) 17136 return NULL; 17137 pr = add_property(ctx, p, atom, JS_PROP_C_W_E | JS_PROP_VARREF); 17138 if (unlikely(!pr)) { 17139 free_var_ref(ctx->rt, var_ref); 17140 return NULL; 17141 } 17142 pr->u.var_ref = var_ref; 17143 js_rc(var_ref)->ref_count++; 17144 return var_ref; 17145 } 17146 17147 /* return a new variable reference. Get it from the uninitialized 17148 variables if it is present. Return NULL in case of memory error. */ 17149 static JSVarRef *js_global_object_find_uninitialized_var(JSContext *ctx, JSObject *p, 17150 JSAtom atom, BOOL is_lexical) 17151 { 17152 JSObject *p1; 17153 JSShapeProperty *prs; 17154 JSProperty *pr; 17155 JSVarRef *var_ref; 17156 17157 p1 = JS_VALUE_GET_OBJ(p->u.global_object.uninitialized_vars); 17158 prs = find_own_property(&pr, p1, atom); 17159 if (prs) { 17160 assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); 17161 var_ref = pr->u.var_ref; 17162 js_rc(var_ref)->ref_count++; 17163 delete_property(ctx, p1, atom); 17164 if (!is_lexical) 17165 var_ref->value = JS_UNDEFINED; 17166 } else { 17167 var_ref = js_create_var_ref(ctx, is_lexical); 17168 if (!var_ref) 17169 return NULL; 17170 } 17171 return var_ref; 17172 } 17173 17174 static JSVarRef *js_closure_define_global_var(JSContext *ctx, JSClosureVar *cv, 17175 BOOL is_direct_or_indirect_eval) 17176 { 17177 JSObject *p, *p1; 17178 JSShapeProperty *prs; 17179 int flags; 17180 JSProperty *pr; 17181 JSVarRef *var_ref; 17182 17183 if (cv->is_lexical) { 17184 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 17185 flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; 17186 if (!cv->is_const) 17187 flags |= JS_PROP_WRITABLE; 17188 17189 prs = find_own_property(&pr, p, cv->var_name); 17190 if (prs) { 17191 assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); 17192 var_ref = pr->u.var_ref; 17193 js_rc(var_ref)->ref_count++; 17194 return var_ref; 17195 } 17196 17197 /* if there is a corresponding global variable, reuse its 17198 reference and create a new one for the global variable */ 17199 p1 = JS_VALUE_GET_OBJ(ctx->global_obj); 17200 prs = find_own_property(&pr, p1, cv->var_name); 17201 if (prs && (prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 17202 JSVarRef *var_ref1; 17203 var_ref1 = js_create_var_ref(ctx, FALSE); 17204 if (!var_ref1) 17205 return NULL; 17206 var_ref = pr->u.var_ref; 17207 var_ref1->value = var_ref->value; 17208 var_ref->value = JS_UNINITIALIZED; 17209 pr->u.var_ref = var_ref1; 17210 goto add_var_ref; 17211 } 17212 } else { 17213 p = JS_VALUE_GET_OBJ(ctx->global_obj); 17214 flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE; 17215 if (is_direct_or_indirect_eval) 17216 flags |= JS_PROP_CONFIGURABLE; 17217 17218 retry: 17219 prs = find_own_property(&pr, p, cv->var_name); 17220 if (prs) { 17221 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) { 17222 if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs)) 17223 return NULL; 17224 goto retry; 17225 } else if ((prs->flags & JS_PROP_TMASK) != JS_PROP_VARREF) { 17226 var_ref = js_global_object_get_uninitialized_var(ctx, p, cv->var_name); 17227 if (!var_ref) 17228 return NULL; 17229 } else { 17230 var_ref = pr->u.var_ref; 17231 js_rc(var_ref)->ref_count++; 17232 } 17233 if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL && 17234 (prs->flags & JS_PROP_CONFIGURABLE)) { 17235 /* update the property flags if possible when 17236 declaring a global function */ 17237 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 17238 free_property(ctx->rt, pr, prs->flags); 17239 prs->flags = flags | JS_PROP_VARREF; 17240 pr->u.var_ref = var_ref; 17241 js_rc(var_ref)->ref_count++; 17242 } else { 17243 assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); 17244 prs->flags = (prs->flags & ~JS_PROP_C_W_E) | flags; 17245 } 17246 var_ref->is_const = FALSE; 17247 } 17248 return var_ref; 17249 } 17250 17251 if (!p->extensible) { 17252 return js_global_object_get_uninitialized_var(ctx, p, cv->var_name); 17253 } 17254 } 17255 17256 /* if there is a corresponding uninitialized variable, use it */ 17257 p1 = JS_VALUE_GET_OBJ(ctx->global_obj); 17258 var_ref = js_global_object_find_uninitialized_var(ctx, p1, cv->var_name, cv->is_lexical); 17259 if (!var_ref) 17260 return NULL; 17261 add_var_ref: 17262 if (cv->is_lexical) { 17263 var_ref->is_lexical = TRUE; 17264 var_ref->is_const = cv->is_const; 17265 } 17266 17267 pr = add_property(ctx, p, cv->var_name, flags | JS_PROP_VARREF); 17268 if (unlikely(!pr)) { 17269 free_var_ref(ctx->rt, var_ref); 17270 return NULL; 17271 } 17272 pr->u.var_ref = var_ref; 17273 js_rc(var_ref)->ref_count++; 17274 return var_ref; 17275 } 17276 17277 static JSVarRef *js_closure_global_var(JSContext *ctx, JSClosureVar *cv) 17278 { 17279 JSObject *p; 17280 JSShapeProperty *prs; 17281 JSProperty *pr; 17282 JSVarRef *var_ref; 17283 17284 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 17285 prs = find_own_property(&pr, p, cv->var_name); 17286 if (prs) { 17287 assert((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF); 17288 var_ref = pr->u.var_ref; 17289 js_rc(var_ref)->ref_count++; 17290 return var_ref; 17291 } 17292 p = JS_VALUE_GET_OBJ(ctx->global_obj); 17293 redo: 17294 prs = find_own_property(&pr, p, cv->var_name); 17295 if (prs) { 17296 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT)) { 17297 /* Instantiate property and retry */ 17298 if (JS_AutoInitProperty(ctx, p, cv->var_name, pr, prs)) 17299 return NULL; 17300 goto redo; 17301 } 17302 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 17303 var_ref = pr->u.var_ref; 17304 js_rc(var_ref)->ref_count++; 17305 return var_ref; 17306 } 17307 } 17308 return js_global_object_get_uninitialized_var(ctx, p, cv->var_name); 17309 } 17310 17311 static JSValue js_closure2(JSContext *ctx, JSValue func_obj, 17312 JSFunctionBytecode *b, 17313 JSVarRef **cur_var_refs, 17314 JSStackFrame *sf, 17315 BOOL is_eval, JSModuleDef *m) 17316 { 17317 JSObject *p; 17318 JSVarRef **var_refs; 17319 int i; 17320 17321 p = JS_VALUE_GET_OBJ(func_obj); 17322 p->u.func.function_bytecode = b; 17323 p->u.func.home_object = NULL; 17324 p->u.func.var_refs = NULL; 17325 if (b->closure_var_count) { 17326 var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); 17327 if (!var_refs) 17328 goto fail; 17329 p->u.func.var_refs = var_refs; 17330 if (is_eval) { 17331 /* first pass to check the global variable definitions */ 17332 for(i = 0; i < b->closure_var_count; i++) { 17333 JSClosureVar *cv = &b->closure_var[i]; 17334 if (cv->closure_type == JS_CLOSURE_GLOBAL_DECL) { 17335 int flags; 17336 flags = 0; 17337 if (cv->is_lexical) 17338 flags |= DEFINE_GLOBAL_LEX_VAR; 17339 if (cv->var_kind == JS_VAR_GLOBAL_FUNCTION_DECL) 17340 flags |= DEFINE_GLOBAL_FUNC_VAR; 17341 if (JS_CheckDefineGlobalVar(ctx, cv->var_name, flags)) 17342 goto fail; 17343 } 17344 } 17345 } 17346 for(i = 0; i < b->closure_var_count; i++) { 17347 JSClosureVar *cv = &b->closure_var[i]; 17348 JSVarRef *var_ref; 17349 switch(cv->closure_type) { 17350 case JS_CLOSURE_MODULE_IMPORT: 17351 /* imported from other modules */ 17352 continue; 17353 case JS_CLOSURE_MODULE_DECL: 17354 var_ref = js_create_var_ref(ctx, cv->is_lexical); 17355 break; 17356 case JS_CLOSURE_GLOBAL_DECL: 17357 var_ref = js_closure_define_global_var(ctx, cv, b->is_direct_or_indirect_eval); 17358 break; 17359 case JS_CLOSURE_GLOBAL: 17360 var_ref = js_closure_global_var(ctx, cv); 17361 break; 17362 case JS_CLOSURE_LOCAL: 17363 /* reuse the existing variable reference if it already exists */ 17364 var_ref = get_var_ref(ctx, sf, cv->var_idx, FALSE); 17365 break; 17366 case JS_CLOSURE_ARG: 17367 /* reuse the existing variable reference if it already exists */ 17368 var_ref = get_var_ref(ctx, sf, cv->var_idx, TRUE); 17369 break; 17370 case JS_CLOSURE_REF: 17371 case JS_CLOSURE_GLOBAL_REF: 17372 var_ref = cur_var_refs[cv->var_idx]; 17373 js_rc(var_ref)->ref_count++; 17374 break; 17375 default: 17376 abort(); 17377 } 17378 if (!var_ref) 17379 goto fail; 17380 var_refs[i] = var_ref; 17381 } 17382 } 17383 return func_obj; 17384 fail: 17385 /* bfunc is freed when func_obj is freed */ 17386 JS_FreeValue(ctx, func_obj); 17387 return JS_EXCEPTION; 17388 } 17389 17390 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) 17391 { 17392 JSValue obj, this_val; 17393 int ret; 17394 17395 this_val = JS_MKPTR(JS_TAG_OBJECT, p); 17396 obj = JS_NewObject(ctx); 17397 if (JS_IsException(obj)) 17398 return JS_EXCEPTION; 17399 set_cycle_flag(ctx, obj); 17400 set_cycle_flag(ctx, this_val); 17401 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, 17402 JS_DupValue(ctx, this_val), 17403 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 17404 if (ret < 0) { 17405 JS_FreeValue(ctx, obj); 17406 return JS_EXCEPTION; 17407 } 17408 return obj; 17409 } 17410 17411 static const uint16_t func_kind_to_class_id[] = { 17412 [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, 17413 [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, 17414 [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, 17415 [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, 17416 }; 17417 17418 static JSValue js_closure(JSContext *ctx, JSValue bfunc, 17419 JSVarRef **cur_var_refs, 17420 JSStackFrame *sf, BOOL is_eval) 17421 { 17422 JSFunctionBytecode *b; 17423 JSValue func_obj; 17424 JSAtom name_atom; 17425 17426 b = JS_VALUE_GET_PTR(bfunc); 17427 func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); 17428 if (JS_IsException(func_obj)) { 17429 JS_FreeValue(ctx, bfunc); 17430 return JS_EXCEPTION; 17431 } 17432 func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf, is_eval, NULL); 17433 if (JS_IsException(func_obj)) { 17434 /* bfunc has been freed */ 17435 goto fail; 17436 } 17437 name_atom = b->func_name; 17438 if (name_atom == JS_ATOM_NULL) 17439 name_atom = JS_ATOM_empty_string; 17440 js_function_set_properties(ctx, func_obj, name_atom, 17441 b->defined_arg_count); 17442 17443 if (b->func_kind & JS_FUNC_GENERATOR) { 17444 JSValue proto; 17445 int proto_class_id; 17446 /* generators have a prototype field which is used as 17447 prototype for the generator object */ 17448 if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) 17449 proto_class_id = JS_CLASS_ASYNC_GENERATOR; 17450 else 17451 proto_class_id = JS_CLASS_GENERATOR; 17452 proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); 17453 if (JS_IsException(proto)) 17454 goto fail; 17455 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, 17456 JS_PROP_WRITABLE); 17457 } else if (b->has_prototype) { 17458 /* add the 'prototype' property: delay instantiation to avoid 17459 creating cycles for every javascript function. The prototype 17460 object is created on the fly when first accessed */ 17461 JS_SetConstructorBit(ctx, func_obj, TRUE); 17462 JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, 17463 JS_AUTOINIT_ID_PROTOTYPE, NULL, 17464 JS_PROP_WRITABLE); 17465 } 17466 return func_obj; 17467 fail: 17468 /* bfunc is freed when func_obj is freed */ 17469 JS_FreeValue(ctx, func_obj); 17470 return JS_EXCEPTION; 17471 } 17472 17473 #define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) 17474 17475 static int js_op_define_class(JSContext *ctx, JSValue *sp, 17476 JSAtom class_name, int class_flags, 17477 JSVarRef **cur_var_refs, 17478 JSStackFrame *sf, BOOL is_computed_name) 17479 { 17480 JSValue bfunc, parent_class, proto = JS_UNDEFINED; 17481 JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; 17482 JSFunctionBytecode *b; 17483 17484 parent_class = sp[-2]; 17485 bfunc = sp[-1]; 17486 17487 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { 17488 if (JS_IsNull(parent_class)) { 17489 parent_proto = JS_NULL; 17490 parent_class = JS_DupValue(ctx, ctx->function_proto); 17491 } else { 17492 if (!JS_IsConstructor(ctx, parent_class)) { 17493 JS_ThrowTypeError(ctx, "parent class must be constructor"); 17494 goto fail; 17495 } 17496 parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); 17497 if (JS_IsException(parent_proto)) 17498 goto fail; 17499 if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { 17500 JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); 17501 goto fail; 17502 } 17503 } 17504 } else { 17505 /* parent_class is JS_UNDEFINED in this case */ 17506 parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); 17507 parent_class = JS_DupValue(ctx, ctx->function_proto); 17508 } 17509 proto = JS_NewObjectProto(ctx, parent_proto); 17510 if (JS_IsException(proto)) 17511 goto fail; 17512 17513 b = JS_VALUE_GET_PTR(bfunc); 17514 assert(b->func_kind == JS_FUNC_NORMAL); 17515 ctor = JS_NewObjectProtoClass(ctx, parent_class, 17516 JS_CLASS_BYTECODE_FUNCTION); 17517 if (JS_IsException(ctor)) 17518 goto fail; 17519 ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf, FALSE, NULL); 17520 bfunc = JS_UNDEFINED; 17521 if (JS_IsException(ctor)) 17522 goto fail; 17523 js_method_set_home_object(ctx, ctor, proto); 17524 JS_SetConstructorBit(ctx, ctor, TRUE); 17525 17526 JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, 17527 JS_NewInt32(ctx, b->defined_arg_count), 17528 JS_PROP_CONFIGURABLE); 17529 17530 if (is_computed_name) { 17531 if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], 17532 JS_PROP_CONFIGURABLE) < 0) 17533 goto fail; 17534 } else { 17535 if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) 17536 goto fail; 17537 } 17538 17539 /* the constructor property must be first. It can be overriden by 17540 computed property names */ 17541 if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, 17542 JS_DupValue(ctx, ctor), 17543 JS_PROP_CONFIGURABLE | 17544 JS_PROP_WRITABLE | JS_PROP_THROW) < 0) 17545 goto fail; 17546 /* set the prototype property */ 17547 if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, 17548 JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) 17549 goto fail; 17550 set_cycle_flag(ctx, ctor); 17551 set_cycle_flag(ctx, proto); 17552 17553 JS_FreeValue(ctx, parent_proto); 17554 JS_FreeValue(ctx, parent_class); 17555 17556 sp[-2] = ctor; 17557 sp[-1] = proto; 17558 return 0; 17559 fail: 17560 JS_FreeValue(ctx, parent_class); 17561 JS_FreeValue(ctx, parent_proto); 17562 JS_FreeValue(ctx, bfunc); 17563 JS_FreeValue(ctx, proto); 17564 JS_FreeValue(ctx, ctor); 17565 sp[-2] = JS_UNDEFINED; 17566 sp[-1] = JS_UNDEFINED; 17567 return -1; 17568 } 17569 17570 static void close_var_ref(JSRuntime *rt, JSStackFrame *sf, JSVarRef *var_ref) 17571 { 17572 if (sf->js_mode & JS_MODE_ASYNC) { 17573 JSAsyncFunctionState *async_func = container_of(sf, JSAsyncFunctionState, frame); 17574 async_func_free(rt, async_func); 17575 } 17576 var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue); 17577 var_ref->pvalue = &var_ref->value; 17578 /* the reference is no longer to a local variable */ 17579 var_ref->is_detached = TRUE; 17580 } 17581 17582 static void close_var_refs(JSRuntime *rt, JSFunctionBytecode *b, JSStackFrame *sf) 17583 { 17584 JSVarRef *var_ref; 17585 int i; 17586 17587 for(i = 0; i < b->var_ref_count; i++) { 17588 var_ref = sf->var_refs[i]; 17589 if (var_ref) 17590 close_var_ref(rt, sf, var_ref); 17591 } 17592 } 17593 17594 static void close_lexical_var(JSContext *ctx, JSFunctionBytecode *b, 17595 JSStackFrame *sf, int var_idx) 17596 { 17597 JSVarRef *var_ref; 17598 int var_ref_idx; 17599 17600 var_ref_idx = b->vardefs[b->arg_count + var_idx].var_ref_idx; 17601 var_ref = sf->var_refs[var_ref_idx]; 17602 if (var_ref) { 17603 close_var_ref(ctx->rt, sf, var_ref); 17604 sf->var_refs[var_ref_idx] = NULL; 17605 } 17606 } 17607 17608 #define JS_CALL_FLAG_COPY_ARGV (1 << 1) 17609 #define JS_CALL_FLAG_GENERATOR (1 << 2) 17610 17611 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, 17612 JSValueConst this_obj, 17613 int argc, JSValueConst *argv, int flags) 17614 { 17615 JSRuntime *rt = ctx->rt; 17616 JSCFunctionType func; 17617 JSObject *p; 17618 JSStackFrame sf_s, *sf = &sf_s, *prev_sf; 17619 JSValue ret_val; 17620 JSValueConst *arg_buf; 17621 int arg_count, i; 17622 JSCFunctionEnum cproto; 17623 17624 p = JS_VALUE_GET_OBJ(func_obj); 17625 cproto = p->u.cfunc.cproto; 17626 arg_count = p->u.cfunc.length; 17627 17628 /* better to always check stack overflow */ 17629 if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) 17630 return JS_ThrowStackOverflow(ctx); 17631 17632 prev_sf = rt->current_stack_frame; 17633 sf->prev_frame = prev_sf; 17634 rt->current_stack_frame = sf; 17635 ctx = p->u.cfunc.realm; /* change the current realm */ 17636 sf->js_mode = 0; 17637 sf->cur_func = (JSValue)func_obj; 17638 sf->arg_count = argc; 17639 arg_buf = argv; 17640 17641 if (unlikely(argc < arg_count)) { 17642 /* ensure that at least argc_count arguments are readable */ 17643 arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); 17644 for(i = 0; i < argc; i++) 17645 arg_buf[i] = argv[i]; 17646 for(i = argc; i < arg_count; i++) 17647 arg_buf[i] = JS_UNDEFINED; 17648 sf->arg_count = arg_count; 17649 } 17650 sf->arg_buf = (JSValue*)arg_buf; 17651 17652 func = p->u.cfunc.c_function; 17653 switch(cproto) { 17654 case JS_CFUNC_constructor: 17655 case JS_CFUNC_constructor_or_func: 17656 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { 17657 if (cproto == JS_CFUNC_constructor) { 17658 not_a_constructor: 17659 ret_val = JS_ThrowTypeError(ctx, "must be called with new"); 17660 break; 17661 } else { 17662 this_obj = JS_UNDEFINED; 17663 } 17664 } 17665 /* here this_obj is new_target */ 17666 /* fall thru */ 17667 case JS_CFUNC_generic: 17668 ret_val = func.generic(ctx, this_obj, argc, arg_buf); 17669 break; 17670 case JS_CFUNC_constructor_magic: 17671 case JS_CFUNC_constructor_or_func_magic: 17672 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { 17673 if (cproto == JS_CFUNC_constructor_magic) { 17674 goto not_a_constructor; 17675 } else { 17676 this_obj = JS_UNDEFINED; 17677 } 17678 } 17679 /* fall thru */ 17680 case JS_CFUNC_generic_magic: 17681 ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, 17682 p->u.cfunc.magic); 17683 break; 17684 case JS_CFUNC_getter: 17685 ret_val = func.getter(ctx, this_obj); 17686 break; 17687 case JS_CFUNC_setter: 17688 ret_val = func.setter(ctx, this_obj, arg_buf[0]); 17689 break; 17690 case JS_CFUNC_getter_magic: 17691 ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); 17692 break; 17693 case JS_CFUNC_setter_magic: 17694 ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); 17695 break; 17696 case JS_CFUNC_f_f: 17697 { 17698 double d1; 17699 17700 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { 17701 ret_val = JS_EXCEPTION; 17702 break; 17703 } 17704 ret_val = JS_NewFloat64(ctx, func.f_f(d1)); 17705 } 17706 break; 17707 case JS_CFUNC_f_f_f: 17708 { 17709 double d1, d2; 17710 17711 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { 17712 ret_val = JS_EXCEPTION; 17713 break; 17714 } 17715 if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { 17716 ret_val = JS_EXCEPTION; 17717 break; 17718 } 17719 ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); 17720 } 17721 break; 17722 case JS_CFUNC_iterator_next: 17723 { 17724 int done; 17725 ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, 17726 &done, p->u.cfunc.magic); 17727 if (!JS_IsException(ret_val) && done != 2) { 17728 ret_val = js_create_iterator_result(ctx, ret_val, done); 17729 } 17730 } 17731 break; 17732 default: 17733 abort(); 17734 } 17735 17736 rt->current_stack_frame = sf->prev_frame; 17737 return ret_val; 17738 } 17739 17740 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, 17741 JSValueConst this_obj, 17742 int argc, JSValueConst *argv, int flags) 17743 { 17744 JSObject *p; 17745 JSBoundFunction *bf; 17746 JSValueConst *arg_buf, new_target; 17747 int arg_count, i; 17748 17749 p = JS_VALUE_GET_OBJ(func_obj); 17750 bf = p->u.bound_function; 17751 arg_count = bf->argc + argc; 17752 if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) 17753 return JS_ThrowStackOverflow(ctx); 17754 arg_buf = alloca(sizeof(JSValue) * arg_count); 17755 for(i = 0; i < bf->argc; i++) { 17756 arg_buf[i] = bf->argv[i]; 17757 } 17758 for(i = 0; i < argc; i++) { 17759 arg_buf[bf->argc + i] = argv[i]; 17760 } 17761 if (flags & JS_CALL_FLAG_CONSTRUCTOR) { 17762 new_target = this_obj; 17763 if (js_same_value(ctx, func_obj, new_target)) 17764 new_target = bf->func_obj; 17765 return JS_CallConstructor2(ctx, bf->func_obj, new_target, 17766 arg_count, arg_buf); 17767 } else { 17768 return JS_Call(ctx, bf->func_obj, bf->this_val, 17769 arg_count, arg_buf); 17770 } 17771 } 17772 17773 /* argument of OP_special_object */ 17774 typedef enum { 17775 OP_SPECIAL_OBJECT_ARGUMENTS, 17776 OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, 17777 OP_SPECIAL_OBJECT_THIS_FUNC, 17778 OP_SPECIAL_OBJECT_NEW_TARGET, 17779 OP_SPECIAL_OBJECT_HOME_OBJECT, 17780 OP_SPECIAL_OBJECT_VAR_OBJECT, 17781 OP_SPECIAL_OBJECT_IMPORT_META, 17782 } OPSpecialObjectEnum; 17783 17784 #define FUNC_RET_AWAIT 0 17785 #define FUNC_RET_YIELD 1 17786 #define FUNC_RET_YIELD_STAR 2 17787 #define FUNC_RET_INITIAL_YIELD 3 17788 17789 #ifdef OPCODE_ASM_LABEL 17790 #pragma GCC diagnostic push 17791 #pragma GCC diagnostic ignored "-Wunused-label" 17792 #endif 17793 17794 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ 17795 static JSValue __JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 17796 JSValueConst this_obj, JSValueConst new_target, 17797 int argc, JSValue *argv, int flags) 17798 { 17799 JSRuntime *rt = caller_ctx->rt; 17800 JSContext *ctx; 17801 JSObject *p; 17802 JSFunctionBytecode *b; 17803 JSStackFrame sf_s, *sf = &sf_s; 17804 const uint8_t *pc; 17805 int opcode, arg_allocated_size, i; 17806 JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; 17807 JSVarRef **var_refs; 17808 size_t alloca_size; 17809 17810 #if !DIRECT_DISPATCH 17811 #define SWITCH(pc) switch (opcode = *pc++) 17812 #define CASE(op) case op 17813 #define DEFAULT default 17814 #define BREAK break 17815 #else 17816 static const void * const dispatch_table[256] = { 17817 #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, 17818 #if SHORT_OPCODES 17819 #define def(id, size, n_pop, n_push, f) 17820 #else 17821 #define def(id, size, n_pop, n_push, f) && case_default, 17822 #endif 17823 #include "quickjs-opcode.h" 17824 [ OP_COUNT ... 255 ] = &&case_default 17825 }; 17826 #define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; 17827 #ifdef OPCODE_ASM_LABEL 17828 #define CASE(op) case_ ## op: asm volatile("label_" #op ":\n.globl label_" #op); dummy_case_ ## op 17829 #else 17830 #define CASE(op) case_ ## op 17831 #endif 17832 #define DEFAULT case_default 17833 #define BREAK SWITCH(pc) 17834 #endif 17835 17836 if (js_poll_interrupts(caller_ctx)) 17837 return JS_EXCEPTION; 17838 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { 17839 if (flags & JS_CALL_FLAG_GENERATOR) { 17840 JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); 17841 /* func_obj get contains a pointer to JSFuncAsyncState */ 17842 /* the stack frame is already allocated */ 17843 sf = &s->frame; 17844 p = JS_VALUE_GET_OBJ(sf->cur_func); 17845 b = p->u.func.function_bytecode; 17846 ctx = b->realm; 17847 var_refs = p->u.func.var_refs; 17848 local_buf = arg_buf = sf->arg_buf; 17849 var_buf = sf->var_buf; 17850 stack_buf = sf->var_buf + b->var_count; 17851 sp = sf->cur_sp; 17852 sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ 17853 pc = sf->cur_pc; 17854 sf->prev_frame = rt->current_stack_frame; 17855 rt->current_stack_frame = sf; 17856 if (s->throw_flag) 17857 goto exception; 17858 else 17859 goto restart; 17860 } else { 17861 goto not_a_function; 17862 } 17863 } 17864 p = JS_VALUE_GET_OBJ(func_obj); 17865 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { 17866 JSClassCall *call_func; 17867 call_func = rt->class_array[p->class_id].call; 17868 if (!call_func) { 17869 not_a_function: 17870 return JS_ThrowTypeError(caller_ctx, "not a function"); 17871 } 17872 return call_func(caller_ctx, func_obj, this_obj, argc, 17873 (JSValueConst *)argv, flags); 17874 } 17875 b = p->u.func.function_bytecode; 17876 17877 if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { 17878 arg_allocated_size = b->arg_count; 17879 } else { 17880 arg_allocated_size = 0; 17881 } 17882 17883 alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + 17884 b->stack_size) + 17885 sizeof(JSVarRef *) * b->var_ref_count; 17886 if (js_check_stack_overflow(rt, alloca_size)) 17887 return JS_ThrowStackOverflow(caller_ctx); 17888 17889 sf->js_mode = b->js_mode; 17890 arg_buf = argv; 17891 sf->arg_count = argc; 17892 sf->cur_func = (JSValue)func_obj; 17893 var_refs = p->u.func.var_refs; 17894 17895 local_buf = alloca(alloca_size); 17896 if (unlikely(arg_allocated_size)) { 17897 int n = min_int(argc, b->arg_count); 17898 arg_buf = local_buf; 17899 for(i = 0; i < n; i++) 17900 arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); 17901 for(; i < b->arg_count; i++) 17902 arg_buf[i] = JS_UNDEFINED; 17903 sf->arg_count = b->arg_count; 17904 } 17905 var_buf = local_buf + arg_allocated_size; 17906 sf->var_buf = var_buf; 17907 sf->arg_buf = arg_buf; 17908 17909 for(i = 0; i < b->var_count; i++) 17910 var_buf[i] = JS_UNDEFINED; 17911 17912 stack_buf = var_buf + b->var_count; 17913 sf->var_refs = (JSVarRef **)(stack_buf + b->stack_size); 17914 for(i = 0; i < b->var_ref_count; i++) 17915 sf->var_refs[i] = NULL; 17916 sp = stack_buf; 17917 pc = b->byte_code_buf; 17918 sf->prev_frame = rt->current_stack_frame; 17919 rt->current_stack_frame = sf; 17920 ctx = b->realm; /* set the current realm */ 17921 17922 restart: 17923 for(;;) { 17924 int call_argc; 17925 JSValue *call_argv; 17926 17927 SWITCH(pc) { 17928 CASE(OP_push_i32): 17929 *sp++ = JS_NewInt32(ctx, get_u32(pc)); 17930 pc += 4; 17931 BREAK; 17932 CASE(OP_push_bigint_i32): 17933 *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc)); 17934 pc += 4; 17935 BREAK; 17936 CASE(OP_push_const): 17937 *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); 17938 pc += 4; 17939 BREAK; 17940 #if SHORT_OPCODES 17941 CASE(OP_push_minus1): 17942 CASE(OP_push_0): 17943 CASE(OP_push_1): 17944 CASE(OP_push_2): 17945 CASE(OP_push_3): 17946 CASE(OP_push_4): 17947 CASE(OP_push_5): 17948 CASE(OP_push_6): 17949 CASE(OP_push_7): 17950 *sp++ = JS_NewInt32(ctx, opcode - OP_push_0); 17951 BREAK; 17952 CASE(OP_push_i8): 17953 *sp++ = JS_NewInt32(ctx, get_i8(pc)); 17954 pc += 1; 17955 BREAK; 17956 CASE(OP_push_i16): 17957 *sp++ = JS_NewInt32(ctx, get_i16(pc)); 17958 pc += 2; 17959 BREAK; 17960 CASE(OP_push_const8): 17961 *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); 17962 BREAK; 17963 CASE(OP_fclosure8): 17964 *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf, FALSE); 17965 if (unlikely(JS_IsException(sp[-1]))) 17966 goto exception; 17967 BREAK; 17968 CASE(OP_push_empty_string): 17969 *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); 17970 BREAK; 17971 #endif 17972 CASE(OP_push_atom_value): 17973 *sp++ = JS_AtomToValue(ctx, get_u32(pc)); 17974 pc += 4; 17975 BREAK; 17976 CASE(OP_undefined): 17977 *sp++ = JS_UNDEFINED; 17978 BREAK; 17979 CASE(OP_null): 17980 *sp++ = JS_NULL; 17981 BREAK; 17982 CASE(OP_push_this): 17983 /* OP_push_this is only called at the start of a function */ 17984 { 17985 JSValue val; 17986 if (!(b->js_mode & JS_MODE_STRICT)) { 17987 uint32_t tag = JS_VALUE_GET_TAG(this_obj); 17988 if (likely(tag == JS_TAG_OBJECT)) 17989 goto normal_this; 17990 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { 17991 val = JS_DupValue(ctx, ctx->global_obj); 17992 } else { 17993 val = JS_ToObject(ctx, this_obj); 17994 if (JS_IsException(val)) 17995 goto exception; 17996 } 17997 } else { 17998 normal_this: 17999 val = JS_DupValue(ctx, this_obj); 18000 } 18001 *sp++ = val; 18002 } 18003 BREAK; 18004 CASE(OP_push_false): 18005 *sp++ = JS_FALSE; 18006 BREAK; 18007 CASE(OP_push_true): 18008 *sp++ = JS_TRUE; 18009 BREAK; 18010 CASE(OP_object): 18011 *sp++ = JS_NewObject(ctx); 18012 if (unlikely(JS_IsException(sp[-1]))) 18013 goto exception; 18014 BREAK; 18015 CASE(OP_special_object): 18016 { 18017 int arg = *pc++; 18018 switch(arg) { 18019 case OP_SPECIAL_OBJECT_ARGUMENTS: 18020 *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); 18021 if (unlikely(JS_IsException(sp[-1]))) 18022 goto exception; 18023 break; 18024 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: 18025 *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, 18026 sf, min_int(argc, b->arg_count)); 18027 if (unlikely(JS_IsException(sp[-1]))) 18028 goto exception; 18029 break; 18030 case OP_SPECIAL_OBJECT_THIS_FUNC: 18031 *sp++ = JS_DupValue(ctx, sf->cur_func); 18032 break; 18033 case OP_SPECIAL_OBJECT_NEW_TARGET: 18034 *sp++ = JS_DupValue(ctx, new_target); 18035 break; 18036 case OP_SPECIAL_OBJECT_HOME_OBJECT: 18037 { 18038 JSObject *p1; 18039 p1 = p->u.func.home_object; 18040 if (unlikely(!p1)) 18041 *sp++ = JS_UNDEFINED; 18042 else 18043 *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 18044 } 18045 break; 18046 case OP_SPECIAL_OBJECT_VAR_OBJECT: 18047 *sp++ = JS_NewObjectProto(ctx, JS_NULL); 18048 if (unlikely(JS_IsException(sp[-1]))) 18049 goto exception; 18050 break; 18051 case OP_SPECIAL_OBJECT_IMPORT_META: 18052 *sp++ = js_import_meta(ctx); 18053 if (unlikely(JS_IsException(sp[-1]))) 18054 goto exception; 18055 break; 18056 default: 18057 abort(); 18058 } 18059 } 18060 BREAK; 18061 CASE(OP_rest): 18062 { 18063 int first = get_u16(pc); 18064 pc += 2; 18065 first = min_int(first, argc); 18066 *sp++ = js_create_array(ctx, argc - first, (JSValueConst *)(argv + first)); 18067 if (unlikely(JS_IsException(sp[-1]))) 18068 goto exception; 18069 } 18070 BREAK; 18071 18072 CASE(OP_drop): 18073 JS_FreeValue(ctx, sp[-1]); 18074 sp--; 18075 BREAK; 18076 CASE(OP_nip): 18077 JS_FreeValue(ctx, sp[-2]); 18078 sp[-2] = sp[-1]; 18079 sp--; 18080 BREAK; 18081 CASE(OP_nip1): /* a b c -> b c */ 18082 JS_FreeValue(ctx, sp[-3]); 18083 sp[-3] = sp[-2]; 18084 sp[-2] = sp[-1]; 18085 sp--; 18086 BREAK; 18087 CASE(OP_dup): 18088 sp[0] = JS_DupValue(ctx, sp[-1]); 18089 sp++; 18090 BREAK; 18091 CASE(OP_dup2): /* a b -> a b a b */ 18092 sp[0] = JS_DupValue(ctx, sp[-2]); 18093 sp[1] = JS_DupValue(ctx, sp[-1]); 18094 sp += 2; 18095 BREAK; 18096 CASE(OP_dup3): /* a b c -> a b c a b c */ 18097 sp[0] = JS_DupValue(ctx, sp[-3]); 18098 sp[1] = JS_DupValue(ctx, sp[-2]); 18099 sp[2] = JS_DupValue(ctx, sp[-1]); 18100 sp += 3; 18101 BREAK; 18102 CASE(OP_dup1): /* a b -> a a b */ 18103 sp[0] = sp[-1]; 18104 sp[-1] = JS_DupValue(ctx, sp[-2]); 18105 sp++; 18106 BREAK; 18107 CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ 18108 sp[0] = sp[-1]; 18109 sp[-1] = sp[-2]; 18110 sp[-2] = JS_DupValue(ctx, sp[0]); 18111 sp++; 18112 BREAK; 18113 CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ 18114 sp[0] = sp[-1]; 18115 sp[-1] = sp[-2]; 18116 sp[-2] = sp[-3]; 18117 sp[-3] = JS_DupValue(ctx, sp[0]); 18118 sp++; 18119 BREAK; 18120 CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ 18121 sp[0] = sp[-1]; 18122 sp[-1] = sp[-2]; 18123 sp[-2] = sp[-3]; 18124 sp[-3] = sp[-4]; 18125 sp[-4] = JS_DupValue(ctx, sp[0]); 18126 sp++; 18127 BREAK; 18128 CASE(OP_perm3): /* obj a b -> a obj b (213) */ 18129 { 18130 JSValue tmp; 18131 tmp = sp[-2]; 18132 sp[-2] = sp[-3]; 18133 sp[-3] = tmp; 18134 } 18135 BREAK; 18136 CASE(OP_rot3l): /* x a b -> a b x (231) */ 18137 { 18138 JSValue tmp; 18139 tmp = sp[-3]; 18140 sp[-3] = sp[-2]; 18141 sp[-2] = sp[-1]; 18142 sp[-1] = tmp; 18143 } 18144 BREAK; 18145 CASE(OP_rot4l): /* x a b c -> a b c x */ 18146 { 18147 JSValue tmp; 18148 tmp = sp[-4]; 18149 sp[-4] = sp[-3]; 18150 sp[-3] = sp[-2]; 18151 sp[-2] = sp[-1]; 18152 sp[-1] = tmp; 18153 } 18154 BREAK; 18155 CASE(OP_rot5l): /* x a b c d -> a b c d x */ 18156 { 18157 JSValue tmp; 18158 tmp = sp[-5]; 18159 sp[-5] = sp[-4]; 18160 sp[-4] = sp[-3]; 18161 sp[-3] = sp[-2]; 18162 sp[-2] = sp[-1]; 18163 sp[-1] = tmp; 18164 } 18165 BREAK; 18166 CASE(OP_rot3r): /* a b x -> x a b (312) */ 18167 { 18168 JSValue tmp; 18169 tmp = sp[-1]; 18170 sp[-1] = sp[-2]; 18171 sp[-2] = sp[-3]; 18172 sp[-3] = tmp; 18173 } 18174 BREAK; 18175 CASE(OP_perm4): /* obj prop a b -> a obj prop b */ 18176 { 18177 JSValue tmp; 18178 tmp = sp[-2]; 18179 sp[-2] = sp[-3]; 18180 sp[-3] = sp[-4]; 18181 sp[-4] = tmp; 18182 } 18183 BREAK; 18184 CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ 18185 { 18186 JSValue tmp; 18187 tmp = sp[-2]; 18188 sp[-2] = sp[-3]; 18189 sp[-3] = sp[-4]; 18190 sp[-4] = sp[-5]; 18191 sp[-5] = tmp; 18192 } 18193 BREAK; 18194 CASE(OP_swap): /* a b -> b a */ 18195 { 18196 JSValue tmp; 18197 tmp = sp[-2]; 18198 sp[-2] = sp[-1]; 18199 sp[-1] = tmp; 18200 } 18201 BREAK; 18202 CASE(OP_swap2): /* a b c d -> c d a b */ 18203 { 18204 JSValue tmp1, tmp2; 18205 tmp1 = sp[-4]; 18206 tmp2 = sp[-3]; 18207 sp[-4] = sp[-2]; 18208 sp[-3] = sp[-1]; 18209 sp[-2] = tmp1; 18210 sp[-1] = tmp2; 18211 } 18212 BREAK; 18213 18214 CASE(OP_fclosure): 18215 { 18216 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); 18217 pc += 4; 18218 *sp++ = js_closure(ctx, bfunc, var_refs, sf, FALSE); 18219 if (unlikely(JS_IsException(sp[-1]))) 18220 goto exception; 18221 } 18222 BREAK; 18223 #if SHORT_OPCODES 18224 CASE(OP_call0): 18225 CASE(OP_call1): 18226 CASE(OP_call2): 18227 CASE(OP_call3): 18228 call_argc = opcode - OP_call0; 18229 goto has_call_argc; 18230 #endif 18231 CASE(OP_call): 18232 CASE(OP_tail_call): 18233 { 18234 call_argc = get_u16(pc); 18235 pc += 2; 18236 goto has_call_argc; 18237 has_call_argc: 18238 call_argv = sp - call_argc; 18239 sf->cur_pc = pc; 18240 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, 18241 JS_UNDEFINED, call_argc, call_argv, 0); 18242 if (unlikely(JS_IsException(ret_val))) 18243 goto exception; 18244 if (opcode == OP_tail_call) 18245 goto done; 18246 for(i = -1; i < call_argc; i++) 18247 JS_FreeValue(ctx, call_argv[i]); 18248 sp -= call_argc + 1; 18249 *sp++ = ret_val; 18250 } 18251 BREAK; 18252 CASE(OP_call_constructor): 18253 { 18254 call_argc = get_u16(pc); 18255 pc += 2; 18256 call_argv = sp - call_argc; 18257 sf->cur_pc = pc; 18258 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], 18259 call_argv[-1], 18260 call_argc, call_argv, 0); 18261 if (unlikely(JS_IsException(ret_val))) 18262 goto exception; 18263 for(i = -2; i < call_argc; i++) 18264 JS_FreeValue(ctx, call_argv[i]); 18265 sp -= call_argc + 2; 18266 *sp++ = ret_val; 18267 } 18268 BREAK; 18269 CASE(OP_call_method): 18270 CASE(OP_tail_call_method): 18271 { 18272 call_argc = get_u16(pc); 18273 pc += 2; 18274 call_argv = sp - call_argc; 18275 sf->cur_pc = pc; 18276 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], 18277 JS_UNDEFINED, call_argc, call_argv, 0); 18278 if (unlikely(JS_IsException(ret_val))) 18279 goto exception; 18280 if (opcode == OP_tail_call_method) 18281 goto done; 18282 for(i = -2; i < call_argc; i++) 18283 JS_FreeValue(ctx, call_argv[i]); 18284 sp -= call_argc + 2; 18285 *sp++ = ret_val; 18286 } 18287 BREAK; 18288 CASE(OP_array_from): 18289 call_argc = get_u16(pc); 18290 pc += 2; 18291 ret_val = js_create_array_free(ctx, call_argc, sp - call_argc); 18292 sp -= call_argc; 18293 if (unlikely(JS_IsException(ret_val))) 18294 goto exception; 18295 *sp++ = ret_val; 18296 BREAK; 18297 18298 CASE(OP_apply): 18299 { 18300 int magic; 18301 magic = get_u16(pc); 18302 pc += 2; 18303 sf->cur_pc = pc; 18304 18305 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); 18306 if (unlikely(JS_IsException(ret_val))) 18307 goto exception; 18308 JS_FreeValue(ctx, sp[-3]); 18309 JS_FreeValue(ctx, sp[-2]); 18310 JS_FreeValue(ctx, sp[-1]); 18311 sp -= 3; 18312 *sp++ = ret_val; 18313 } 18314 BREAK; 18315 CASE(OP_return): 18316 ret_val = *--sp; 18317 goto done; 18318 CASE(OP_return_undef): 18319 ret_val = JS_UNDEFINED; 18320 goto done; 18321 18322 CASE(OP_check_ctor_return): 18323 /* return TRUE if 'this' should be returned */ 18324 if (!JS_IsObject(sp[-1])) { 18325 if (!JS_IsUndefined(sp[-1])) { 18326 JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); 18327 goto exception; 18328 } 18329 sp[0] = JS_TRUE; 18330 } else { 18331 sp[0] = JS_FALSE; 18332 } 18333 sp++; 18334 BREAK; 18335 CASE(OP_check_ctor): 18336 if (JS_IsUndefined(new_target)) { 18337 non_ctor_call: 18338 JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); 18339 goto exception; 18340 } 18341 BREAK; 18342 CASE(OP_init_ctor): 18343 { 18344 JSValue super, ret; 18345 sf->cur_pc = pc; 18346 if (JS_IsUndefined(new_target)) 18347 goto non_ctor_call; 18348 super = JS_GetPrototype(ctx, func_obj); 18349 if (JS_IsException(super)) 18350 goto exception; 18351 ret = JS_CallConstructor2(ctx, super, new_target, argc, (JSValueConst *)argv); 18352 JS_FreeValue(ctx, super); 18353 if (JS_IsException(ret)) 18354 goto exception; 18355 *sp++ = ret; 18356 } 18357 BREAK; 18358 CASE(OP_check_brand): 18359 { 18360 int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); 18361 if (ret < 0) 18362 goto exception; 18363 if (!ret) { 18364 JS_ThrowTypeError(ctx, "invalid brand on object"); 18365 goto exception; 18366 } 18367 } 18368 BREAK; 18369 CASE(OP_add_brand): 18370 if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) 18371 goto exception; 18372 JS_FreeValue(ctx, sp[-2]); 18373 JS_FreeValue(ctx, sp[-1]); 18374 sp -= 2; 18375 BREAK; 18376 18377 CASE(OP_throw): 18378 JS_Throw(ctx, *--sp); 18379 goto exception; 18380 18381 CASE(OP_throw_error): 18382 #define JS_THROW_VAR_RO 0 18383 #define JS_THROW_VAR_REDECL 1 18384 #define JS_THROW_VAR_UNINITIALIZED 2 18385 #define JS_THROW_ERROR_DELETE_SUPER 3 18386 #define JS_THROW_ERROR_ITERATOR_THROW 4 18387 { 18388 JSAtom atom; 18389 int type; 18390 atom = get_u32(pc); 18391 type = pc[4]; 18392 pc += 5; 18393 if (type == JS_THROW_VAR_RO) 18394 JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); 18395 else 18396 if (type == JS_THROW_VAR_REDECL) 18397 JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); 18398 else 18399 if (type == JS_THROW_VAR_UNINITIALIZED) 18400 JS_ThrowReferenceErrorUninitialized(ctx, atom); 18401 else 18402 if (type == JS_THROW_ERROR_DELETE_SUPER) 18403 JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); 18404 else 18405 if (type == JS_THROW_ERROR_ITERATOR_THROW) 18406 JS_ThrowTypeError(ctx, "iterator does not have a throw method"); 18407 else 18408 JS_ThrowInternalError(ctx, "invalid throw var type %d", type); 18409 } 18410 goto exception; 18411 18412 CASE(OP_eval): 18413 { 18414 JSValueConst obj; 18415 int scope_idx; 18416 call_argc = get_u16(pc); 18417 scope_idx = get_u16(pc + 2) + ARG_SCOPE_END; 18418 pc += 4; 18419 call_argv = sp - call_argc; 18420 sf->cur_pc = pc; 18421 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { 18422 if (call_argc >= 1) 18423 obj = call_argv[0]; 18424 else 18425 obj = JS_UNDEFINED; 18426 ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, 18427 JS_EVAL_TYPE_DIRECT, scope_idx); 18428 } else { 18429 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, 18430 JS_UNDEFINED, call_argc, call_argv, 0); 18431 } 18432 if (unlikely(JS_IsException(ret_val))) 18433 goto exception; 18434 for(i = -1; i < call_argc; i++) 18435 JS_FreeValue(ctx, call_argv[i]); 18436 sp -= call_argc + 1; 18437 *sp++ = ret_val; 18438 } 18439 BREAK; 18440 /* could merge with OP_apply */ 18441 CASE(OP_apply_eval): 18442 { 18443 int scope_idx; 18444 uint32_t len; 18445 JSValue *tab; 18446 JSValueConst obj; 18447 18448 scope_idx = get_u16(pc) + ARG_SCOPE_END; 18449 pc += 2; 18450 sf->cur_pc = pc; 18451 tab = build_arg_list(ctx, &len, sp[-1]); 18452 if (!tab) 18453 goto exception; 18454 if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { 18455 if (len >= 1) 18456 obj = tab[0]; 18457 else 18458 obj = JS_UNDEFINED; 18459 ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, 18460 JS_EVAL_TYPE_DIRECT, scope_idx); 18461 } else { 18462 ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, 18463 (JSValueConst *)tab); 18464 } 18465 free_arg_list(ctx, tab, len); 18466 if (unlikely(JS_IsException(ret_val))) 18467 goto exception; 18468 JS_FreeValue(ctx, sp[-2]); 18469 JS_FreeValue(ctx, sp[-1]); 18470 sp -= 2; 18471 *sp++ = ret_val; 18472 } 18473 BREAK; 18474 18475 CASE(OP_regexp): 18476 { 18477 sp[-2] = JS_NewRegexp(ctx, sp[-2], sp[-1]); 18478 sp--; 18479 if (JS_IsException(sp[-1])) 18480 goto exception; 18481 } 18482 BREAK; 18483 18484 CASE(OP_get_super): 18485 { 18486 JSValue proto; 18487 sf->cur_pc = pc; 18488 proto = JS_GetPrototype(ctx, sp[-1]); 18489 if (JS_IsException(proto)) 18490 goto exception; 18491 JS_FreeValue(ctx, sp[-1]); 18492 sp[-1] = proto; 18493 } 18494 BREAK; 18495 18496 CASE(OP_import): 18497 { 18498 JSValue val; 18499 sf->cur_pc = pc; 18500 val = js_dynamic_import(ctx, sp[-2], sp[-1]); 18501 if (JS_IsException(val)) 18502 goto exception; 18503 JS_FreeValue(ctx, sp[-2]); 18504 JS_FreeValue(ctx, sp[-1]); 18505 sp--; 18506 sp[-1] = val; 18507 } 18508 BREAK; 18509 18510 CASE(OP_get_var_undef): 18511 CASE(OP_get_var): 18512 { 18513 int idx; 18514 JSValue val; 18515 idx = get_u16(pc); 18516 pc += 2; 18517 val = *var_refs[idx]->pvalue; 18518 if (unlikely(JS_IsUninitialized(val))) { 18519 JSClosureVar *cv = &b->closure_var[idx]; 18520 if (cv->is_lexical) { 18521 JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name); 18522 goto exception; 18523 } else { 18524 sf->cur_pc = pc; 18525 sp[0] = JS_GetPropertyInternal(ctx, ctx->global_obj, 18526 cv->var_name, 18527 ctx->global_obj, 18528 opcode - OP_get_var_undef); 18529 if (JS_IsException(sp[0])) 18530 goto exception; 18531 } 18532 } else { 18533 sp[0] = JS_DupValue(ctx, val); 18534 } 18535 sp++; 18536 } 18537 BREAK; 18538 18539 CASE(OP_put_var): 18540 CASE(OP_put_var_init): 18541 { 18542 int idx, ret; 18543 JSVarRef *var_ref; 18544 idx = get_u16(pc); 18545 pc += 2; 18546 var_ref = var_refs[idx]; 18547 if (unlikely(JS_IsUninitialized(*var_ref->pvalue) || 18548 var_ref->is_const)) { 18549 JSClosureVar *cv = &b->closure_var[idx]; 18550 if (var_ref->is_lexical) { 18551 if (opcode == OP_put_var_init) 18552 goto put_var_ok; 18553 if (JS_IsUninitialized(*var_ref->pvalue)) 18554 JS_ThrowReferenceErrorUninitialized(ctx, cv->var_name); 18555 else 18556 JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, cv->var_name); 18557 goto exception; 18558 } else { 18559 sf->cur_pc = pc; 18560 ret = JS_HasProperty(ctx, ctx->global_obj, cv->var_name); 18561 if (ret < 0) 18562 goto exception; 18563 if (ret == 0 && is_strict_mode(ctx)) { 18564 JS_ThrowReferenceErrorNotDefined(ctx, cv->var_name); 18565 goto exception; 18566 } 18567 ret = JS_SetPropertyInternal(ctx, ctx->global_obj, cv->var_name, sp[-1], 18568 ctx->global_obj, JS_PROP_THROW_STRICT); 18569 sp--; 18570 if (ret < 0) 18571 goto exception; 18572 } 18573 } else { 18574 put_var_ok: 18575 set_value(ctx, var_ref->pvalue, sp[-1]); 18576 sp--; 18577 } 18578 } 18579 BREAK; 18580 CASE(OP_get_loc): 18581 { 18582 int idx; 18583 idx = get_u16(pc); 18584 pc += 2; 18585 sp[0] = JS_DupValue(ctx, var_buf[idx]); 18586 sp++; 18587 } 18588 BREAK; 18589 CASE(OP_put_loc): 18590 { 18591 int idx; 18592 idx = get_u16(pc); 18593 pc += 2; 18594 set_value(ctx, &var_buf[idx], sp[-1]); 18595 sp--; 18596 } 18597 BREAK; 18598 CASE(OP_set_loc): 18599 { 18600 int idx; 18601 idx = get_u16(pc); 18602 pc += 2; 18603 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); 18604 } 18605 BREAK; 18606 CASE(OP_get_arg): 18607 { 18608 int idx; 18609 idx = get_u16(pc); 18610 pc += 2; 18611 sp[0] = JS_DupValue(ctx, arg_buf[idx]); 18612 sp++; 18613 } 18614 BREAK; 18615 CASE(OP_put_arg): 18616 { 18617 int idx; 18618 idx = get_u16(pc); 18619 pc += 2; 18620 set_value(ctx, &arg_buf[idx], sp[-1]); 18621 sp--; 18622 } 18623 BREAK; 18624 CASE(OP_set_arg): 18625 { 18626 int idx; 18627 idx = get_u16(pc); 18628 pc += 2; 18629 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); 18630 } 18631 BREAK; 18632 18633 #if SHORT_OPCODES 18634 CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; 18635 CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; 18636 CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; 18637 18638 CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; 18639 CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; 18640 CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; 18641 CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; 18642 CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; 18643 CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; 18644 CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; 18645 CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; 18646 CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; 18647 CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; 18648 CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; 18649 CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; 18650 CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; 18651 CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; 18652 CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; 18653 CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; 18654 CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; 18655 CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; 18656 CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; 18657 CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; 18658 CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; 18659 CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; 18660 CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; 18661 CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; 18662 CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; 18663 CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; 18664 CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; 18665 CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; 18666 CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; 18667 CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; 18668 CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; 18669 CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; 18670 CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 18671 CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 18672 CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 18673 CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 18674 #endif 18675 18676 CASE(OP_get_var_ref): 18677 { 18678 int idx; 18679 JSValue val; 18680 idx = get_u16(pc); 18681 pc += 2; 18682 val = *var_refs[idx]->pvalue; 18683 sp[0] = JS_DupValue(ctx, val); 18684 sp++; 18685 } 18686 BREAK; 18687 CASE(OP_put_var_ref): 18688 { 18689 int idx; 18690 idx = get_u16(pc); 18691 pc += 2; 18692 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 18693 sp--; 18694 } 18695 BREAK; 18696 CASE(OP_set_var_ref): 18697 { 18698 int idx; 18699 idx = get_u16(pc); 18700 pc += 2; 18701 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); 18702 } 18703 BREAK; 18704 CASE(OP_get_var_ref_check): 18705 { 18706 int idx; 18707 JSValue val; 18708 idx = get_u16(pc); 18709 pc += 2; 18710 val = *var_refs[idx]->pvalue; 18711 if (unlikely(JS_IsUninitialized(val))) { 18712 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 18713 goto exception; 18714 } 18715 sp[0] = JS_DupValue(ctx, val); 18716 sp++; 18717 } 18718 BREAK; 18719 CASE(OP_put_var_ref_check): 18720 { 18721 int idx; 18722 idx = get_u16(pc); 18723 pc += 2; 18724 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { 18725 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 18726 goto exception; 18727 } 18728 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 18729 sp--; 18730 } 18731 BREAK; 18732 CASE(OP_put_var_ref_check_init): 18733 { 18734 int idx; 18735 idx = get_u16(pc); 18736 pc += 2; 18737 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { 18738 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 18739 goto exception; 18740 } 18741 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 18742 sp--; 18743 } 18744 BREAK; 18745 CASE(OP_set_loc_uninitialized): 18746 { 18747 int idx; 18748 idx = get_u16(pc); 18749 pc += 2; 18750 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); 18751 } 18752 BREAK; 18753 CASE(OP_get_loc_check): 18754 { 18755 int idx; 18756 idx = get_u16(pc); 18757 pc += 2; 18758 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 18759 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); 18760 goto exception; 18761 } 18762 sp[0] = JS_DupValue(ctx, var_buf[idx]); 18763 sp++; 18764 } 18765 BREAK; 18766 CASE(OP_get_loc_checkthis): 18767 { 18768 int idx; 18769 idx = get_u16(pc); 18770 pc += 2; 18771 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 18772 JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE); 18773 goto exception; 18774 } 18775 sp[0] = JS_DupValue(ctx, var_buf[idx]); 18776 sp++; 18777 } 18778 BREAK; 18779 CASE(OP_put_loc_check): 18780 { 18781 int idx; 18782 idx = get_u16(pc); 18783 pc += 2; 18784 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 18785 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); 18786 goto exception; 18787 } 18788 set_value(ctx, &var_buf[idx], sp[-1]); 18789 sp--; 18790 } 18791 BREAK; 18792 CASE(OP_set_loc_check): 18793 { 18794 int idx; 18795 idx = get_u16(pc); 18796 pc += 2; 18797 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 18798 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); 18799 goto exception; 18800 } 18801 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); 18802 } 18803 BREAK; 18804 CASE(OP_put_loc_check_init): 18805 { 18806 int idx; 18807 idx = get_u16(pc); 18808 pc += 2; 18809 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { 18810 JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); 18811 goto exception; 18812 } 18813 set_value(ctx, &var_buf[idx], sp[-1]); 18814 sp--; 18815 } 18816 BREAK; 18817 CASE(OP_close_loc): 18818 { 18819 int idx; 18820 idx = get_u16(pc); 18821 pc += 2; 18822 close_lexical_var(ctx, b, sf, idx); 18823 } 18824 BREAK; 18825 18826 CASE(OP_make_loc_ref): 18827 CASE(OP_make_arg_ref): 18828 CASE(OP_make_var_ref_ref): 18829 { 18830 JSVarRef *var_ref; 18831 JSProperty *pr; 18832 JSAtom atom; 18833 int idx; 18834 atom = get_u32(pc); 18835 idx = get_u16(pc + 4); 18836 pc += 6; 18837 *sp++ = JS_NewObjectProto(ctx, JS_NULL); 18838 if (unlikely(JS_IsException(sp[-1]))) 18839 goto exception; 18840 if (opcode == OP_make_var_ref_ref) { 18841 var_ref = var_refs[idx]; 18842 js_rc(var_ref)->ref_count++; 18843 } else { 18844 var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); 18845 if (!var_ref) 18846 goto exception; 18847 } 18848 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, 18849 JS_PROP_WRITABLE | JS_PROP_VARREF); 18850 if (!pr) { 18851 free_var_ref(rt, var_ref); 18852 goto exception; 18853 } 18854 pr->u.var_ref = var_ref; 18855 *sp++ = JS_AtomToValue(ctx, atom); 18856 } 18857 BREAK; 18858 CASE(OP_make_var_ref): 18859 { 18860 JSAtom atom; 18861 atom = get_u32(pc); 18862 pc += 4; 18863 sf->cur_pc = pc; 18864 18865 if (JS_GetGlobalVarRef(ctx, atom, sp)) 18866 goto exception; 18867 sp += 2; 18868 } 18869 BREAK; 18870 18871 CASE(OP_goto): 18872 pc += (int32_t)get_u32(pc); 18873 if (unlikely(js_poll_interrupts(ctx))) 18874 goto exception; 18875 BREAK; 18876 #if SHORT_OPCODES 18877 CASE(OP_goto16): 18878 pc += (int16_t)get_u16(pc); 18879 if (unlikely(js_poll_interrupts(ctx))) 18880 goto exception; 18881 BREAK; 18882 CASE(OP_goto8): 18883 pc += (int8_t)pc[0]; 18884 if (unlikely(js_poll_interrupts(ctx))) 18885 goto exception; 18886 BREAK; 18887 #endif 18888 CASE(OP_if_true): 18889 { 18890 int res; 18891 JSValue op1; 18892 18893 op1 = sp[-1]; 18894 pc += 4; 18895 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 18896 res = JS_VALUE_GET_INT(op1); 18897 } else { 18898 res = JS_ToBoolFree(ctx, op1); 18899 } 18900 sp--; 18901 if (res) { 18902 pc += (int32_t)get_u32(pc - 4) - 4; 18903 } 18904 if (unlikely(js_poll_interrupts(ctx))) 18905 goto exception; 18906 } 18907 BREAK; 18908 CASE(OP_if_false): 18909 { 18910 int res; 18911 JSValue op1; 18912 18913 op1 = sp[-1]; 18914 pc += 4; 18915 /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */ 18916 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 18917 res = JS_VALUE_GET_INT(op1); 18918 } else { 18919 res = JS_ToBoolFree(ctx, op1); 18920 } 18921 sp--; 18922 if (!res) { 18923 pc += (int32_t)get_u32(pc - 4) - 4; 18924 } 18925 if (unlikely(js_poll_interrupts(ctx))) 18926 goto exception; 18927 } 18928 BREAK; 18929 #if SHORT_OPCODES 18930 CASE(OP_if_true8): 18931 { 18932 int res; 18933 JSValue op1; 18934 18935 op1 = sp[-1]; 18936 pc += 1; 18937 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 18938 res = JS_VALUE_GET_INT(op1); 18939 } else { 18940 res = JS_ToBoolFree(ctx, op1); 18941 } 18942 sp--; 18943 if (res) { 18944 pc += (int8_t)pc[-1] - 1; 18945 } 18946 if (unlikely(js_poll_interrupts(ctx))) 18947 goto exception; 18948 } 18949 BREAK; 18950 CASE(OP_if_false8): 18951 { 18952 int res; 18953 JSValue op1; 18954 18955 op1 = sp[-1]; 18956 pc += 1; 18957 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 18958 res = JS_VALUE_GET_INT(op1); 18959 } else { 18960 res = JS_ToBoolFree(ctx, op1); 18961 } 18962 sp--; 18963 if (!res) { 18964 pc += (int8_t)pc[-1] - 1; 18965 } 18966 if (unlikely(js_poll_interrupts(ctx))) 18967 goto exception; 18968 } 18969 BREAK; 18970 #endif 18971 CASE(OP_catch): 18972 { 18973 int32_t diff; 18974 diff = get_u32(pc); 18975 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); 18976 sp++; 18977 pc += 4; 18978 } 18979 BREAK; 18980 CASE(OP_gosub): 18981 { 18982 int32_t diff; 18983 diff = get_u32(pc); 18984 /* XXX: should have a different tag to avoid security flaw */ 18985 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); 18986 sp++; 18987 pc += diff; 18988 } 18989 BREAK; 18990 CASE(OP_ret): 18991 { 18992 JSValue op1; 18993 uint32_t pos; 18994 op1 = sp[-1]; 18995 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) 18996 goto ret_fail; 18997 pos = JS_VALUE_GET_INT(op1); 18998 if (unlikely(pos >= b->byte_code_len)) { 18999 ret_fail: 19000 JS_ThrowInternalError(ctx, "invalid ret value"); 19001 goto exception; 19002 } 19003 sp--; 19004 pc = b->byte_code_buf + pos; 19005 } 19006 BREAK; 19007 19008 CASE(OP_for_in_start): 19009 sf->cur_pc = pc; 19010 if (js_for_in_start(ctx, sp)) 19011 goto exception; 19012 BREAK; 19013 CASE(OP_for_in_next): 19014 sf->cur_pc = pc; 19015 if (js_for_in_next(ctx, sp)) 19016 goto exception; 19017 sp += 2; 19018 BREAK; 19019 CASE(OP_for_of_start): 19020 sf->cur_pc = pc; 19021 if (js_for_of_start(ctx, sp, FALSE)) 19022 goto exception; 19023 sp += 1; 19024 *sp++ = JS_NewCatchOffset(ctx, 0); 19025 BREAK; 19026 CASE(OP_for_of_next): 19027 { 19028 int offset = -3 - pc[0]; 19029 pc += 1; 19030 sf->cur_pc = pc; 19031 if (js_for_of_next(ctx, sp, offset)) 19032 goto exception; 19033 sp += 2; 19034 } 19035 BREAK; 19036 CASE(OP_for_await_of_next): 19037 sf->cur_pc = pc; 19038 if (js_for_await_of_next(ctx, sp)) 19039 goto exception; 19040 sp++; 19041 BREAK; 19042 CASE(OP_for_await_of_start): 19043 sf->cur_pc = pc; 19044 if (js_for_of_start(ctx, sp, TRUE)) 19045 goto exception; 19046 sp += 1; 19047 *sp++ = JS_NewCatchOffset(ctx, 0); 19048 BREAK; 19049 CASE(OP_iterator_get_value_done): 19050 sf->cur_pc = pc; 19051 if (js_iterator_get_value_done(ctx, sp)) 19052 goto exception; 19053 sp += 1; 19054 BREAK; 19055 CASE(OP_iterator_check_object): 19056 if (unlikely(!JS_IsObject(sp[-1]))) { 19057 JS_ThrowTypeError(ctx, "iterator must return an object"); 19058 goto exception; 19059 } 19060 BREAK; 19061 19062 CASE(OP_iterator_close): 19063 /* iter_obj next catch_offset -> */ 19064 sp--; /* drop the catch offset to avoid getting caught by exception */ 19065 JS_FreeValue(ctx, sp[-1]); /* drop the next method */ 19066 sp--; 19067 if (!JS_IsUndefined(sp[-1])) { 19068 sf->cur_pc = pc; 19069 if (JS_IteratorClose(ctx, sp[-1], FALSE)) 19070 goto exception; 19071 JS_FreeValue(ctx, sp[-1]); 19072 } 19073 sp--; 19074 BREAK; 19075 CASE(OP_nip_catch): 19076 { 19077 JSValue ret_val; 19078 /* catch_offset ... ret_val -> ret_eval */ 19079 ret_val = *--sp; 19080 while (sp > stack_buf && 19081 JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { 19082 JS_FreeValue(ctx, *--sp); 19083 } 19084 if (unlikely(sp == stack_buf)) { 19085 JS_ThrowInternalError(ctx, "nip_catch"); 19086 JS_FreeValue(ctx, ret_val); 19087 goto exception; 19088 } 19089 sp[-1] = ret_val; 19090 } 19091 BREAK; 19092 19093 CASE(OP_iterator_next): 19094 /* stack: iter_obj next catch_offset val */ 19095 { 19096 JSValue ret; 19097 sf->cur_pc = pc; 19098 ret = JS_Call(ctx, sp[-3], sp[-4], 19099 1, (JSValueConst *)(sp - 1)); 19100 if (JS_IsException(ret)) 19101 goto exception; 19102 JS_FreeValue(ctx, sp[-1]); 19103 sp[-1] = ret; 19104 } 19105 BREAK; 19106 19107 CASE(OP_iterator_call): 19108 /* stack: iter_obj next catch_offset val */ 19109 { 19110 JSValue method, ret; 19111 BOOL ret_flag; 19112 int flags; 19113 flags = *pc++; 19114 sf->cur_pc = pc; 19115 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? 19116 JS_ATOM_throw : JS_ATOM_return); 19117 if (JS_IsException(method)) 19118 goto exception; 19119 if (JS_IsUndefined(method) || JS_IsNull(method)) { 19120 ret_flag = TRUE; 19121 } else { 19122 if (flags & 2) { 19123 /* no argument */ 19124 ret = JS_CallFree(ctx, method, sp[-4], 19125 0, NULL); 19126 } else { 19127 ret = JS_CallFree(ctx, method, sp[-4], 19128 1, (JSValueConst *)(sp - 1)); 19129 } 19130 if (JS_IsException(ret)) 19131 goto exception; 19132 JS_FreeValue(ctx, sp[-1]); 19133 sp[-1] = ret; 19134 ret_flag = FALSE; 19135 } 19136 sp[0] = JS_NewBool(ctx, ret_flag); 19137 sp += 1; 19138 } 19139 BREAK; 19140 19141 CASE(OP_lnot): 19142 { 19143 int res; 19144 JSValue op1; 19145 19146 op1 = sp[-1]; 19147 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 19148 res = JS_VALUE_GET_INT(op1) != 0; 19149 } else { 19150 res = JS_ToBoolFree(ctx, op1); 19151 } 19152 sp[-1] = JS_NewBool(ctx, !res); 19153 } 19154 BREAK; 19155 19156 #define GET_FIELD_INLINE(name, keep, is_length) \ 19157 { \ 19158 JSValue val, obj; \ 19159 JSAtom atom; \ 19160 JSObject *p; \ 19161 JSProperty *pr; \ 19162 JSShapeProperty *prs; \ 19163 \ 19164 if (is_length) { \ 19165 atom = JS_ATOM_length; \ 19166 } else { \ 19167 atom = get_u32(pc); \ 19168 pc += 4; \ 19169 } \ 19170 \ 19171 obj = sp[-1]; \ 19172 if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { \ 19173 p = JS_VALUE_GET_OBJ(obj); \ 19174 for(;;) { \ 19175 prs = find_own_property(&pr, p, atom); \ 19176 if (prs) { \ 19177 /* found */ \ 19178 if (unlikely(prs->flags & JS_PROP_TMASK)) \ 19179 goto name ## _slow_path; \ 19180 val = JS_DupValue(ctx, pr->u.value); \ 19181 break; \ 19182 } \ 19183 if (unlikely(p->is_exotic)) { \ 19184 /* XXX: should avoid the slow path for arrays \ 19185 and typed arrays by ensuring that 'prop' is \ 19186 not numeric */ \ 19187 obj = JS_MKPTR(JS_TAG_OBJECT, p); \ 19188 goto name ## _slow_path; \ 19189 } \ 19190 p = p->shape->proto; \ 19191 if (!p) { \ 19192 val = JS_UNDEFINED; \ 19193 break; \ 19194 } \ 19195 } \ 19196 } else { \ 19197 name ## _slow_path: \ 19198 sf->cur_pc = pc; \ 19199 val = JS_GetPropertyInternal(ctx, obj, atom, sp[-1], 0); \ 19200 if (unlikely(JS_IsException(val))) \ 19201 goto exception; \ 19202 } \ 19203 if (keep) { \ 19204 *sp++ = val; \ 19205 } else { \ 19206 JS_FreeValue(ctx, sp[-1]); \ 19207 sp[-1] = val; \ 19208 } \ 19209 } 19210 19211 19212 CASE(OP_get_field): 19213 GET_FIELD_INLINE(get_field, 0, 0); 19214 BREAK; 19215 19216 CASE(OP_get_field2): 19217 GET_FIELD_INLINE(get_field2, 1, 0); 19218 BREAK; 19219 19220 #if SHORT_OPCODES 19221 CASE(OP_get_length): 19222 GET_FIELD_INLINE(get_length, 0, 1); 19223 BREAK; 19224 #endif 19225 19226 CASE(OP_put_field): 19227 { 19228 int ret; 19229 JSValue obj; 19230 JSAtom atom; 19231 JSObject *p; 19232 JSProperty *pr; 19233 JSShapeProperty *prs; 19234 19235 atom = get_u32(pc); 19236 pc += 4; 19237 19238 obj = sp[-2]; 19239 if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT)) { 19240 p = JS_VALUE_GET_OBJ(obj); 19241 prs = find_own_property(&pr, p, atom); 19242 if (!prs) 19243 goto put_field_slow_path; 19244 if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | 19245 JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { 19246 /* fast path */ 19247 set_value(ctx, &pr->u.value, sp[-1]); 19248 } else { 19249 goto put_field_slow_path; 19250 } 19251 JS_FreeValue(ctx, obj); 19252 sp -= 2; 19253 } else { 19254 put_field_slow_path: 19255 sf->cur_pc = pc; 19256 ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-1], obj, 19257 JS_PROP_THROW_STRICT); 19258 JS_FreeValue(ctx, obj); 19259 sp -= 2; 19260 if (unlikely(ret < 0)) 19261 goto exception; 19262 } 19263 19264 } 19265 BREAK; 19266 19267 CASE(OP_private_symbol): 19268 { 19269 JSAtom atom; 19270 JSValue val; 19271 19272 atom = get_u32(pc); 19273 pc += 4; 19274 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); 19275 if (JS_IsException(val)) 19276 goto exception; 19277 *sp++ = val; 19278 } 19279 BREAK; 19280 19281 CASE(OP_get_private_field): 19282 { 19283 JSValue val; 19284 19285 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); 19286 JS_FreeValue(ctx, sp[-1]); 19287 JS_FreeValue(ctx, sp[-2]); 19288 sp[-2] = val; 19289 sp--; 19290 if (unlikely(JS_IsException(val))) 19291 goto exception; 19292 } 19293 BREAK; 19294 19295 CASE(OP_put_private_field): 19296 { 19297 int ret; 19298 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); 19299 JS_FreeValue(ctx, sp[-3]); 19300 JS_FreeValue(ctx, sp[-1]); 19301 sp -= 3; 19302 if (unlikely(ret < 0)) 19303 goto exception; 19304 } 19305 BREAK; 19306 19307 CASE(OP_define_private_field): 19308 { 19309 int ret; 19310 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); 19311 JS_FreeValue(ctx, sp[-2]); 19312 sp -= 2; 19313 if (unlikely(ret < 0)) 19314 goto exception; 19315 } 19316 BREAK; 19317 19318 CASE(OP_define_field): 19319 { 19320 int ret; 19321 JSAtom atom; 19322 atom = get_u32(pc); 19323 pc += 4; 19324 19325 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], 19326 JS_PROP_C_W_E | JS_PROP_THROW); 19327 sp--; 19328 if (unlikely(ret < 0)) 19329 goto exception; 19330 } 19331 BREAK; 19332 19333 CASE(OP_set_name): 19334 { 19335 int ret; 19336 JSAtom atom; 19337 atom = get_u32(pc); 19338 pc += 4; 19339 19340 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); 19341 if (unlikely(ret < 0)) 19342 goto exception; 19343 } 19344 BREAK; 19345 CASE(OP_set_name_computed): 19346 { 19347 int ret; 19348 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); 19349 if (unlikely(ret < 0)) 19350 goto exception; 19351 } 19352 BREAK; 19353 CASE(OP_set_proto): 19354 { 19355 JSValue proto; 19356 sf->cur_pc = pc; 19357 proto = sp[-1]; 19358 if (JS_IsObject(proto) || JS_IsNull(proto)) { 19359 if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) 19360 goto exception; 19361 } 19362 JS_FreeValue(ctx, proto); 19363 sp--; 19364 } 19365 BREAK; 19366 CASE(OP_set_home_object): 19367 js_method_set_home_object(ctx, sp[-1], sp[-2]); 19368 BREAK; 19369 CASE(OP_define_method): 19370 CASE(OP_define_method_computed): 19371 { 19372 JSValue getter, setter, value; 19373 JSValueConst obj; 19374 JSAtom atom; 19375 int flags, ret, op_flags; 19376 BOOL is_computed; 19377 #define OP_DEFINE_METHOD_METHOD 0 19378 #define OP_DEFINE_METHOD_GETTER 1 19379 #define OP_DEFINE_METHOD_SETTER 2 19380 #define OP_DEFINE_METHOD_ENUMERABLE 4 19381 19382 is_computed = (opcode == OP_define_method_computed); 19383 if (is_computed) { 19384 atom = JS_ValueToAtom(ctx, sp[-2]); 19385 if (unlikely(atom == JS_ATOM_NULL)) 19386 goto exception; 19387 opcode += OP_define_method - OP_define_method_computed; 19388 } else { 19389 atom = get_u32(pc); 19390 pc += 4; 19391 } 19392 op_flags = *pc++; 19393 19394 obj = sp[-2 - is_computed]; 19395 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | 19396 JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; 19397 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) 19398 flags |= JS_PROP_ENUMERABLE; 19399 op_flags &= 3; 19400 value = JS_UNDEFINED; 19401 getter = JS_UNDEFINED; 19402 setter = JS_UNDEFINED; 19403 if (op_flags == OP_DEFINE_METHOD_METHOD) { 19404 value = sp[-1]; 19405 flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; 19406 } else if (op_flags == OP_DEFINE_METHOD_GETTER) { 19407 getter = sp[-1]; 19408 flags |= JS_PROP_HAS_GET; 19409 } else { 19410 setter = sp[-1]; 19411 flags |= JS_PROP_HAS_SET; 19412 } 19413 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); 19414 if (ret >= 0) { 19415 ret = JS_DefineProperty(ctx, obj, atom, value, 19416 getter, setter, flags); 19417 } 19418 JS_FreeValue(ctx, sp[-1]); 19419 if (is_computed) { 19420 JS_FreeAtom(ctx, atom); 19421 JS_FreeValue(ctx, sp[-2]); 19422 } 19423 sp -= 1 + is_computed; 19424 if (unlikely(ret < 0)) 19425 goto exception; 19426 } 19427 BREAK; 19428 19429 CASE(OP_define_class): 19430 CASE(OP_define_class_computed): 19431 { 19432 int class_flags; 19433 JSAtom atom; 19434 19435 atom = get_u32(pc); 19436 class_flags = pc[4]; 19437 pc += 5; 19438 if (js_op_define_class(ctx, sp, atom, class_flags, 19439 var_refs, sf, 19440 (opcode == OP_define_class_computed)) < 0) 19441 goto exception; 19442 } 19443 BREAK; 19444 19445 #define GET_ARRAY_EL_INLINE(name, keep) \ 19446 { \ 19447 JSValue val, obj, prop; \ 19448 JSObject *p; \ 19449 uint32_t idx; \ 19450 \ 19451 obj = sp[-2]; \ 19452 prop = sp[-1]; \ 19453 if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && \ 19454 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { \ 19455 p = JS_VALUE_GET_OBJ(obj); \ 19456 idx = JS_VALUE_GET_INT(prop); \ 19457 if (unlikely(p->class_id != JS_CLASS_ARRAY)) \ 19458 goto name ## _slow_path; \ 19459 if (unlikely(idx >= p->u.array.count)) \ 19460 goto name ## _slow_path; \ 19461 val = JS_DupValue(ctx, p->u.array.u.values[idx]); \ 19462 } else { \ 19463 name ## _slow_path: \ 19464 sf->cur_pc = pc; \ 19465 val = JS_GetPropertyValue(ctx, obj, prop); \ 19466 if (unlikely(JS_IsException(val))) { \ 19467 if (keep) \ 19468 sp[-1] = JS_UNDEFINED; \ 19469 else \ 19470 sp--; \ 19471 goto exception; \ 19472 } \ 19473 } \ 19474 if (keep) { \ 19475 sp[-1] = val; \ 19476 } else { \ 19477 JS_FreeValue(ctx, obj); \ 19478 sp[-2] = val; \ 19479 sp--; \ 19480 } \ 19481 } 19482 19483 CASE(OP_get_array_el): 19484 GET_ARRAY_EL_INLINE(get_array_el, 0); 19485 BREAK; 19486 19487 CASE(OP_get_array_el2): 19488 GET_ARRAY_EL_INLINE(get_array_el2, 1); 19489 BREAK; 19490 19491 CASE(OP_get_array_el3): 19492 { 19493 JSValue val; 19494 JSObject *p; 19495 uint32_t idx; 19496 19497 if (likely(JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_OBJECT && 19498 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_INT)) { 19499 p = JS_VALUE_GET_OBJ(sp[-2]); 19500 idx = JS_VALUE_GET_INT(sp[-1]); 19501 if (unlikely(p->class_id != JS_CLASS_ARRAY)) 19502 goto get_array_el3_slow_path; 19503 if (unlikely(idx >= p->u.array.count)) 19504 goto get_array_el3_slow_path; 19505 val = JS_DupValue(ctx, p->u.array.u.values[idx]); 19506 } else { 19507 get_array_el3_slow_path: 19508 switch (JS_VALUE_GET_TAG(sp[-1])) { 19509 case JS_TAG_INT: 19510 case JS_TAG_STRING: 19511 case JS_TAG_SYMBOL: 19512 /* undefined and null are tested in JS_GetPropertyValue() */ 19513 break; 19514 default: 19515 /* must be tested before JS_ToPropertyKey */ 19516 if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { 19517 JS_ThrowTypeError(ctx, "value has no property"); 19518 goto exception; 19519 } 19520 sf->cur_pc = pc; 19521 ret_val = JS_ToPropertyKey(ctx, sp[-1]); 19522 if (JS_IsException(ret_val)) 19523 goto exception; 19524 JS_FreeValue(ctx, sp[-1]); 19525 sp[-1] = ret_val; 19526 break; 19527 } 19528 sf->cur_pc = pc; 19529 val = JS_GetPropertyValue(ctx, sp[-2], JS_DupValue(ctx, sp[-1])); 19530 if (unlikely(JS_IsException(val))) 19531 goto exception; 19532 } 19533 *sp++ = val; 19534 } 19535 BREAK; 19536 19537 CASE(OP_get_ref_value): 19538 { 19539 JSValue val; 19540 JSAtom atom; 19541 int ret; 19542 19543 sf->cur_pc = pc; 19544 atom = JS_ValueToAtom(ctx, sp[-1]); 19545 if (atom == JS_ATOM_NULL) 19546 goto exception; 19547 if (unlikely(JS_IsUndefined(sp[-2]))) { 19548 JS_ThrowReferenceErrorNotDefined(ctx, atom); 19549 JS_FreeAtom(ctx, atom); 19550 goto exception; 19551 } 19552 ret = JS_HasProperty(ctx, sp[-2], atom); 19553 if (unlikely(ret <= 0)) { 19554 if (ret < 0) { 19555 JS_FreeAtom(ctx, atom); 19556 goto exception; 19557 } 19558 if (is_strict_mode(ctx)) { 19559 JS_ThrowReferenceErrorNotDefined(ctx, atom); 19560 JS_FreeAtom(ctx, atom); 19561 goto exception; 19562 } 19563 val = JS_UNDEFINED; 19564 } else { 19565 val = JS_GetProperty(ctx, sp[-2], atom); 19566 } 19567 JS_FreeAtom(ctx, atom); 19568 if (unlikely(JS_IsException(val))) 19569 goto exception; 19570 sp[0] = val; 19571 sp++; 19572 } 19573 BREAK; 19574 19575 CASE(OP_get_super_value): 19576 { 19577 JSValue val; 19578 JSAtom atom; 19579 sf->cur_pc = pc; 19580 atom = JS_ValueToAtom(ctx, sp[-1]); 19581 if (unlikely(atom == JS_ATOM_NULL)) 19582 goto exception; 19583 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); 19584 JS_FreeAtom(ctx, atom); 19585 if (unlikely(JS_IsException(val))) 19586 goto exception; 19587 JS_FreeValue(ctx, sp[-1]); 19588 JS_FreeValue(ctx, sp[-2]); 19589 JS_FreeValue(ctx, sp[-3]); 19590 sp[-3] = val; 19591 sp -= 2; 19592 } 19593 BREAK; 19594 19595 CASE(OP_put_array_el): 19596 { 19597 int ret; 19598 JSObject *p; 19599 uint32_t idx; 19600 19601 if (likely(JS_VALUE_GET_TAG(sp[-3]) == JS_TAG_OBJECT && 19602 JS_VALUE_GET_TAG(sp[-2]) == JS_TAG_INT)) { 19603 p = JS_VALUE_GET_OBJ(sp[-3]); 19604 idx = JS_VALUE_GET_INT(sp[-2]); 19605 if (unlikely(p->class_id != JS_CLASS_ARRAY)) 19606 goto put_array_el_slow_path; 19607 if (unlikely(idx >= (uint32_t)p->u.array.count)) { 19608 uint32_t new_len, array_len; 19609 if (unlikely(idx != (uint32_t)p->u.array.count || 19610 !p->fast_array || 19611 !can_extend_fast_array(p))) { 19612 goto put_array_el_slow_path; 19613 } 19614 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) != JS_TAG_INT)) 19615 goto put_array_el_slow_path; 19616 /* cannot overflow otherwise the length would not be an integer */ 19617 new_len = idx + 1; 19618 if (unlikely(new_len > p->u.array.u1.size)) 19619 goto put_array_el_slow_path; 19620 array_len = JS_VALUE_GET_INT(p->prop[0].u.value); 19621 if (new_len > array_len) { 19622 if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) 19623 goto put_array_el_slow_path; 19624 p->prop[0].u.value = JS_NewInt32(ctx, new_len); 19625 } 19626 p->u.array.count = new_len; 19627 p->u.array.u.values[idx] = sp[-1]; 19628 } else { 19629 set_value(ctx, &p->u.array.u.values[idx], sp[-1]); 19630 } 19631 JS_FreeValue(ctx, sp[-3]); 19632 sp -= 3; 19633 } else { 19634 put_array_el_slow_path: 19635 sf->cur_pc = pc; 19636 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); 19637 JS_FreeValue(ctx, sp[-3]); 19638 sp -= 3; 19639 if (unlikely(ret < 0)) 19640 goto exception; 19641 } 19642 } 19643 BREAK; 19644 19645 CASE(OP_put_ref_value): 19646 { 19647 int ret; 19648 JSAtom atom; 19649 sf->cur_pc = pc; 19650 atom = JS_ValueToAtom(ctx, sp[-2]); 19651 if (unlikely(atom == JS_ATOM_NULL)) 19652 goto exception; 19653 if (unlikely(JS_IsUndefined(sp[-3]))) { 19654 if (is_strict_mode(ctx)) { 19655 JS_ThrowReferenceErrorNotDefined(ctx, atom); 19656 JS_FreeAtom(ctx, atom); 19657 goto exception; 19658 } else { 19659 sp[-3] = JS_DupValue(ctx, ctx->global_obj); 19660 } 19661 } 19662 ret = JS_HasProperty(ctx, sp[-3], atom); 19663 if (unlikely(ret <= 0)) { 19664 if (unlikely(ret < 0)) { 19665 JS_FreeAtom(ctx, atom); 19666 goto exception; 19667 } 19668 if (is_strict_mode(ctx)) { 19669 JS_ThrowReferenceErrorNotDefined(ctx, atom); 19670 JS_FreeAtom(ctx, atom); 19671 goto exception; 19672 } 19673 } 19674 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-3], JS_PROP_THROW_STRICT); 19675 JS_FreeAtom(ctx, atom); 19676 JS_FreeValue(ctx, sp[-2]); 19677 JS_FreeValue(ctx, sp[-3]); 19678 sp -= 3; 19679 if (unlikely(ret < 0)) 19680 goto exception; 19681 } 19682 BREAK; 19683 19684 CASE(OP_put_super_value): 19685 { 19686 int ret; 19687 JSAtom atom; 19688 sf->cur_pc = pc; 19689 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { 19690 JS_ThrowTypeErrorNotAnObject(ctx); 19691 goto exception; 19692 } 19693 atom = JS_ValueToAtom(ctx, sp[-2]); 19694 if (unlikely(atom == JS_ATOM_NULL)) 19695 goto exception; 19696 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4], 19697 JS_PROP_THROW_STRICT); 19698 JS_FreeAtom(ctx, atom); 19699 JS_FreeValue(ctx, sp[-4]); 19700 JS_FreeValue(ctx, sp[-3]); 19701 JS_FreeValue(ctx, sp[-2]); 19702 sp -= 4; 19703 if (ret < 0) 19704 goto exception; 19705 } 19706 BREAK; 19707 19708 CASE(OP_define_array_el): 19709 { 19710 int ret; 19711 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], 19712 JS_PROP_C_W_E | JS_PROP_THROW); 19713 sp -= 1; 19714 if (unlikely(ret < 0)) 19715 goto exception; 19716 } 19717 BREAK; 19718 19719 CASE(OP_append): /* array pos enumobj -- array pos */ 19720 { 19721 sf->cur_pc = pc; 19722 if (js_append_enumerate(ctx, sp)) 19723 goto exception; 19724 JS_FreeValue(ctx, *--sp); 19725 } 19726 BREAK; 19727 19728 CASE(OP_copy_data_properties): /* target source excludeList */ 19729 { 19730 /* stack offsets (-1 based): 19731 2 bits for target, 19732 3 bits for source, 19733 2 bits for exclusionList */ 19734 int mask; 19735 19736 mask = *pc++; 19737 sf->cur_pc = pc; 19738 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], 19739 sp[-1 - ((mask >> 2) & 7)], 19740 sp[-1 - ((mask >> 5) & 7)], 0)) 19741 goto exception; 19742 } 19743 BREAK; 19744 19745 CASE(OP_add): 19746 { 19747 JSValue op1, op2; 19748 op1 = sp[-2]; 19749 op2 = sp[-1]; 19750 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 19751 int64_t r; 19752 r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); 19753 if (unlikely((int)r != r)) { 19754 sp[-2] = __JS_NewFloat64(ctx, (double)r); 19755 } else { 19756 sp[-2] = JS_NewInt32(ctx, r); 19757 } 19758 sp--; 19759 } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) || 19760 JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19761 double d1, d2; 19762 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) { 19763 d1 = JS_VALUE_GET_FLOAT64(op1); 19764 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 19765 d1 = JS_VALUE_GET_INT(op1); 19766 } else { 19767 goto add_slow_case; 19768 } 19769 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19770 d2 = JS_VALUE_GET_FLOAT64(op2); 19771 } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) { 19772 d2 = JS_VALUE_GET_INT(op2); 19773 } else { 19774 goto add_slow_case; 19775 } 19776 sp[-2] = __JS_NewFloat64(ctx, d1 + d2); 19777 sp--; 19778 } else if (JS_IsString(op1) && JS_IsString(op2)) { 19779 sp[-2] = JS_ConcatString(ctx, op1, op2); 19780 sp--; 19781 if (JS_IsException(sp[-1])) 19782 goto exception; 19783 } else { 19784 add_slow_case: 19785 sf->cur_pc = pc; 19786 if (js_add_slow(ctx, sp)) 19787 goto exception; 19788 sp--; 19789 } 19790 } 19791 BREAK; 19792 CASE(OP_add_loc): 19793 { 19794 JSValue op2; 19795 JSValue *pv; 19796 int idx; 19797 idx = *pc; 19798 pc += 1; 19799 19800 op2 = sp[-1]; 19801 pv = &var_buf[idx]; 19802 if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { 19803 int64_t r; 19804 r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); 19805 if (unlikely((int)r != r)) { 19806 *pv = __JS_NewFloat64(ctx, (double)r); 19807 } else { 19808 *pv = JS_NewInt32(ctx, r); 19809 } 19810 sp--; 19811 } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { 19812 *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + 19813 JS_VALUE_GET_FLOAT64(op2)); 19814 sp--; 19815 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING && 19816 JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { 19817 sp--; 19818 sf->cur_pc = pc; 19819 if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { 19820 JS_FreeValue(ctx, op2); 19821 } else { 19822 op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); 19823 if (JS_IsException(op2)) 19824 goto exception; 19825 set_value(ctx, pv, op2); 19826 } 19827 } else { 19828 JSValue ops[2]; 19829 /* In case of exception, js_add_slow frees ops[0] 19830 and ops[1], so we must duplicate *pv */ 19831 sf->cur_pc = pc; 19832 ops[0] = JS_DupValue(ctx, *pv); 19833 ops[1] = op2; 19834 sp--; 19835 if (js_add_slow(ctx, ops + 2)) 19836 goto exception; 19837 set_value(ctx, pv, ops[0]); 19838 } 19839 } 19840 BREAK; 19841 CASE(OP_sub): 19842 { 19843 JSValue op1, op2; 19844 op1 = sp[-2]; 19845 op2 = sp[-1]; 19846 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 19847 int64_t r; 19848 r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); 19849 if (unlikely((int)r != r)) { 19850 sp[-2] = __JS_NewFloat64(ctx, (double)r); 19851 } else { 19852 sp[-2] = JS_NewInt32(ctx, r); 19853 } 19854 sp--; 19855 } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) || 19856 JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19857 double d1, d2; 19858 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) { 19859 d1 = JS_VALUE_GET_FLOAT64(op1); 19860 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 19861 d1 = JS_VALUE_GET_INT(op1); 19862 } else { 19863 goto binary_arith_slow; 19864 } 19865 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19866 d2 = JS_VALUE_GET_FLOAT64(op2); 19867 } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) { 19868 d2 = JS_VALUE_GET_INT(op2); 19869 } else { 19870 goto binary_arith_slow; 19871 } 19872 sp[-2] = __JS_NewFloat64(ctx, d1 - d2); 19873 sp--; 19874 } else { 19875 goto binary_arith_slow; 19876 } 19877 } 19878 BREAK; 19879 CASE(OP_mul): 19880 { 19881 JSValue op1, op2; 19882 double d; 19883 op1 = sp[-2]; 19884 op2 = sp[-1]; 19885 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 19886 int32_t v1, v2; 19887 int64_t r; 19888 v1 = JS_VALUE_GET_INT(op1); 19889 v2 = JS_VALUE_GET_INT(op2); 19890 r = (int64_t)v1 * v2; 19891 if (unlikely((int)r != r)) { 19892 d = (double)r; 19893 goto mul_fp_res; 19894 } 19895 /* need to test zero case for -0 result */ 19896 if (unlikely(r == 0 && (v1 | v2) < 0)) { 19897 d = -0.0; 19898 goto mul_fp_res; 19899 } 19900 sp[-2] = JS_NewInt32(ctx, r); 19901 sp--; 19902 } else if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)) || 19903 JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19904 double d1, d2; 19905 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) { 19906 d1 = JS_VALUE_GET_FLOAT64(op1); 19907 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 19908 d1 = JS_VALUE_GET_INT(op1); 19909 } else { 19910 goto binary_arith_slow; 19911 } 19912 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op2))) { 19913 d2 = JS_VALUE_GET_FLOAT64(op2); 19914 } else if (JS_VALUE_GET_TAG(op2) == JS_TAG_INT) { 19915 d2 = JS_VALUE_GET_INT(op2); 19916 } else { 19917 goto binary_arith_slow; 19918 } 19919 d = d1 * d2; 19920 mul_fp_res: 19921 sp[-2] = __JS_NewFloat64(ctx, d); 19922 sp--; 19923 } else { 19924 goto binary_arith_slow; 19925 } 19926 } 19927 BREAK; 19928 CASE(OP_div): 19929 { 19930 JSValue op1, op2; 19931 op1 = sp[-2]; 19932 op2 = sp[-1]; 19933 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 19934 int v1, v2; 19935 v1 = JS_VALUE_GET_INT(op1); 19936 v2 = JS_VALUE_GET_INT(op2); 19937 sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); 19938 sp--; 19939 } else { 19940 goto binary_arith_slow; 19941 } 19942 } 19943 BREAK; 19944 CASE(OP_mod): 19945 { 19946 JSValue op1, op2; 19947 op1 = sp[-2]; 19948 op2 = sp[-1]; 19949 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 19950 int v1, v2, r; 19951 v1 = JS_VALUE_GET_INT(op1); 19952 v2 = JS_VALUE_GET_INT(op2); 19953 /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = 19954 -1 and the cases where the result is -0. */ 19955 if (unlikely(v1 < 0 || v2 <= 0)) 19956 goto binary_arith_slow; 19957 r = v1 % v2; 19958 sp[-2] = JS_NewInt32(ctx, r); 19959 sp--; 19960 } else { 19961 goto binary_arith_slow; 19962 } 19963 } 19964 BREAK; 19965 CASE(OP_pow): 19966 binary_arith_slow: 19967 sf->cur_pc = pc; 19968 if (js_binary_arith_slow(ctx, sp, opcode)) 19969 goto exception; 19970 sp--; 19971 BREAK; 19972 19973 CASE(OP_plus): 19974 { 19975 JSValue op1; 19976 uint32_t tag; 19977 op1 = sp[-1]; 19978 tag = JS_VALUE_GET_TAG(op1); 19979 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { 19980 } else if (tag == JS_TAG_NULL || tag == JS_TAG_BOOL) { 19981 sp[-1] = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); 19982 } else { 19983 sf->cur_pc = pc; 19984 if (js_unary_arith_slow(ctx, sp, opcode)) 19985 goto exception; 19986 } 19987 } 19988 BREAK; 19989 CASE(OP_neg): 19990 { 19991 JSValue op1; 19992 uint32_t tag; 19993 int val; 19994 double d; 19995 op1 = sp[-1]; 19996 tag = JS_VALUE_GET_TAG(op1); 19997 if (tag == JS_TAG_INT || 19998 tag == JS_TAG_BOOL || 19999 tag == JS_TAG_NULL) { 20000 val = JS_VALUE_GET_INT(op1); 20001 /* Note: -0 cannot be expressed as integer */ 20002 if (unlikely(val == 0)) { 20003 d = -0.0; 20004 goto neg_fp_res; 20005 } 20006 if (unlikely(val == INT32_MIN)) { 20007 d = -(double)val; 20008 goto neg_fp_res; 20009 } 20010 sp[-1] = JS_NewInt32(ctx, -val); 20011 } else if (JS_TAG_IS_FLOAT64(tag)) { 20012 d = -JS_VALUE_GET_FLOAT64(op1); 20013 neg_fp_res: 20014 sp[-1] = __JS_NewFloat64(ctx, d); 20015 } else { 20016 sf->cur_pc = pc; 20017 if (js_unary_arith_slow(ctx, sp, opcode)) 20018 goto exception; 20019 } 20020 } 20021 BREAK; 20022 CASE(OP_inc): 20023 { 20024 JSValue op1; 20025 int val; 20026 op1 = sp[-1]; 20027 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20028 val = JS_VALUE_GET_INT(op1); 20029 if (unlikely(val == INT32_MAX)) 20030 goto inc_slow; 20031 sp[-1] = JS_NewInt32(ctx, val + 1); 20032 } else { 20033 inc_slow: 20034 sf->cur_pc = pc; 20035 if (js_unary_arith_slow(ctx, sp, opcode)) 20036 goto exception; 20037 } 20038 } 20039 BREAK; 20040 CASE(OP_dec): 20041 { 20042 JSValue op1; 20043 int val; 20044 op1 = sp[-1]; 20045 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20046 val = JS_VALUE_GET_INT(op1); 20047 if (unlikely(val == INT32_MIN)) 20048 goto dec_slow; 20049 sp[-1] = JS_NewInt32(ctx, val - 1); 20050 } else { 20051 dec_slow: 20052 sf->cur_pc = pc; 20053 if (js_unary_arith_slow(ctx, sp, opcode)) 20054 goto exception; 20055 } 20056 } 20057 BREAK; 20058 CASE(OP_post_inc): 20059 { 20060 JSValue op1; 20061 int val; 20062 op1 = sp[-1]; 20063 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20064 val = JS_VALUE_GET_INT(op1); 20065 if (unlikely(val == INT32_MAX)) 20066 goto post_inc_slow; 20067 sp[0] = JS_NewInt32(ctx, val + 1); 20068 } else { 20069 post_inc_slow: 20070 sf->cur_pc = pc; 20071 if (js_post_inc_slow(ctx, sp, opcode)) 20072 goto exception; 20073 } 20074 sp++; 20075 } 20076 BREAK; 20077 CASE(OP_post_dec): 20078 { 20079 JSValue op1; 20080 int val; 20081 op1 = sp[-1]; 20082 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20083 val = JS_VALUE_GET_INT(op1); 20084 if (unlikely(val == INT32_MIN)) 20085 goto post_dec_slow; 20086 sp[0] = JS_NewInt32(ctx, val - 1); 20087 } else { 20088 post_dec_slow: 20089 sf->cur_pc = pc; 20090 if (js_post_inc_slow(ctx, sp, opcode)) 20091 goto exception; 20092 } 20093 sp++; 20094 } 20095 BREAK; 20096 CASE(OP_inc_loc): 20097 { 20098 JSValue op1; 20099 int val; 20100 int idx; 20101 idx = *pc; 20102 pc += 1; 20103 20104 op1 = var_buf[idx]; 20105 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20106 val = JS_VALUE_GET_INT(op1); 20107 if (unlikely(val == INT32_MAX)) 20108 goto inc_loc_slow; 20109 var_buf[idx] = JS_NewInt32(ctx, val + 1); 20110 } else { 20111 inc_loc_slow: 20112 sf->cur_pc = pc; 20113 /* must duplicate otherwise the variable value may 20114 be destroyed before JS code accesses it */ 20115 op1 = JS_DupValue(ctx, op1); 20116 if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) 20117 goto exception; 20118 set_value(ctx, &var_buf[idx], op1); 20119 } 20120 } 20121 BREAK; 20122 CASE(OP_dec_loc): 20123 { 20124 JSValue op1; 20125 int val; 20126 int idx; 20127 idx = *pc; 20128 pc += 1; 20129 20130 op1 = var_buf[idx]; 20131 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20132 val = JS_VALUE_GET_INT(op1); 20133 if (unlikely(val == INT32_MIN)) 20134 goto dec_loc_slow; 20135 var_buf[idx] = JS_NewInt32(ctx, val - 1); 20136 } else { 20137 dec_loc_slow: 20138 sf->cur_pc = pc; 20139 /* must duplicate otherwise the variable value may 20140 be destroyed before JS code accesses it */ 20141 op1 = JS_DupValue(ctx, op1); 20142 if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) 20143 goto exception; 20144 set_value(ctx, &var_buf[idx], op1); 20145 } 20146 } 20147 BREAK; 20148 CASE(OP_not): 20149 { 20150 JSValue op1; 20151 op1 = sp[-1]; 20152 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 20153 sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); 20154 } else { 20155 sf->cur_pc = pc; 20156 if (js_not_slow(ctx, sp)) 20157 goto exception; 20158 } 20159 } 20160 BREAK; 20161 20162 CASE(OP_shl): 20163 { 20164 JSValue op1, op2; 20165 op1 = sp[-2]; 20166 op2 = sp[-1]; 20167 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20168 uint32_t v1, v2; 20169 v1 = JS_VALUE_GET_INT(op1); 20170 v2 = JS_VALUE_GET_INT(op2); 20171 v2 &= 0x1f; 20172 sp[-2] = JS_NewInt32(ctx, v1 << v2); 20173 sp--; 20174 } else { 20175 sf->cur_pc = pc; 20176 if (js_binary_logic_slow(ctx, sp, opcode)) 20177 goto exception; 20178 sp--; 20179 } 20180 } 20181 BREAK; 20182 CASE(OP_shr): 20183 { 20184 JSValue op1, op2; 20185 op1 = sp[-2]; 20186 op2 = sp[-1]; 20187 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20188 uint32_t v2; 20189 v2 = JS_VALUE_GET_INT(op2); 20190 v2 &= 0x1f; 20191 sp[-2] = JS_NewUint32(ctx, 20192 (uint32_t)JS_VALUE_GET_INT(op1) >> 20193 v2); 20194 sp--; 20195 } else { 20196 sf->cur_pc = pc; 20197 if (js_shr_slow(ctx, sp)) 20198 goto exception; 20199 sp--; 20200 } 20201 } 20202 BREAK; 20203 CASE(OP_sar): 20204 { 20205 JSValue op1, op2; 20206 op1 = sp[-2]; 20207 op2 = sp[-1]; 20208 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20209 uint32_t v2; 20210 v2 = JS_VALUE_GET_INT(op2); 20211 v2 &= 0x1f; 20212 sp[-2] = JS_NewInt32(ctx, 20213 (int)JS_VALUE_GET_INT(op1) >> v2); 20214 sp--; 20215 } else { 20216 sf->cur_pc = pc; 20217 if (js_binary_logic_slow(ctx, sp, opcode)) 20218 goto exception; 20219 sp--; 20220 } 20221 } 20222 BREAK; 20223 CASE(OP_and): 20224 { 20225 JSValue op1, op2; 20226 op1 = sp[-2]; 20227 op2 = sp[-1]; 20228 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20229 sp[-2] = JS_NewInt32(ctx, 20230 JS_VALUE_GET_INT(op1) & 20231 JS_VALUE_GET_INT(op2)); 20232 sp--; 20233 } else { 20234 sf->cur_pc = pc; 20235 if (js_binary_logic_slow(ctx, sp, opcode)) 20236 goto exception; 20237 sp--; 20238 } 20239 } 20240 BREAK; 20241 CASE(OP_or): 20242 { 20243 JSValue op1, op2; 20244 op1 = sp[-2]; 20245 op2 = sp[-1]; 20246 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20247 sp[-2] = JS_NewInt32(ctx, 20248 JS_VALUE_GET_INT(op1) | 20249 JS_VALUE_GET_INT(op2)); 20250 sp--; 20251 } else { 20252 sf->cur_pc = pc; 20253 if (js_binary_logic_slow(ctx, sp, opcode)) 20254 goto exception; 20255 sp--; 20256 } 20257 } 20258 BREAK; 20259 CASE(OP_xor): 20260 { 20261 JSValue op1, op2; 20262 op1 = sp[-2]; 20263 op2 = sp[-1]; 20264 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 20265 sp[-2] = JS_NewInt32(ctx, 20266 JS_VALUE_GET_INT(op1) ^ 20267 JS_VALUE_GET_INT(op2)); 20268 sp--; 20269 } else { 20270 sf->cur_pc = pc; 20271 if (js_binary_logic_slow(ctx, sp, opcode)) 20272 goto exception; 20273 sp--; 20274 } 20275 } 20276 BREAK; 20277 20278 20279 #define OP_CMP(opcode, binary_op, slow_call) \ 20280 CASE(opcode): \ 20281 { \ 20282 JSValue op1, op2; \ 20283 op1 = sp[-2]; \ 20284 op2 = sp[-1]; \ 20285 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ 20286 sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ 20287 sp--; \ 20288 } else { \ 20289 sf->cur_pc = pc; \ 20290 if (slow_call) \ 20291 goto exception; \ 20292 sp--; \ 20293 } \ 20294 } \ 20295 BREAK 20296 20297 OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); 20298 OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); 20299 OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); 20300 OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); 20301 OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); 20302 OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); 20303 OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); 20304 OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); 20305 20306 CASE(OP_in): 20307 sf->cur_pc = pc; 20308 if (js_operator_in(ctx, sp)) 20309 goto exception; 20310 sp--; 20311 BREAK; 20312 CASE(OP_private_in): 20313 sf->cur_pc = pc; 20314 if (js_operator_private_in(ctx, sp)) 20315 goto exception; 20316 sp--; 20317 BREAK; 20318 CASE(OP_instanceof): 20319 sf->cur_pc = pc; 20320 if (js_operator_instanceof(ctx, sp)) 20321 goto exception; 20322 sp--; 20323 BREAK; 20324 CASE(OP_typeof): 20325 { 20326 JSValue op1; 20327 JSAtom atom; 20328 20329 op1 = sp[-1]; 20330 atom = js_operator_typeof(ctx, op1); 20331 JS_FreeValue(ctx, op1); 20332 sp[-1] = JS_AtomToString(ctx, atom); 20333 } 20334 BREAK; 20335 CASE(OP_delete): 20336 sf->cur_pc = pc; 20337 if (js_operator_delete(ctx, sp)) 20338 goto exception; 20339 sp--; 20340 BREAK; 20341 CASE(OP_delete_var): 20342 { 20343 JSAtom atom; 20344 int ret; 20345 20346 atom = get_u32(pc); 20347 pc += 4; 20348 sf->cur_pc = pc; 20349 20350 ret = JS_DeleteGlobalVar(ctx, atom); 20351 if (unlikely(ret < 0)) 20352 goto exception; 20353 *sp++ = JS_NewBool(ctx, ret); 20354 } 20355 BREAK; 20356 20357 CASE(OP_to_object): 20358 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { 20359 sf->cur_pc = pc; 20360 ret_val = JS_ToObject(ctx, sp[-1]); 20361 if (JS_IsException(ret_val)) 20362 goto exception; 20363 JS_FreeValue(ctx, sp[-1]); 20364 sp[-1] = ret_val; 20365 } 20366 BREAK; 20367 20368 CASE(OP_to_propkey): 20369 switch (JS_VALUE_GET_TAG(sp[-1])) { 20370 case JS_TAG_INT: 20371 case JS_TAG_STRING: 20372 case JS_TAG_SYMBOL: 20373 break; 20374 default: 20375 sf->cur_pc = pc; 20376 ret_val = JS_ToPropertyKey(ctx, sp[-1]); 20377 if (JS_IsException(ret_val)) 20378 goto exception; 20379 JS_FreeValue(ctx, sp[-1]); 20380 sp[-1] = ret_val; 20381 break; 20382 } 20383 BREAK; 20384 20385 #if 0 20386 CASE(OP_to_string): 20387 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { 20388 ret_val = JS_ToString(ctx, sp[-1]); 20389 if (JS_IsException(ret_val)) 20390 goto exception; 20391 JS_FreeValue(ctx, sp[-1]); 20392 sp[-1] = ret_val; 20393 } 20394 BREAK; 20395 #endif 20396 CASE(OP_with_get_var): 20397 CASE(OP_with_put_var): 20398 CASE(OP_with_delete_var): 20399 CASE(OP_with_make_ref): 20400 CASE(OP_with_get_ref): 20401 { 20402 JSAtom atom; 20403 int32_t diff; 20404 JSValue obj, val; 20405 int ret, is_with; 20406 atom = get_u32(pc); 20407 diff = get_u32(pc + 4); 20408 is_with = pc[8]; 20409 pc += 9; 20410 sf->cur_pc = pc; 20411 20412 obj = sp[-1]; 20413 ret = JS_HasProperty(ctx, obj, atom); 20414 if (unlikely(ret < 0)) 20415 goto exception; 20416 if (ret) { 20417 if (is_with) { 20418 ret = js_has_unscopable(ctx, obj, atom); 20419 if (unlikely(ret < 0)) 20420 goto exception; 20421 if (ret) 20422 goto no_with; 20423 } 20424 switch (opcode) { 20425 case OP_with_get_var: 20426 /* in Object Environment Records, GetBindingValue() calls HasProperty() */ 20427 ret = JS_HasProperty(ctx, obj, atom); 20428 if (unlikely(ret <= 0)) { 20429 if (ret < 0) 20430 goto exception; 20431 if (is_strict_mode(ctx)) { 20432 JS_ThrowReferenceErrorNotDefined(ctx, atom); 20433 goto exception; 20434 } 20435 val = JS_UNDEFINED; 20436 } else { 20437 val = JS_GetProperty(ctx, obj, atom); 20438 if (unlikely(JS_IsException(val))) 20439 goto exception; 20440 } 20441 set_value(ctx, &sp[-1], val); 20442 break; 20443 case OP_with_put_var: /* used e.g. in for in/of */ 20444 /* in Object Environment Records, SetMutableBinding() calls HasProperty() */ 20445 ret = JS_HasProperty(ctx, obj, atom); 20446 if (unlikely(ret <= 0)) { 20447 if (ret < 0) 20448 goto exception; 20449 if (is_strict_mode(ctx)) { 20450 JS_ThrowReferenceErrorNotDefined(ctx, atom); 20451 goto exception; 20452 } 20453 } 20454 ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, 20455 JS_PROP_THROW_STRICT); 20456 JS_FreeValue(ctx, sp[-1]); 20457 sp -= 2; 20458 if (unlikely(ret < 0)) 20459 goto exception; 20460 break; 20461 case OP_with_delete_var: 20462 ret = JS_DeleteProperty(ctx, obj, atom, 0); 20463 if (unlikely(ret < 0)) 20464 goto exception; 20465 JS_FreeValue(ctx, sp[-1]); 20466 sp[-1] = JS_NewBool(ctx, ret); 20467 break; 20468 case OP_with_make_ref: 20469 /* produce a pair object/propname on the stack */ 20470 *sp++ = JS_AtomToValue(ctx, atom); 20471 break; 20472 case OP_with_get_ref: 20473 /* produce a pair object/method on the stack */ 20474 /* in Object Environment Records, GetBindingValue() calls HasProperty() */ 20475 ret = JS_HasProperty(ctx, obj, atom); 20476 if (unlikely(ret < 0)) 20477 goto exception; 20478 if (!ret) { 20479 val = JS_UNDEFINED; 20480 } else { 20481 val = JS_GetProperty(ctx, obj, atom); 20482 if (unlikely(JS_IsException(val))) 20483 goto exception; 20484 } 20485 *sp++ = val; 20486 break; 20487 } 20488 pc += diff - 5; 20489 } else { 20490 no_with: 20491 /* if not jumping, drop the object argument */ 20492 JS_FreeValue(ctx, sp[-1]); 20493 sp--; 20494 } 20495 } 20496 BREAK; 20497 20498 CASE(OP_await): 20499 ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); 20500 goto done_generator; 20501 CASE(OP_yield): 20502 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); 20503 goto done_generator; 20504 CASE(OP_yield_star): 20505 CASE(OP_async_yield_star): 20506 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); 20507 goto done_generator; 20508 CASE(OP_return_async): 20509 ret_val = JS_UNDEFINED; 20510 goto done_generator; 20511 CASE(OP_initial_yield): 20512 ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD); 20513 goto done_generator; 20514 20515 CASE(OP_nop): 20516 BREAK; 20517 CASE(OP_is_undefined_or_null): 20518 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || 20519 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { 20520 goto set_true; 20521 } else { 20522 goto free_and_set_false; 20523 } 20524 #if SHORT_OPCODES 20525 CASE(OP_is_undefined): 20526 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { 20527 goto set_true; 20528 } else { 20529 goto free_and_set_false; 20530 } 20531 CASE(OP_is_null): 20532 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { 20533 goto set_true; 20534 } else { 20535 goto free_and_set_false; 20536 } 20537 /* XXX: could merge to a single opcode */ 20538 CASE(OP_typeof_is_undefined): 20539 /* different from OP_is_undefined because of isHTMLDDA */ 20540 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { 20541 goto free_and_set_true; 20542 } else { 20543 goto free_and_set_false; 20544 } 20545 CASE(OP_typeof_is_function): 20546 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { 20547 goto free_and_set_true; 20548 } else { 20549 goto free_and_set_false; 20550 } 20551 free_and_set_true: 20552 JS_FreeValue(ctx, sp[-1]); 20553 #endif 20554 set_true: 20555 sp[-1] = JS_TRUE; 20556 BREAK; 20557 free_and_set_false: 20558 JS_FreeValue(ctx, sp[-1]); 20559 sp[-1] = JS_FALSE; 20560 BREAK; 20561 CASE(OP_invalid): 20562 DEFAULT: 20563 JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", 20564 (int)(pc - b->byte_code_buf - 1), opcode); 20565 goto exception; 20566 } 20567 } 20568 exception: 20569 if (is_backtrace_needed(ctx, rt->current_exception)) { 20570 /* add the backtrace information now (it is not done 20571 before if the exception happens in a bytecode 20572 operation */ 20573 sf->cur_pc = pc; 20574 build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0); 20575 } 20576 if (!rt->current_exception_is_uncatchable) { 20577 while (sp > stack_buf) { 20578 JSValue val = *--sp; 20579 JS_FreeValue(ctx, val); 20580 if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { 20581 int pos = JS_VALUE_GET_INT(val); 20582 if (pos == 0) { 20583 /* enumerator: close it with a throw */ 20584 JS_FreeValue(ctx, sp[-1]); /* drop the next method */ 20585 sp--; 20586 JS_IteratorClose(ctx, sp[-1], TRUE); 20587 } else { 20588 *sp++ = rt->current_exception; 20589 rt->current_exception = JS_UNINITIALIZED; 20590 pc = b->byte_code_buf + pos; 20591 goto restart; 20592 } 20593 } 20594 } 20595 } 20596 ret_val = JS_EXCEPTION; 20597 /* the local variables are freed by the caller in the generator 20598 case. Hence the label 'done' should never be reached in a 20599 generator function. */ 20600 if (b->func_kind != JS_FUNC_NORMAL) { 20601 done_generator: 20602 sf->cur_pc = pc; 20603 sf->cur_sp = sp; 20604 } else { 20605 done: 20606 if (unlikely(b->var_ref_count != 0)) { 20607 /* variable references reference the stack: must close them */ 20608 close_var_refs(rt, b, sf); 20609 } 20610 /* free the local variables and stack */ 20611 for(pval = local_buf; pval < sp; pval++) { 20612 JS_FreeValue(ctx, *pval); 20613 } 20614 } 20615 rt->current_stack_frame = sf->prev_frame; 20616 return ret_val; 20617 } 20618 20619 #ifdef PERF_TRAMPOLINE 20620 20621 #include <sys/mman.h> 20622 #include <fcntl.h> 20623 #include <unistd.h> 20624 20625 static FILE * 20626 perf_map_get_file(void) 20627 { 20628 static FILE *perf_map_file = NULL; 20629 if (perf_map_file) { 20630 return perf_map_file; 20631 } 20632 char filename[100]; 20633 pid_t pid = getpid(); 20634 // Location and file name of perf map is hard-coded in perf tool. 20635 // Use exclusive create flag wit nofollow to prevent symlink attacks. 20636 int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC; 20637 snprintf(filename, sizeof(filename) - 1, "/tmp/perf-%jd.map", 20638 (intmax_t)pid); 20639 int fd = open(filename, flags, 0600); 20640 if (fd == -1) { 20641 return NULL; 20642 } 20643 perf_map_file = fdopen(fd, "w"); 20644 if (!perf_map_file) { 20645 close(fd); 20646 return NULL; 20647 } 20648 return perf_map_file; 20649 } 20650 20651 static void 20652 perf_map_write_entry(JSContext *ctx, const void *code_addr, unsigned int code_size, JSFunctionBytecode *b) 20653 { 20654 FILE *method_file = perf_map_get_file(); 20655 const char *atom_entry = NULL; 20656 const char *atom_filename = NULL; 20657 const char *filename = NULL; 20658 int line = 0; 20659 if (b->has_debug) { 20660 int col; 20661 line = find_line_num(ctx, b, -1, &col); 20662 } 20663 if (b->func_name != JS_ATOM_NULL) { 20664 atom_entry = JS_AtomToCString(ctx, b->func_name); 20665 } 20666 if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { 20667 atom_filename = JS_AtomToCString(ctx, b->debug.filename); 20668 } 20669 if (NULL == atom_filename) { 20670 filename = "<unknown>"; 20671 } else { 20672 filename = atom_filename; 20673 } 20674 if (NULL == atom_entry) { 20675 fprintf(method_file, "%p %x js@%s:%u\n", code_addr, code_size, filename, line); 20676 } else { 20677 fprintf(method_file, "%p %x js::%s@%s:%u\n", code_addr, code_size, atom_entry, filename, line); 20678 } 20679 fflush(method_file); 20680 JS_FreeCString(ctx, atom_entry); 20681 JS_FreeCString(ctx, atom_filename); 20682 } 20683 20684 typedef struct { 20685 JSContext *caller_ctx; 20686 JSValueConst func_obj; 20687 JSValueConst this_obj; 20688 JSValueConst new_target; 20689 int argc; 20690 JSValue *argv; 20691 int flags; 20692 } CallInternalArgs; 20693 20694 typedef JSValue CallFn(CallInternalArgs *args); 20695 typedef JSValue TrampolineFn(CallInternalArgs *args, CallFn fn); 20696 20697 /** 20698 * For x86-64: 20699 * push %rbp; mov %rsp,%rbp; call *%rsi; pop %rbp; ret; 20700 */ 20701 char perf_trampoline_code[] = {0x55, 0x48, 0x89, 0xe5, 0xff, 0xd6, 0x5d, 0xc3}; 20702 20703 void *compile_trampoline() 20704 { 20705 size_t mem_size = 4096 * 16; 20706 char *memory = 20707 mmap(NULL, // address 20708 mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 20709 -1, // fd (not used here) 20710 0); // offset (not used here) 20711 memcpy(memory, perf_trampoline_code, 8); 20712 mprotect(memory, mem_size, PROT_READ | PROT_EXEC); 20713 return memory; 20714 } 20715 20716 static JSValue fallback_trampoline(CallInternalArgs *ci_args, CallFn fn) 20717 { 20718 JSValue value; 20719 value = fn(ci_args); 20720 return value; 20721 } 20722 20723 static JSValue JS_CallInternalStruct(CallInternalArgs *ci_args) 20724 { 20725 return __JS_CallInternal(ci_args->caller_ctx, ci_args->func_obj, 20726 ci_args->this_obj, ci_args->new_target, 20727 ci_args->argc, ci_args->argv, ci_args->flags); 20728 } 20729 20730 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 20731 JSValueConst this_obj, JSValueConst new_target, 20732 int argc, JSValue *argv, int flags) 20733 { 20734 CallInternalArgs ci_args; 20735 JSObject *p; 20736 JSFunctionBytecode *b = NULL; 20737 20738 ci_args.caller_ctx = caller_ctx; 20739 ci_args.func_obj = func_obj; 20740 ci_args.this_obj = this_obj; 20741 ci_args.new_target = new_target; 20742 ci_args.argc = argc; 20743 ci_args.argv = argv; 20744 ci_args.flags = flags; 20745 20746 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { 20747 if (flags & JS_CALL_FLAG_GENERATOR) { 20748 JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); 20749 JSStackFrame *sf; 20750 /* func_obj get contains a pointer to JSFuncAsyncState */ 20751 /* the stack frame is already allocated */ 20752 sf = &s->frame; 20753 p = JS_VALUE_GET_OBJ(sf->cur_func); 20754 b = p->u.func.function_bytecode; 20755 } 20756 } else { 20757 p = JS_VALUE_GET_OBJ(func_obj); 20758 if (p->class_id == JS_CLASS_BYTECODE_FUNCTION) { 20759 b = p->u.func.function_bytecode; 20760 } 20761 } 20762 20763 if (b) { 20764 TrampolineFn *fn; 20765 if (!b->perf_trampoline) { 20766 b->perf_trampoline = compile_trampoline(); 20767 if (b->perf_trampoline) { 20768 perf_map_write_entry(caller_ctx, b->perf_trampoline, 8, b); 20769 } 20770 } 20771 fn = b->perf_trampoline; 20772 if (fn) { 20773 return fn(&ci_args, JS_CallInternalStruct); 20774 } 20775 } 20776 20777 return fallback_trampoline(&ci_args, JS_CallInternalStruct); 20778 //return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags); 20779 } 20780 20781 #else 20782 20783 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 20784 JSValueConst this_obj, JSValueConst new_target, 20785 int argc, JSValue *argv, int flags) 20786 { 20787 return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags); 20788 } 20789 20790 #endif /* PERF_TRAMPOLINE */ 20791 20792 #ifdef OPCODE_ASM_LABEL 20793 #pragma GCC diagnostic pop 20794 #endif 20795 20796 JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, 20797 int argc, JSValueConst *argv) 20798 { 20799 return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, 20800 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); 20801 } 20802 20803 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, 20804 int argc, JSValueConst *argv) 20805 { 20806 JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, 20807 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); 20808 JS_FreeValue(ctx, func_obj); 20809 return res; 20810 } 20811 20812 /* warning: the refcount of the context is not incremented. Return 20813 NULL in case of exception (case of revoked proxy only) */ 20814 static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) 20815 { 20816 JSObject *p; 20817 JSContext *realm; 20818 20819 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 20820 return ctx; 20821 p = JS_VALUE_GET_OBJ(func_obj); 20822 switch(p->class_id) { 20823 case JS_CLASS_C_FUNCTION: 20824 realm = p->u.cfunc.realm; 20825 break; 20826 case JS_CLASS_BYTECODE_FUNCTION: 20827 case JS_CLASS_GENERATOR_FUNCTION: 20828 case JS_CLASS_ASYNC_FUNCTION: 20829 case JS_CLASS_ASYNC_GENERATOR_FUNCTION: 20830 { 20831 JSFunctionBytecode *b; 20832 b = p->u.func.function_bytecode; 20833 realm = b->realm; 20834 } 20835 break; 20836 case JS_CLASS_PROXY: 20837 { 20838 JSProxyData *s = p->u.opaque; 20839 if (!s) 20840 return ctx; 20841 if (s->is_revoked) { 20842 JS_ThrowTypeErrorRevokedProxy(ctx); 20843 return NULL; 20844 } else { 20845 realm = JS_GetFunctionRealm(ctx, s->target); 20846 } 20847 } 20848 break; 20849 case JS_CLASS_BOUND_FUNCTION: 20850 { 20851 JSBoundFunction *bf = p->u.bound_function; 20852 realm = JS_GetFunctionRealm(ctx, bf->func_obj); 20853 } 20854 break; 20855 default: 20856 realm = ctx; 20857 break; 20858 } 20859 return realm; 20860 } 20861 20862 static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, 20863 int class_id) 20864 { 20865 JSValue proto, obj; 20866 JSContext *realm; 20867 20868 if (JS_IsUndefined(ctor)) { 20869 proto = JS_DupValue(ctx, ctx->class_proto[class_id]); 20870 } else { 20871 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); 20872 if (JS_IsException(proto)) 20873 return proto; 20874 if (!JS_IsObject(proto)) { 20875 JS_FreeValue(ctx, proto); 20876 realm = JS_GetFunctionRealm(ctx, ctor); 20877 if (!realm) 20878 return JS_EXCEPTION; 20879 proto = JS_DupValue(ctx, realm->class_proto[class_id]); 20880 } 20881 } 20882 obj = JS_NewObjectProtoClass(ctx, proto, class_id); 20883 JS_FreeValue(ctx, proto); 20884 return obj; 20885 } 20886 20887 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ 20888 static JSValue JS_CallConstructorInternal(JSContext *ctx, 20889 JSValueConst func_obj, 20890 JSValueConst new_target, 20891 int argc, JSValue *argv, int flags) 20892 { 20893 JSObject *p; 20894 JSFunctionBytecode *b; 20895 20896 if (js_poll_interrupts(ctx)) 20897 return JS_EXCEPTION; 20898 flags |= JS_CALL_FLAG_CONSTRUCTOR; 20899 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) 20900 goto not_a_function; 20901 p = JS_VALUE_GET_OBJ(func_obj); 20902 if (unlikely(!p->is_constructor)) 20903 return JS_ThrowTypeErrorNotAConstructor(ctx, func_obj); 20904 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { 20905 JSClassCall *call_func; 20906 call_func = ctx->rt->class_array[p->class_id].call; 20907 if (!call_func) { 20908 not_a_function: 20909 return JS_ThrowTypeError(ctx, "not a function"); 20910 } 20911 return call_func(ctx, func_obj, new_target, argc, 20912 (JSValueConst *)argv, flags); 20913 } 20914 20915 b = p->u.func.function_bytecode; 20916 if (b->is_derived_class_constructor) { 20917 return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); 20918 } else { 20919 JSValue obj, ret; 20920 /* legacy constructor behavior */ 20921 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); 20922 if (JS_IsException(obj)) 20923 return JS_EXCEPTION; 20924 ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); 20925 if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || 20926 JS_IsException(ret)) { 20927 JS_FreeValue(ctx, obj); 20928 return ret; 20929 } else { 20930 JS_FreeValue(ctx, ret); 20931 return obj; 20932 } 20933 } 20934 } 20935 20936 JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, 20937 JSValueConst new_target, 20938 int argc, JSValueConst *argv) 20939 { 20940 return JS_CallConstructorInternal(ctx, func_obj, new_target, 20941 argc, (JSValue *)argv, 20942 JS_CALL_FLAG_COPY_ARGV); 20943 } 20944 20945 JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, 20946 int argc, JSValueConst *argv) 20947 { 20948 return JS_CallConstructorInternal(ctx, func_obj, func_obj, 20949 argc, (JSValue *)argv, 20950 JS_CALL_FLAG_COPY_ARGV); 20951 } 20952 20953 JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, 20954 int argc, JSValueConst *argv) 20955 { 20956 JSValue func_obj; 20957 func_obj = JS_GetProperty(ctx, this_val, atom); 20958 if (JS_IsException(func_obj)) 20959 return func_obj; 20960 return JS_CallFree(ctx, func_obj, this_val, argc, argv); 20961 } 20962 20963 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, 20964 int argc, JSValueConst *argv) 20965 { 20966 JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); 20967 JS_FreeValue(ctx, this_val); 20968 return res; 20969 } 20970 20971 /* JSAsyncFunctionState (used by generator and async functions) */ 20972 static JSAsyncFunctionState *async_func_init(JSContext *ctx, 20973 JSValueConst func_obj, JSValueConst this_obj, 20974 int argc, JSValueConst *argv) 20975 { 20976 JSAsyncFunctionState *s; 20977 JSObject *p; 20978 JSFunctionBytecode *b; 20979 JSStackFrame *sf; 20980 int i, arg_buf_len, n; 20981 20982 p = JS_VALUE_GET_OBJ(func_obj); 20983 b = p->u.func.function_bytecode; 20984 arg_buf_len = max_int(b->arg_count, argc); 20985 s = js_malloc(ctx, sizeof(*s) + sizeof(JSValue) * (arg_buf_len + b->var_count + b->stack_size) + sizeof(JSVarRef *) * b->var_ref_count); 20986 if (!s) 20987 return NULL; 20988 memset(s, 0, sizeof(*s)); 20989 js_rc(s)->ref_count = 1; 20990 add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); 20991 20992 sf = &s->frame; 20993 sf->js_mode = b->js_mode | JS_MODE_ASYNC; 20994 sf->cur_pc = b->byte_code_buf; 20995 sf->arg_buf = (JSValue *)(s + 1); 20996 sf->cur_func = JS_DupValue(ctx, func_obj); 20997 s->this_val = JS_DupValue(ctx, this_obj); 20998 s->argc = argc; 20999 sf->arg_count = arg_buf_len; 21000 sf->var_buf = sf->arg_buf + arg_buf_len; 21001 sf->cur_sp = sf->var_buf + b->var_count; 21002 sf->var_refs = (JSVarRef **)(sf->cur_sp + b->stack_size); 21003 for(i = 0; i < b->var_ref_count; i++) 21004 sf->var_refs[i] = NULL; 21005 for(i = 0; i < argc; i++) 21006 sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); 21007 n = arg_buf_len + b->var_count; 21008 for(i = argc; i < n; i++) 21009 sf->arg_buf[i] = JS_UNDEFINED; 21010 s->resolving_funcs[0] = JS_UNDEFINED; 21011 s->resolving_funcs[1] = JS_UNDEFINED; 21012 s->is_completed = FALSE; 21013 return s; 21014 } 21015 21016 static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) 21017 { 21018 JSStackFrame *sf = &s->frame; 21019 JSValue *sp; 21020 21021 /* cannot free the function if it is running */ 21022 assert(sf->cur_sp != NULL); 21023 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { 21024 JS_FreeValueRT(rt, *sp); 21025 } 21026 JS_FreeValueRT(rt, sf->cur_func); 21027 JS_FreeValueRT(rt, s->this_val); 21028 } 21029 21030 static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) 21031 { 21032 JSRuntime *rt = ctx->rt; 21033 JSStackFrame *sf = &s->frame; 21034 JSValue func_obj, ret; 21035 21036 assert(!s->is_completed); 21037 if (js_check_stack_overflow(ctx->rt, 0)) { 21038 ret = JS_ThrowStackOverflow(ctx); 21039 } else { 21040 /* the tag does not matter provided it is not an object */ 21041 func_obj = JS_MKPTR(JS_TAG_INT, s); 21042 ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, 21043 s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); 21044 } 21045 if (JS_IsException(ret) || JS_IsUndefined(ret)) { 21046 JSObject *p; 21047 JSFunctionBytecode *b; 21048 21049 p = JS_VALUE_GET_OBJ(sf->cur_func); 21050 b = p->u.func.function_bytecode; 21051 21052 if (JS_IsUndefined(ret)) { 21053 ret = sf->cur_sp[-1]; 21054 sf->cur_sp[-1] = JS_UNDEFINED; 21055 } 21056 /* end of execution */ 21057 s->is_completed = TRUE; 21058 21059 /* close the closure variables. */ 21060 close_var_refs(rt, b, sf); 21061 21062 async_func_free_frame(rt, s); 21063 } 21064 return ret; 21065 } 21066 21067 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) 21068 { 21069 /* cannot close the closure variables here because it would 21070 potentially modify the object graph */ 21071 if (!s->is_completed) { 21072 async_func_free_frame(rt, s); 21073 } 21074 21075 JS_FreeValueRT(rt, s->resolving_funcs[0]); 21076 JS_FreeValueRT(rt, s->resolving_funcs[1]); 21077 21078 remove_gc_object(&s->header); 21079 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(s)->ref_count != 0) { 21080 list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list); 21081 } else { 21082 js_free_rt(rt, s); 21083 } 21084 } 21085 21086 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) 21087 { 21088 if (--js_rc(s)->ref_count == 0) { 21089 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { 21090 list_del(&s->header.link); 21091 list_add(&s->header.link, &rt->gc_zero_ref_count_list); 21092 if (rt->gc_phase == JS_GC_PHASE_NONE) { 21093 free_zero_refcount(rt); 21094 } 21095 } 21096 } 21097 } 21098 21099 /* Generators */ 21100 21101 typedef enum JSGeneratorStateEnum { 21102 JS_GENERATOR_STATE_SUSPENDED_START, 21103 JS_GENERATOR_STATE_SUSPENDED_YIELD, 21104 JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, 21105 JS_GENERATOR_STATE_EXECUTING, 21106 JS_GENERATOR_STATE_COMPLETED, 21107 } JSGeneratorStateEnum; 21108 21109 typedef struct JSGeneratorData { 21110 JSGeneratorStateEnum state; 21111 JSAsyncFunctionState *func_state; 21112 } JSGeneratorData; 21113 21114 static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) 21115 { 21116 if (s->state == JS_GENERATOR_STATE_COMPLETED) 21117 return; 21118 if (s->func_state) { 21119 async_func_free(rt, s->func_state); 21120 s->func_state = NULL; 21121 } 21122 s->state = JS_GENERATOR_STATE_COMPLETED; 21123 } 21124 21125 static void js_generator_finalizer(JSRuntime *rt, JSValue obj) 21126 { 21127 JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); 21128 21129 if (s) { 21130 free_generator_stack_rt(rt, s); 21131 js_free_rt(rt, s); 21132 } 21133 } 21134 21135 static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) 21136 { 21137 free_generator_stack_rt(ctx->rt, s); 21138 } 21139 21140 static void js_generator_mark(JSRuntime *rt, JSValueConst val, 21141 JS_MarkFunc *mark_func) 21142 { 21143 JSObject *p = JS_VALUE_GET_OBJ(val); 21144 JSGeneratorData *s = p->u.generator_data; 21145 21146 if (!s || !s->func_state) 21147 return; 21148 mark_func(rt, &s->func_state->header); 21149 } 21150 21151 /* XXX: use enum */ 21152 #define GEN_MAGIC_NEXT 0 21153 #define GEN_MAGIC_RETURN 1 21154 #define GEN_MAGIC_THROW 2 21155 21156 static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, 21157 int argc, JSValueConst *argv, 21158 BOOL *pdone, int magic) 21159 { 21160 JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); 21161 JSStackFrame *sf; 21162 JSValue ret, func_ret; 21163 21164 *pdone = TRUE; 21165 if (!s) 21166 return JS_ThrowTypeError(ctx, "not a generator"); 21167 switch(s->state) { 21168 default: 21169 case JS_GENERATOR_STATE_SUSPENDED_START: 21170 sf = &s->func_state->frame; 21171 if (magic == GEN_MAGIC_NEXT) { 21172 goto exec_no_arg; 21173 } else { 21174 free_generator_stack(ctx, s); 21175 goto done; 21176 } 21177 break; 21178 case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: 21179 case JS_GENERATOR_STATE_SUSPENDED_YIELD: 21180 sf = &s->func_state->frame; 21181 /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ 21182 ret = JS_DupValue(ctx, argv[0]); 21183 if (magic == GEN_MAGIC_THROW && 21184 s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { 21185 JS_Throw(ctx, ret); 21186 s->func_state->throw_flag = TRUE; 21187 } else { 21188 sf->cur_sp[-1] = ret; 21189 sf->cur_sp[0] = JS_NewInt32(ctx, magic); 21190 sf->cur_sp++; 21191 exec_no_arg: 21192 s->func_state->throw_flag = FALSE; 21193 } 21194 s->state = JS_GENERATOR_STATE_EXECUTING; 21195 func_ret = async_func_resume(ctx, s->func_state); 21196 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; 21197 if (s->func_state->is_completed) { 21198 /* finalize the execution in case of exception or normal return */ 21199 free_generator_stack(ctx, s); 21200 return func_ret; 21201 } else { 21202 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); 21203 /* get the returned yield value at the top of the stack */ 21204 ret = sf->cur_sp[-1]; 21205 sf->cur_sp[-1] = JS_UNDEFINED; 21206 if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { 21207 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; 21208 /* return (value, done) object */ 21209 *pdone = 2; 21210 } else { 21211 *pdone = FALSE; 21212 } 21213 } 21214 break; 21215 case JS_GENERATOR_STATE_COMPLETED: 21216 done: 21217 /* execution is finished */ 21218 switch(magic) { 21219 default: 21220 case GEN_MAGIC_NEXT: 21221 ret = JS_UNDEFINED; 21222 break; 21223 case GEN_MAGIC_RETURN: 21224 ret = JS_DupValue(ctx, argv[0]); 21225 break; 21226 case GEN_MAGIC_THROW: 21227 ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); 21228 break; 21229 } 21230 break; 21231 case JS_GENERATOR_STATE_EXECUTING: 21232 ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); 21233 break; 21234 } 21235 return ret; 21236 } 21237 21238 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, 21239 JSValueConst this_obj, 21240 int argc, JSValueConst *argv, 21241 int flags) 21242 { 21243 JSValue obj, func_ret; 21244 JSGeneratorData *s; 21245 21246 s = js_mallocz(ctx, sizeof(*s)); 21247 if (!s) 21248 return JS_EXCEPTION; 21249 s->state = JS_GENERATOR_STATE_SUSPENDED_START; 21250 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); 21251 if (!s->func_state) { 21252 s->state = JS_GENERATOR_STATE_COMPLETED; 21253 goto fail; 21254 } 21255 21256 /* execute the function up to 'OP_initial_yield' */ 21257 func_ret = async_func_resume(ctx, s->func_state); 21258 if (JS_IsException(func_ret)) 21259 goto fail; 21260 JS_FreeValue(ctx, func_ret); 21261 21262 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); 21263 if (JS_IsException(obj)) 21264 goto fail; 21265 JS_SetOpaque(obj, s); 21266 return obj; 21267 fail: 21268 free_generator_stack_rt(ctx->rt, s); 21269 js_free(ctx, s); 21270 return JS_EXCEPTION; 21271 } 21272 21273 /* AsyncFunction */ 21274 21275 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) 21276 { 21277 JSObject *p = JS_VALUE_GET_OBJ(val); 21278 JSAsyncFunctionState *s = p->u.async_function_data; 21279 if (s) { 21280 async_func_free(rt, s); 21281 } 21282 } 21283 21284 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, 21285 JS_MarkFunc *mark_func) 21286 { 21287 JSObject *p = JS_VALUE_GET_OBJ(val); 21288 JSAsyncFunctionState *s = p->u.async_function_data; 21289 if (s) { 21290 mark_func(rt, &s->header); 21291 } 21292 } 21293 21294 static int js_async_function_resolve_create(JSContext *ctx, 21295 JSAsyncFunctionState *s, 21296 JSValue *resolving_funcs) 21297 { 21298 int i; 21299 JSObject *p; 21300 21301 for(i = 0; i < 2; i++) { 21302 resolving_funcs[i] = 21303 JS_NewObjectProtoClass(ctx, ctx->function_proto, 21304 JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); 21305 if (JS_IsException(resolving_funcs[i])) { 21306 if (i == 1) 21307 JS_FreeValue(ctx, resolving_funcs[0]); 21308 return -1; 21309 } 21310 p = JS_VALUE_GET_OBJ(resolving_funcs[i]); 21311 js_rc(s)->ref_count++; 21312 p->u.async_function_data = s; 21313 } 21314 return 0; 21315 } 21316 21317 static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s) 21318 { 21319 JSValue func_ret, ret2; 21320 21321 func_ret = async_func_resume(ctx, s); 21322 if (s->is_completed) { 21323 if (JS_IsException(func_ret)) { 21324 JSValue error; 21325 fail: 21326 error = JS_GetException(ctx); 21327 ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, 21328 1, (JSValueConst *)&error); 21329 JS_FreeValue(ctx, error); 21330 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ 21331 } else { 21332 /* normal return */ 21333 ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, 21334 1, (JSValueConst *)&func_ret); 21335 JS_FreeValue(ctx, func_ret); 21336 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ 21337 } 21338 } else { 21339 JSValue value, promise, resolving_funcs[2], resolving_funcs1[2]; 21340 int i, res; 21341 21342 value = s->frame.cur_sp[-1]; 21343 s->frame.cur_sp[-1] = JS_UNDEFINED; 21344 21345 /* await */ 21346 JS_FreeValue(ctx, func_ret); /* not used */ 21347 promise = js_promise_resolve(ctx, ctx->promise_ctor, 21348 1, (JSValueConst *)&value, 0); 21349 JS_FreeValue(ctx, value); 21350 if (JS_IsException(promise)) 21351 goto fail; 21352 if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { 21353 JS_FreeValue(ctx, promise); 21354 goto fail; 21355 } 21356 21357 /* Note: no need to create 'thrownawayCapability' as in 21358 the spec */ 21359 for(i = 0; i < 2; i++) 21360 resolving_funcs1[i] = JS_UNDEFINED; 21361 res = perform_promise_then(ctx, promise, 21362 (JSValueConst *)resolving_funcs, 21363 (JSValueConst *)resolving_funcs1); 21364 JS_FreeValue(ctx, promise); 21365 for(i = 0; i < 2; i++) 21366 JS_FreeValue(ctx, resolving_funcs[i]); 21367 if (res) 21368 goto fail; 21369 } 21370 } 21371 21372 static JSValue js_async_function_resolve_call(JSContext *ctx, 21373 JSValueConst func_obj, 21374 JSValueConst this_obj, 21375 int argc, JSValueConst *argv, 21376 int flags) 21377 { 21378 JSObject *p = JS_VALUE_GET_OBJ(func_obj); 21379 JSAsyncFunctionState *s = p->u.async_function_data; 21380 BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; 21381 JSValueConst arg; 21382 21383 if (argc > 0) 21384 arg = argv[0]; 21385 else 21386 arg = JS_UNDEFINED; 21387 s->throw_flag = is_reject; 21388 if (is_reject) { 21389 JS_Throw(ctx, JS_DupValue(ctx, arg)); 21390 } else { 21391 /* return value of await */ 21392 s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); 21393 } 21394 js_async_function_resume(ctx, s); 21395 return JS_UNDEFINED; 21396 } 21397 21398 static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, 21399 JSValueConst this_obj, 21400 int argc, JSValueConst *argv, int flags) 21401 { 21402 JSValue promise; 21403 JSAsyncFunctionState *s; 21404 21405 s = async_func_init(ctx, func_obj, this_obj, argc, argv); 21406 if (!s) 21407 return JS_EXCEPTION; 21408 21409 promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); 21410 if (JS_IsException(promise)) { 21411 async_func_free(ctx->rt, s); 21412 return JS_EXCEPTION; 21413 } 21414 21415 js_async_function_resume(ctx, s); 21416 21417 async_func_free(ctx->rt, s); 21418 21419 return promise; 21420 } 21421 21422 /* AsyncGenerator */ 21423 21424 typedef enum JSAsyncGeneratorStateEnum { 21425 JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, 21426 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, 21427 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, 21428 JS_ASYNC_GENERATOR_STATE_EXECUTING, 21429 JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, 21430 JS_ASYNC_GENERATOR_STATE_COMPLETED, 21431 } JSAsyncGeneratorStateEnum; 21432 21433 typedef struct JSAsyncGeneratorRequest { 21434 struct list_head link; 21435 /* completion */ 21436 int completion_type; /* GEN_MAGIC_x */ 21437 JSValue result; 21438 /* promise capability */ 21439 JSValue promise; 21440 JSValue resolving_funcs[2]; 21441 } JSAsyncGeneratorRequest; 21442 21443 typedef struct JSAsyncGeneratorData { 21444 JSObject *generator; /* back pointer to the object (const) */ 21445 JSAsyncGeneratorStateEnum state; 21446 /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ 21447 JSAsyncFunctionState *func_state; 21448 struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ 21449 } JSAsyncGeneratorData; 21450 21451 static void js_async_generator_free(JSRuntime *rt, 21452 JSAsyncGeneratorData *s) 21453 { 21454 struct list_head *el, *el1; 21455 JSAsyncGeneratorRequest *req; 21456 21457 list_for_each_safe(el, el1, &s->queue) { 21458 req = list_entry(el, JSAsyncGeneratorRequest, link); 21459 JS_FreeValueRT(rt, req->result); 21460 JS_FreeValueRT(rt, req->promise); 21461 JS_FreeValueRT(rt, req->resolving_funcs[0]); 21462 JS_FreeValueRT(rt, req->resolving_funcs[1]); 21463 js_free_rt(rt, req); 21464 } 21465 if (s->func_state) 21466 async_func_free(rt, s->func_state); 21467 js_free_rt(rt, s); 21468 } 21469 21470 static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) 21471 { 21472 JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); 21473 21474 if (s) { 21475 js_async_generator_free(rt, s); 21476 } 21477 } 21478 21479 static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, 21480 JS_MarkFunc *mark_func) 21481 { 21482 JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); 21483 struct list_head *el; 21484 JSAsyncGeneratorRequest *req; 21485 if (s) { 21486 list_for_each(el, &s->queue) { 21487 req = list_entry(el, JSAsyncGeneratorRequest, link); 21488 JS_MarkValue(rt, req->result, mark_func); 21489 JS_MarkValue(rt, req->promise, mark_func); 21490 JS_MarkValue(rt, req->resolving_funcs[0], mark_func); 21491 JS_MarkValue(rt, req->resolving_funcs[1], mark_func); 21492 } 21493 if (s->func_state) { 21494 mark_func(rt, &s->func_state->header); 21495 } 21496 } 21497 } 21498 21499 static JSValue js_async_generator_resolve_function(JSContext *ctx, 21500 JSValueConst this_obj, 21501 int argc, JSValueConst *argv, 21502 int magic, JSValue *func_data); 21503 21504 static int js_async_generator_resolve_function_create(JSContext *ctx, 21505 JSValueConst generator, 21506 JSValue *resolving_funcs, 21507 BOOL is_resume_next) 21508 { 21509 int i; 21510 JSValue func; 21511 21512 for(i = 0; i < 2; i++) { 21513 func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, 21514 i + is_resume_next * 2, 1, &generator); 21515 if (JS_IsException(func)) { 21516 if (i == 1) 21517 JS_FreeValue(ctx, resolving_funcs[0]); 21518 return -1; 21519 } 21520 resolving_funcs[i] = func; 21521 } 21522 return 0; 21523 } 21524 21525 static int js_async_generator_await(JSContext *ctx, 21526 JSAsyncGeneratorData *s, 21527 JSValueConst value) 21528 { 21529 JSValue promise, resolving_funcs[2], resolving_funcs1[2]; 21530 int i, res; 21531 21532 promise = js_promise_resolve(ctx, ctx->promise_ctor, 21533 1, &value, 0); 21534 if (JS_IsException(promise)) 21535 goto fail; 21536 21537 if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), 21538 resolving_funcs, FALSE)) { 21539 JS_FreeValue(ctx, promise); 21540 goto fail; 21541 } 21542 21543 /* Note: no need to create 'thrownawayCapability' as in 21544 the spec */ 21545 for(i = 0; i < 2; i++) 21546 resolving_funcs1[i] = JS_UNDEFINED; 21547 res = perform_promise_then(ctx, promise, 21548 (JSValueConst *)resolving_funcs, 21549 (JSValueConst *)resolving_funcs1); 21550 JS_FreeValue(ctx, promise); 21551 for(i = 0; i < 2; i++) 21552 JS_FreeValue(ctx, resolving_funcs[i]); 21553 if (res) 21554 goto fail; 21555 return 0; 21556 fail: 21557 return -1; 21558 } 21559 21560 static void js_async_generator_resolve_or_reject(JSContext *ctx, 21561 JSAsyncGeneratorData *s, 21562 JSValueConst result, 21563 int is_reject) 21564 { 21565 JSAsyncGeneratorRequest *next; 21566 JSValue ret; 21567 21568 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); 21569 list_del(&next->link); 21570 ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, 21571 &result); 21572 JS_FreeValue(ctx, ret); 21573 JS_FreeValue(ctx, next->result); 21574 JS_FreeValue(ctx, next->promise); 21575 JS_FreeValue(ctx, next->resolving_funcs[0]); 21576 JS_FreeValue(ctx, next->resolving_funcs[1]); 21577 js_free(ctx, next); 21578 } 21579 21580 static void js_async_generator_resolve(JSContext *ctx, 21581 JSAsyncGeneratorData *s, 21582 JSValueConst value, 21583 BOOL done) 21584 { 21585 JSValue result; 21586 result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); 21587 /* XXX: better exception handling ? */ 21588 js_async_generator_resolve_or_reject(ctx, s, result, 0); 21589 JS_FreeValue(ctx, result); 21590 } 21591 21592 static void js_async_generator_reject(JSContext *ctx, 21593 JSAsyncGeneratorData *s, 21594 JSValueConst exception) 21595 { 21596 js_async_generator_resolve_or_reject(ctx, s, exception, 1); 21597 } 21598 21599 static void js_async_generator_complete(JSContext *ctx, 21600 JSAsyncGeneratorData *s) 21601 { 21602 if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { 21603 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; 21604 async_func_free(ctx->rt, s->func_state); 21605 s->func_state = NULL; 21606 } 21607 } 21608 21609 static int js_async_generator_completed_return(JSContext *ctx, 21610 JSAsyncGeneratorData *s, 21611 JSValueConst value) 21612 { 21613 JSValue promise, resolving_funcs[2], resolving_funcs1[2]; 21614 int res; 21615 21616 // Can fail looking up JS_ATOM_constructor when is_reject==0. 21617 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, 21618 /*is_reject*/0); 21619 // A poisoned .constructor property is observable and the resulting 21620 // exception should be delivered to the catch handler. 21621 if (JS_IsException(promise)) { 21622 JSValue err = JS_GetException(ctx); 21623 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err, 21624 /*is_reject*/1); 21625 JS_FreeValue(ctx, err); 21626 if (JS_IsException(promise)) 21627 return -1; 21628 } 21629 if (js_async_generator_resolve_function_create(ctx, 21630 JS_MKPTR(JS_TAG_OBJECT, s->generator), 21631 resolving_funcs1, 21632 TRUE)) { 21633 JS_FreeValue(ctx, promise); 21634 return -1; 21635 } 21636 resolving_funcs[0] = JS_UNDEFINED; 21637 resolving_funcs[1] = JS_UNDEFINED; 21638 res = perform_promise_then(ctx, promise, 21639 (JSValueConst *)resolving_funcs1, 21640 (JSValueConst *)resolving_funcs); 21641 JS_FreeValue(ctx, resolving_funcs1[0]); 21642 JS_FreeValue(ctx, resolving_funcs1[1]); 21643 JS_FreeValue(ctx, promise); 21644 return res; 21645 } 21646 21647 static void js_async_generator_resume_next(JSContext *ctx, 21648 JSAsyncGeneratorData *s) 21649 { 21650 JSAsyncGeneratorRequest *next; 21651 JSValue func_ret, value; 21652 21653 for(;;) { 21654 if (list_empty(&s->queue)) 21655 break; 21656 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); 21657 switch(s->state) { 21658 case JS_ASYNC_GENERATOR_STATE_EXECUTING: 21659 /* only happens when restarting execution after await() */ 21660 goto resume_exec; 21661 case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: 21662 goto done; 21663 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: 21664 if (next->completion_type == GEN_MAGIC_NEXT) { 21665 goto exec_no_arg; 21666 } else { 21667 js_async_generator_complete(ctx, s); 21668 } 21669 break; 21670 case JS_ASYNC_GENERATOR_STATE_COMPLETED: 21671 if (next->completion_type == GEN_MAGIC_NEXT) { 21672 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); 21673 } else if (next->completion_type == GEN_MAGIC_RETURN) { 21674 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; 21675 js_async_generator_completed_return(ctx, s, next->result); 21676 } else { 21677 js_async_generator_reject(ctx, s, next->result); 21678 } 21679 goto done; 21680 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: 21681 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: 21682 value = JS_DupValue(ctx, next->result); 21683 if (next->completion_type == GEN_MAGIC_THROW && 21684 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { 21685 JS_Throw(ctx, value); 21686 s->func_state->throw_flag = TRUE; 21687 } else { 21688 /* 'yield' returns a value. 'yield *' also returns a value 21689 in case the 'throw' method is called */ 21690 s->func_state->frame.cur_sp[-1] = value; 21691 s->func_state->frame.cur_sp[0] = 21692 JS_NewInt32(ctx, next->completion_type); 21693 s->func_state->frame.cur_sp++; 21694 exec_no_arg: 21695 s->func_state->throw_flag = FALSE; 21696 } 21697 s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; 21698 resume_exec: 21699 func_ret = async_func_resume(ctx, s->func_state); 21700 if (s->func_state->is_completed) { 21701 if (JS_IsException(func_ret)) { 21702 value = JS_GetException(ctx); 21703 js_async_generator_complete(ctx, s); 21704 js_async_generator_reject(ctx, s, value); 21705 JS_FreeValue(ctx, value); 21706 } else { 21707 /* end of function */ 21708 js_async_generator_complete(ctx, s); 21709 js_async_generator_resolve(ctx, s, func_ret, TRUE); 21710 JS_FreeValue(ctx, func_ret); 21711 } 21712 } else { 21713 int func_ret_code, ret; 21714 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); 21715 func_ret_code = JS_VALUE_GET_INT(func_ret); 21716 value = s->func_state->frame.cur_sp[-1]; 21717 s->func_state->frame.cur_sp[-1] = JS_UNDEFINED; 21718 switch(func_ret_code) { 21719 case FUNC_RET_YIELD: 21720 case FUNC_RET_YIELD_STAR: 21721 if (func_ret_code == FUNC_RET_YIELD_STAR) 21722 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; 21723 else 21724 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; 21725 js_async_generator_resolve(ctx, s, value, FALSE); 21726 JS_FreeValue(ctx, value); 21727 break; 21728 case FUNC_RET_AWAIT: 21729 ret = js_async_generator_await(ctx, s, value); 21730 JS_FreeValue(ctx, value); 21731 if (ret < 0) { 21732 /* exception: throw it */ 21733 s->func_state->throw_flag = TRUE; 21734 goto resume_exec; 21735 } 21736 goto done; 21737 default: 21738 abort(); 21739 } 21740 } 21741 break; 21742 default: 21743 abort(); 21744 } 21745 } 21746 done: ; 21747 } 21748 21749 static JSValue js_async_generator_resolve_function(JSContext *ctx, 21750 JSValueConst this_obj, 21751 int argc, JSValueConst *argv, 21752 int magic, JSValue *func_data) 21753 { 21754 BOOL is_reject = magic & 1; 21755 JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); 21756 JSValueConst arg = argv[0]; 21757 21758 /* XXX: what if s == NULL */ 21759 21760 if (magic >= 2) { 21761 /* resume next case in AWAITING_RETURN state */ 21762 assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || 21763 s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); 21764 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; 21765 if (is_reject) { 21766 js_async_generator_reject(ctx, s, arg); 21767 } else { 21768 js_async_generator_resolve(ctx, s, arg, TRUE); 21769 } 21770 } else if (s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING) { 21771 /* restart function execution after await() */ 21772 s->func_state->throw_flag = is_reject; 21773 if (is_reject) { 21774 JS_Throw(ctx, JS_DupValue(ctx, arg)); 21775 } else { 21776 /* return value of await */ 21777 s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg); 21778 } 21779 js_async_generator_resume_next(ctx, s); 21780 } 21781 return JS_UNDEFINED; 21782 } 21783 21784 /* magic = GEN_MAGIC_x */ 21785 static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, 21786 int argc, JSValueConst *argv, 21787 int magic) 21788 { 21789 JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); 21790 JSValue promise, resolving_funcs[2]; 21791 JSAsyncGeneratorRequest *req; 21792 21793 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 21794 if (JS_IsException(promise)) 21795 return JS_EXCEPTION; 21796 if (!s) { 21797 JSValue err, res2; 21798 JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); 21799 err = JS_GetException(ctx); 21800 res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 21801 1, (JSValueConst *)&err); 21802 JS_FreeValue(ctx, err); 21803 JS_FreeValue(ctx, res2); 21804 JS_FreeValue(ctx, resolving_funcs[0]); 21805 JS_FreeValue(ctx, resolving_funcs[1]); 21806 return promise; 21807 } 21808 req = js_mallocz(ctx, sizeof(*req)); 21809 if (!req) 21810 goto fail; 21811 req->completion_type = magic; 21812 req->result = JS_DupValue(ctx, argv[0]); 21813 req->promise = JS_DupValue(ctx, promise); 21814 req->resolving_funcs[0] = resolving_funcs[0]; 21815 req->resolving_funcs[1] = resolving_funcs[1]; 21816 list_add_tail(&req->link, &s->queue); 21817 if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { 21818 js_async_generator_resume_next(ctx, s); 21819 } 21820 return promise; 21821 fail: 21822 JS_FreeValue(ctx, resolving_funcs[0]); 21823 JS_FreeValue(ctx, resolving_funcs[1]); 21824 JS_FreeValue(ctx, promise); 21825 return JS_EXCEPTION; 21826 } 21827 21828 static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, 21829 JSValueConst this_obj, 21830 int argc, JSValueConst *argv, 21831 int flags) 21832 { 21833 JSValue obj, func_ret; 21834 JSAsyncGeneratorData *s; 21835 21836 s = js_mallocz(ctx, sizeof(*s)); 21837 if (!s) 21838 return JS_EXCEPTION; 21839 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; 21840 init_list_head(&s->queue); 21841 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); 21842 if (!s->func_state) 21843 goto fail; 21844 /* execute the function up to 'OP_initial_yield' (no yield nor 21845 await are possible) */ 21846 func_ret = async_func_resume(ctx, s->func_state); 21847 if (JS_IsException(func_ret)) 21848 goto fail; 21849 JS_FreeValue(ctx, func_ret); 21850 21851 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); 21852 if (JS_IsException(obj)) 21853 goto fail; 21854 s->generator = JS_VALUE_GET_OBJ(obj); 21855 JS_SetOpaque(obj, s); 21856 return obj; 21857 fail: 21858 js_async_generator_free(ctx->rt, s); 21859 return JS_EXCEPTION; 21860 } 21861 21862 /* JS parser */ 21863 21864 enum { 21865 TOK_NUMBER = -128, 21866 TOK_STRING, 21867 TOK_TEMPLATE, 21868 TOK_IDENT, 21869 TOK_REGEXP, 21870 /* warning: order matters (see js_parse_assign_expr) */ 21871 TOK_MUL_ASSIGN, 21872 TOK_DIV_ASSIGN, 21873 TOK_MOD_ASSIGN, 21874 TOK_PLUS_ASSIGN, 21875 TOK_MINUS_ASSIGN, 21876 TOK_SHL_ASSIGN, 21877 TOK_SAR_ASSIGN, 21878 TOK_SHR_ASSIGN, 21879 TOK_AND_ASSIGN, 21880 TOK_XOR_ASSIGN, 21881 TOK_OR_ASSIGN, 21882 TOK_POW_ASSIGN, 21883 TOK_LAND_ASSIGN, 21884 TOK_LOR_ASSIGN, 21885 TOK_DOUBLE_QUESTION_MARK_ASSIGN, 21886 TOK_DEC, 21887 TOK_INC, 21888 TOK_SHL, 21889 TOK_SAR, 21890 TOK_SHR, 21891 TOK_LT, 21892 TOK_LTE, 21893 TOK_GT, 21894 TOK_GTE, 21895 TOK_EQ, 21896 TOK_STRICT_EQ, 21897 TOK_NEQ, 21898 TOK_STRICT_NEQ, 21899 TOK_LAND, 21900 TOK_LOR, 21901 TOK_POW, 21902 TOK_ARROW, 21903 TOK_ELLIPSIS, 21904 TOK_DOUBLE_QUESTION_MARK, 21905 TOK_QUESTION_MARK_DOT, 21906 TOK_ERROR, 21907 TOK_PRIVATE_NAME, 21908 TOK_EOF, 21909 /* keywords: WARNING: same order as atoms */ 21910 TOK_NULL, /* must be first */ 21911 TOK_FALSE, 21912 TOK_TRUE, 21913 TOK_IF, 21914 TOK_ELSE, 21915 TOK_RETURN, 21916 TOK_VAR, 21917 TOK_THIS, 21918 TOK_DELETE, 21919 TOK_VOID, 21920 TOK_TYPEOF, 21921 TOK_NEW, 21922 TOK_IN, 21923 TOK_INSTANCEOF, 21924 TOK_DO, 21925 TOK_WHILE, 21926 TOK_FOR, 21927 TOK_BREAK, 21928 TOK_CONTINUE, 21929 TOK_SWITCH, 21930 TOK_CASE, 21931 TOK_DEFAULT, 21932 TOK_THROW, 21933 TOK_TRY, 21934 TOK_CATCH, 21935 TOK_FINALLY, 21936 TOK_FUNCTION, 21937 TOK_DEBUGGER, 21938 TOK_WITH, 21939 /* FutureReservedWord */ 21940 TOK_CLASS, 21941 TOK_CONST, 21942 TOK_ENUM, 21943 TOK_EXPORT, 21944 TOK_EXTENDS, 21945 TOK_IMPORT, 21946 TOK_SUPER, 21947 /* FutureReservedWords when parsing strict mode code */ 21948 TOK_IMPLEMENTS, 21949 TOK_INTERFACE, 21950 TOK_LET, 21951 TOK_PACKAGE, 21952 TOK_PRIVATE, 21953 TOK_PROTECTED, 21954 TOK_PUBLIC, 21955 TOK_STATIC, 21956 TOK_YIELD, 21957 TOK_AWAIT, /* must be last */ 21958 TOK_OF, /* only used for js_parse_skip_parens_token() */ 21959 }; 21960 21961 #define TOK_FIRST_KEYWORD TOK_NULL 21962 #define TOK_LAST_KEYWORD TOK_AWAIT 21963 21964 /* unicode code points */ 21965 #define CP_NBSP 0x00a0 21966 #define CP_BOM 0xfeff 21967 21968 #define CP_LS 0x2028 21969 #define CP_PS 0x2029 21970 21971 typedef struct BlockEnv { 21972 struct BlockEnv *prev; 21973 JSAtom label_name; /* JS_ATOM_NULL if none */ 21974 int label_break; /* -1 if none */ 21975 int label_cont; /* -1 if none */ 21976 int drop_count; /* number of stack elements to drop */ 21977 int label_finally; /* -1 if none */ 21978 int scope_level; 21979 uint8_t has_iterator : 1; 21980 uint8_t is_regular_stmt : 1; /* i.e. not a loop statement */ 21981 } BlockEnv; 21982 21983 typedef struct JSGlobalVar { 21984 int cpool_idx; /* if >= 0, index in the constant pool for hoisted 21985 function defintion*/ 21986 uint8_t force_init : 1; /* force initialization to undefined */ 21987 uint8_t is_lexical : 1; /* global let/const definition */ 21988 uint8_t is_const : 1; /* const definition */ 21989 int scope_level; /* scope of definition */ 21990 JSAtom var_name; /* variable name */ 21991 } JSGlobalVar; 21992 21993 typedef struct RelocEntry { 21994 struct RelocEntry *next; 21995 uint32_t addr; /* address to patch */ 21996 int size; /* address size: 1, 2 or 4 bytes */ 21997 } RelocEntry; 21998 21999 typedef struct JumpSlot { 22000 int op; 22001 int size; 22002 int pos; 22003 int label; 22004 } JumpSlot; 22005 22006 typedef struct LabelSlot { 22007 int ref_count; 22008 int pos; /* phase 1 address, -1 means not resolved yet */ 22009 int pos2; /* phase 2 address, -1 means not resolved yet */ 22010 int addr; /* phase 3 address, -1 means not resolved yet */ 22011 RelocEntry *first_reloc; 22012 } LabelSlot; 22013 22014 typedef struct LineNumberSlot { 22015 uint32_t pc; 22016 uint32_t source_pos; 22017 } LineNumberSlot; 22018 22019 typedef struct { 22020 /* last source position */ 22021 const uint8_t *ptr; 22022 int line_num; 22023 int col_num; 22024 const uint8_t *buf_start; 22025 } GetLineColCache; 22026 22027 typedef enum JSParseFunctionEnum { 22028 JS_PARSE_FUNC_STATEMENT, 22029 JS_PARSE_FUNC_VAR, 22030 JS_PARSE_FUNC_EXPR, 22031 JS_PARSE_FUNC_ARROW, 22032 JS_PARSE_FUNC_GETTER, 22033 JS_PARSE_FUNC_SETTER, 22034 JS_PARSE_FUNC_METHOD, 22035 JS_PARSE_FUNC_CLASS_STATIC_INIT, 22036 JS_PARSE_FUNC_CLASS_CONSTRUCTOR, 22037 JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, 22038 } JSParseFunctionEnum; 22039 22040 typedef enum JSParseExportEnum { 22041 JS_PARSE_EXPORT_NONE, 22042 JS_PARSE_EXPORT_NAMED, 22043 JS_PARSE_EXPORT_DEFAULT, 22044 } JSParseExportEnum; 22045 22046 typedef struct JSVarScope { 22047 int parent; /* index into fd->scopes of the enclosing scope */ 22048 int first; /* index into fd->vars of the last variable in this scope */ 22049 } JSVarScope; 22050 22051 typedef struct JSVarDef { 22052 JSAtom var_name; 22053 /* index into fd->scopes of this variable lexical scope */ 22054 int scope_level; 22055 /* - if scope_level = 0: scope in which the variable is defined 22056 - if scope_level != 0: index into fd->vars of the next 22057 variable in the same or enclosing lexical scope 22058 */ 22059 int scope_next; 22060 uint8_t is_const : 1; 22061 uint8_t is_lexical : 1; 22062 uint8_t is_captured : 1; /* XXX: could remove and use a var_ref_idx value */ 22063 uint8_t is_static_private : 1; /* only used during private class field parsing */ 22064 uint8_t var_kind : 4; /* see JSVarKindEnum */ 22065 /* if is_captured = TRUE, provides, the index of the corresponding 22066 JSVarRef on stack */ 22067 uint16_t var_ref_idx; 22068 /* function pool index for lexical variables with var_kind = 22069 JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of 22070 the definition of the 'var' variables (they have scope_level = 22071 0) */ 22072 int func_pool_idx; 22073 } JSVarDef; 22074 22075 typedef struct JSFunctionDef { 22076 JSContext *ctx; 22077 struct JSFunctionDef *parent; 22078 int parent_cpool_idx; /* index in the constant pool of the parent 22079 or -1 if none */ 22080 int parent_scope_level; /* scope level in parent at point of definition */ 22081 struct list_head child_list; /* list of JSFunctionDef.link */ 22082 struct list_head link; 22083 22084 BOOL is_eval; /* TRUE if eval code */ 22085 int eval_type; /* only valid if is_eval = TRUE */ 22086 BOOL is_global_var; /* TRUE if variables are not defined locally: 22087 eval global, eval module or non strict eval */ 22088 BOOL is_func_expr; /* TRUE if function expression */ 22089 BOOL has_home_object; /* TRUE if the home object is available */ 22090 BOOL has_prototype; /* true if a prototype field is necessary */ 22091 BOOL has_simple_parameter_list; 22092 BOOL has_parameter_expressions; /* if true, an argument scope is created */ 22093 BOOL has_use_strict; /* to reject directive in special cases */ 22094 BOOL has_eval_call; /* true if the function contains a call to eval() */ 22095 BOOL has_arguments_binding; /* true if the 'arguments' binding is 22096 available in the function */ 22097 BOOL has_this_binding; /* true if the 'this' and new.target binding are 22098 available in the function */ 22099 BOOL new_target_allowed; /* true if the 'new.target' does not 22100 throw a syntax error */ 22101 BOOL super_call_allowed; /* true if super() is allowed */ 22102 BOOL super_allowed; /* true if super. or super[] is allowed */ 22103 BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ 22104 BOOL is_derived_class_constructor; 22105 BOOL in_function_body; 22106 JSFunctionKindEnum func_kind : 8; 22107 JSParseFunctionEnum func_type : 8; 22108 uint8_t js_mode; /* bitmap of JS_MODE_x */ 22109 JSAtom func_name; /* JS_ATOM_NULL if no name */ 22110 22111 JSVarDef *vars; 22112 int var_size; /* allocated size for vars[] */ 22113 int var_count; 22114 JSVarDef *args; 22115 int arg_size; /* allocated size for args[] */ 22116 int arg_count; /* number of arguments */ 22117 int defined_arg_count; 22118 int var_ref_count; /* number of local/arg variable references */ 22119 int var_object_idx; /* -1 if none */ 22120 int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ 22121 int arguments_var_idx; /* -1 if none */ 22122 int arguments_arg_idx; /* argument variable definition in argument scope, 22123 -1 if none */ 22124 int func_var_idx; /* variable containing the current function (-1 22125 if none, only used if is_func_expr is true) */ 22126 int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ 22127 int this_var_idx; /* variable containg the 'this' value, -1 if none */ 22128 int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ 22129 int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ 22130 int home_object_var_idx; 22131 BOOL need_home_object; 22132 22133 int scope_level; /* index into fd->scopes if the current lexical scope */ 22134 int scope_first; /* index into vd->vars of first lexically scoped variable */ 22135 int scope_size; /* allocated size of fd->scopes array */ 22136 int scope_count; /* number of entries used in the fd->scopes array */ 22137 JSVarScope *scopes; 22138 JSVarScope def_scope_array[4]; 22139 int body_scope; /* scope of the body of the function or eval */ 22140 22141 int global_var_count; 22142 int global_var_size; 22143 JSGlobalVar *global_vars; 22144 22145 DynBuf byte_code; 22146 int last_opcode_pos; /* -1 if no last opcode */ 22147 const uint8_t *last_opcode_source_ptr; 22148 BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ 22149 22150 LabelSlot *label_slots; 22151 int label_size; /* allocated size for label_slots[] */ 22152 int label_count; 22153 BlockEnv *top_break; /* break/continue label stack */ 22154 22155 /* constant pool (strings, functions, numbers) */ 22156 JSValue *cpool; 22157 int cpool_count; 22158 int cpool_size; 22159 22160 /* list of variables in the closure */ 22161 int closure_var_count; 22162 int closure_var_size; 22163 JSClosureVar *closure_var; 22164 22165 JumpSlot *jump_slots; 22166 int jump_size; 22167 int jump_count; 22168 22169 LineNumberSlot *line_number_slots; 22170 int line_number_size; 22171 int line_number_count; 22172 int line_number_last; 22173 int line_number_last_pc; 22174 22175 /* pc2line table */ 22176 BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */ 22177 BOOL strip_source : 1; /* strip only source code */ 22178 JSAtom filename; 22179 uint32_t source_pos; /* pointer in the eval() source */ 22180 GetLineColCache *get_line_col_cache; /* XXX: could remove to save memory */ 22181 DynBuf pc2line; 22182 22183 char *source; /* raw source, utf-8 encoded */ 22184 int source_len; 22185 22186 JSModuleDef *module; /* != NULL when parsing a module */ 22187 BOOL has_await; /* TRUE if await is used (used in module eval) */ 22188 } JSFunctionDef; 22189 22190 typedef struct JSToken { 22191 int val; 22192 const uint8_t *ptr; /* position in the source */ 22193 union { 22194 struct { 22195 JSValue str; 22196 int sep; 22197 } str; 22198 struct { 22199 JSValue val; 22200 } num; 22201 struct { 22202 JSAtom atom; 22203 BOOL has_escape; 22204 BOOL is_reserved; 22205 } ident; 22206 struct { 22207 JSValue body; 22208 JSValue flags; 22209 } regexp; 22210 } u; 22211 } JSToken; 22212 22213 typedef struct JSParseState { 22214 JSContext *ctx; 22215 const char *filename; 22216 JSToken token; 22217 BOOL got_lf; /* true if got line feed before the current token */ 22218 const uint8_t *last_ptr; 22219 const uint8_t *buf_start; 22220 const uint8_t *buf_ptr; 22221 const uint8_t *buf_end; 22222 22223 /* current function code */ 22224 JSFunctionDef *cur_func; 22225 BOOL is_module; /* parsing a module */ 22226 BOOL allow_html_comments; 22227 BOOL ext_json; /* JSON parsing: true if accepting JSON superset */ 22228 GetLineColCache get_line_col_cache; 22229 } JSParseState; 22230 22231 typedef struct JSOpCode { 22232 #ifdef DUMP_BYTECODE 22233 const char *name; 22234 #endif 22235 uint8_t size; /* in bytes */ 22236 /* the opcodes remove n_pop items from the top of the stack, then 22237 pushes n_push items */ 22238 uint8_t n_pop; 22239 uint8_t n_push; 22240 uint8_t fmt; 22241 } JSOpCode; 22242 22243 static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { 22244 #define FMT(f) 22245 #ifdef DUMP_BYTECODE 22246 #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, 22247 #else 22248 #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, 22249 #endif 22250 #include "quickjs-opcode.h" 22251 #undef DEF 22252 #undef FMT 22253 }; 22254 22255 #if SHORT_OPCODES 22256 /* After the final compilation pass, short opcodes are used. Their 22257 opcodes overlap with the temporary opcodes which cannot appear in 22258 the final bytecode. Their description is after the temporary 22259 opcodes in opcode_info[]. */ 22260 #define short_opcode_info(op) \ 22261 opcode_info[(op) >= OP_TEMP_START ? \ 22262 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] 22263 #else 22264 #define short_opcode_info(op) opcode_info[op] 22265 #endif 22266 22267 static __exception int next_token(JSParseState *s); 22268 22269 static void free_token(JSParseState *s, JSToken *token) 22270 { 22271 switch(token->val) { 22272 case TOK_NUMBER: 22273 JS_FreeValue(s->ctx, token->u.num.val); 22274 break; 22275 case TOK_STRING: 22276 case TOK_TEMPLATE: 22277 JS_FreeValue(s->ctx, token->u.str.str); 22278 break; 22279 case TOK_REGEXP: 22280 JS_FreeValue(s->ctx, token->u.regexp.body); 22281 JS_FreeValue(s->ctx, token->u.regexp.flags); 22282 break; 22283 case TOK_IDENT: 22284 case TOK_PRIVATE_NAME: 22285 JS_FreeAtom(s->ctx, token->u.ident.atom); 22286 break; 22287 default: 22288 if (token->val >= TOK_FIRST_KEYWORD && 22289 token->val <= TOK_LAST_KEYWORD) { 22290 JS_FreeAtom(s->ctx, token->u.ident.atom); 22291 } 22292 break; 22293 } 22294 } 22295 22296 static void __attribute((unused)) dump_token(JSParseState *s, 22297 const JSToken *token) 22298 { 22299 switch(token->val) { 22300 case TOK_NUMBER: 22301 { 22302 double d; 22303 JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ 22304 printf("number: %.14g\n", d); 22305 } 22306 break; 22307 case TOK_IDENT: 22308 dump_atom: 22309 { 22310 char buf[ATOM_GET_STR_BUF_SIZE]; 22311 printf("ident: '%s'\n", 22312 JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); 22313 } 22314 break; 22315 case TOK_STRING: 22316 { 22317 const char *str; 22318 /* XXX: quote the string */ 22319 str = JS_ToCString(s->ctx, token->u.str.str); 22320 printf("string: '%s'\n", str); 22321 JS_FreeCString(s->ctx, str); 22322 } 22323 break; 22324 case TOK_TEMPLATE: 22325 { 22326 const char *str; 22327 str = JS_ToCString(s->ctx, token->u.str.str); 22328 printf("template: `%s`\n", str); 22329 JS_FreeCString(s->ctx, str); 22330 } 22331 break; 22332 case TOK_REGEXP: 22333 { 22334 const char *str, *str2; 22335 str = JS_ToCString(s->ctx, token->u.regexp.body); 22336 str2 = JS_ToCString(s->ctx, token->u.regexp.flags); 22337 printf("regexp: '%s' '%s'\n", str, str2); 22338 JS_FreeCString(s->ctx, str); 22339 JS_FreeCString(s->ctx, str2); 22340 } 22341 break; 22342 case TOK_EOF: 22343 printf("eof\n"); 22344 break; 22345 default: 22346 if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { 22347 goto dump_atom; 22348 } else if (s->token.val >= 256) { 22349 printf("token: %d\n", token->val); 22350 } else { 22351 printf("token: '%c'\n", token->val); 22352 } 22353 break; 22354 } 22355 } 22356 22357 /* return the zero based line and column number in the source. */ 22358 /* Note: we no longer support '\r' as line terminator */ 22359 static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len) 22360 { 22361 int line_num, col_num, c; 22362 size_t i; 22363 22364 line_num = 0; 22365 col_num = 0; 22366 for(i = 0; i < len; i++) { 22367 c = buf[i]; 22368 if (c == '\n') { 22369 line_num++; 22370 col_num = 0; 22371 } else if (c < 0x80 || c >= 0xc0) { 22372 col_num++; 22373 } 22374 } 22375 *pcol_num = col_num; 22376 return line_num; 22377 } 22378 22379 static int get_line_col_cached(GetLineColCache *s, int *pcol_num, const uint8_t *ptr) 22380 { 22381 int line_num, col_num; 22382 if (ptr >= s->ptr) { 22383 line_num = get_line_col(&col_num, s->ptr, ptr - s->ptr); 22384 if (line_num == 0) { 22385 s->col_num += col_num; 22386 } else { 22387 s->line_num += line_num; 22388 s->col_num = col_num; 22389 } 22390 } else { 22391 line_num = get_line_col(&col_num, ptr, s->ptr - ptr); 22392 if (line_num == 0) { 22393 s->col_num -= col_num; 22394 } else { 22395 const uint8_t *p; 22396 s->line_num -= line_num; 22397 /* find the absolute column position */ 22398 col_num = 0; 22399 for(p = ptr - 1; p >= s->buf_start; p--) { 22400 if (*p == '\n') { 22401 break; 22402 } else if (*p < 0x80 || *p >= 0xc0) { 22403 col_num++; 22404 } 22405 } 22406 s->col_num = col_num; 22407 } 22408 } 22409 s->ptr = ptr; 22410 *pcol_num = s->col_num; 22411 return s->line_num; 22412 } 22413 22414 /* 'ptr' is the position of the error in the source */ 22415 static int js_parse_error_v(JSParseState *s, const uint8_t *ptr, const char *fmt, va_list ap) 22416 { 22417 JSContext *ctx = s->ctx; 22418 int line_num, col_num; 22419 line_num = get_line_col(&col_num, s->buf_start, ptr - s->buf_start); 22420 JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); 22421 build_backtrace(ctx, ctx->rt->current_exception, s->filename, 22422 line_num + 1, col_num + 1, 0); 22423 return -1; 22424 } 22425 22426 static __attribute__((format(printf, 3, 4))) int js_parse_error_pos(JSParseState *s, const uint8_t *ptr, const char *fmt, ...) 22427 { 22428 va_list ap; 22429 int ret; 22430 22431 va_start(ap, fmt); 22432 ret = js_parse_error_v(s, ptr, fmt, ap); 22433 va_end(ap); 22434 return ret; 22435 } 22436 22437 static __attribute__((format(printf, 2, 3))) int js_parse_error(JSParseState *s, const char *fmt, ...) 22438 { 22439 va_list ap; 22440 int ret; 22441 22442 va_start(ap, fmt); 22443 ret = js_parse_error_v(s, s->token.ptr, fmt, ap); 22444 va_end(ap); 22445 return ret; 22446 } 22447 22448 static int js_parse_expect(JSParseState *s, int tok) 22449 { 22450 if (s->token.val != tok) { 22451 /* XXX: dump token correctly in all cases */ 22452 return js_parse_error(s, "expecting '%c'", tok); 22453 } 22454 return next_token(s); 22455 } 22456 22457 static int js_parse_expect_semi(JSParseState *s) 22458 { 22459 if (s->token.val != ';') { 22460 /* automatic insertion of ';' */ 22461 if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { 22462 return 0; 22463 } 22464 return js_parse_error(s, "expecting '%c'", ';'); 22465 } 22466 return next_token(s); 22467 } 22468 22469 static int js_parse_error_reserved_identifier(JSParseState *s) 22470 { 22471 char buf1[ATOM_GET_STR_BUF_SIZE]; 22472 return js_parse_error(s, "'%s' is a reserved identifier", 22473 JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), 22474 s->token.u.ident.atom)); 22475 } 22476 22477 static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) 22478 { 22479 uint32_t c; 22480 StringBuffer b_s, *b = &b_s; 22481 JSValue str; 22482 22483 /* p points to the first byte of the template part */ 22484 if (string_buffer_init(s->ctx, b, 32)) 22485 goto fail; 22486 for(;;) { 22487 if (p >= s->buf_end) 22488 goto unexpected_eof; 22489 c = *p++; 22490 if (c == '`') { 22491 /* template end part */ 22492 break; 22493 } 22494 if (c == '$' && *p == '{') { 22495 /* template start or middle part */ 22496 p++; 22497 break; 22498 } 22499 if (c == '\\') { 22500 if (string_buffer_putc8(b, c)) 22501 goto fail; 22502 if (p >= s->buf_end) 22503 goto unexpected_eof; 22504 c = *p++; 22505 } 22506 /* newline sequences are normalized as single '\n' bytes */ 22507 if (c == '\r') { 22508 if (*p == '\n') 22509 p++; 22510 c = '\n'; 22511 } 22512 if (c >= 0x80) { 22513 const uint8_t *p_next; 22514 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 22515 if (c > 0x10FFFF) { 22516 js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence"); 22517 goto fail; 22518 } 22519 p = p_next; 22520 } 22521 if (string_buffer_putc(b, c)) 22522 goto fail; 22523 } 22524 str = string_buffer_end(b); 22525 if (JS_IsException(str)) 22526 return -1; 22527 s->token.val = TOK_TEMPLATE; 22528 s->token.u.str.sep = c; 22529 s->token.u.str.str = str; 22530 s->buf_ptr = p; 22531 return 0; 22532 22533 unexpected_eof: 22534 js_parse_error(s, "unexpected end of string"); 22535 fail: 22536 string_buffer_free(b); 22537 return -1; 22538 } 22539 22540 static __exception int js_parse_string(JSParseState *s, int sep, 22541 BOOL do_throw, const uint8_t *p, 22542 JSToken *token, const uint8_t **pp) 22543 { 22544 int ret; 22545 uint32_t c; 22546 StringBuffer b_s, *b = &b_s; 22547 const uint8_t *p_escape; 22548 JSValue str; 22549 22550 /* string */ 22551 if (string_buffer_init(s->ctx, b, 32)) 22552 goto fail; 22553 for(;;) { 22554 if (p >= s->buf_end) 22555 goto invalid_char; 22556 c = *p; 22557 if (c < 0x20) { 22558 if (sep == '`') { 22559 if (c == '\r') { 22560 if (p[1] == '\n') 22561 p++; 22562 c = '\n'; 22563 } 22564 /* do not update s->line_num */ 22565 } else if (c == '\n' || c == '\r') 22566 goto invalid_char; 22567 } 22568 p++; 22569 if (c == sep) 22570 break; 22571 if (c == '$' && *p == '{' && sep == '`') { 22572 /* template start or middle part */ 22573 p++; 22574 break; 22575 } 22576 if (c == '\\') { 22577 p_escape = p - 1; 22578 c = *p; 22579 /* XXX: need a specific JSON case to avoid 22580 accepting invalid escapes */ 22581 switch(c) { 22582 case '\0': 22583 if (p >= s->buf_end) 22584 goto invalid_char; 22585 p++; 22586 break; 22587 case '\'': 22588 case '\"': 22589 case '\\': 22590 p++; 22591 break; 22592 case '\r': /* accept DOS and MAC newline sequences */ 22593 if (p[1] == '\n') { 22594 p++; 22595 } 22596 /* fall thru */ 22597 case '\n': 22598 /* ignore escaped newline sequence */ 22599 p++; 22600 continue; 22601 default: 22602 if (c >= '0' && c <= '9') { 22603 if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') 22604 goto parse_escape; 22605 if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { 22606 p++; 22607 c = '\0'; 22608 } else { 22609 if (c >= '8' || sep == '`') { 22610 /* Note: according to ES2021, \8 and \9 are not 22611 accepted in strict mode or in templates. */ 22612 goto invalid_escape; 22613 } else { 22614 if (do_throw) 22615 js_parse_error_pos(s, p_escape, "octal escape sequences are not allowed in strict mode"); 22616 } 22617 goto fail; 22618 } 22619 } else if (c >= 0x80) { 22620 const uint8_t *p_next; 22621 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 22622 if (c > 0x10FFFF) { 22623 goto invalid_utf8; 22624 } 22625 p = p_next; 22626 /* LS or PS are skipped */ 22627 if (c == CP_LS || c == CP_PS) 22628 continue; 22629 } else { 22630 parse_escape: 22631 ret = lre_parse_escape(&p, TRUE); 22632 if (ret == -1) { 22633 invalid_escape: 22634 if (do_throw) 22635 js_parse_error_pos(s, p_escape, "malformed escape sequence in string literal"); 22636 goto fail; 22637 } else if (ret < 0) { 22638 /* ignore the '\' (could output a warning) */ 22639 p++; 22640 } else { 22641 c = ret; 22642 } 22643 } 22644 break; 22645 } 22646 } else if (c >= 0x80) { 22647 const uint8_t *p_next; 22648 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 22649 if (c > 0x10FFFF) 22650 goto invalid_utf8; 22651 p = p_next; 22652 } 22653 if (string_buffer_putc(b, c)) 22654 goto fail; 22655 } 22656 str = string_buffer_end(b); 22657 if (JS_IsException(str)) 22658 return -1; 22659 token->val = TOK_STRING; 22660 token->u.str.sep = c; 22661 token->u.str.str = str; 22662 *pp = p; 22663 return 0; 22664 22665 invalid_utf8: 22666 if (do_throw) 22667 js_parse_error(s, "invalid UTF-8 sequence"); 22668 goto fail; 22669 invalid_char: 22670 if (do_throw) 22671 js_parse_error(s, "unexpected end of string"); 22672 fail: 22673 string_buffer_free(b); 22674 return -1; 22675 } 22676 22677 static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { 22678 return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && 22679 !s->token.u.ident.has_escape; 22680 } 22681 22682 static __exception int js_parse_regexp(JSParseState *s) 22683 { 22684 const uint8_t *p; 22685 BOOL in_class; 22686 StringBuffer b_s, *b = &b_s; 22687 StringBuffer b2_s, *b2 = &b2_s; 22688 uint32_t c; 22689 JSValue body_str, flags_str; 22690 22691 p = s->buf_ptr; 22692 p++; 22693 in_class = FALSE; 22694 if (string_buffer_init(s->ctx, b, 32)) 22695 return -1; 22696 if (string_buffer_init(s->ctx, b2, 1)) 22697 goto fail; 22698 for(;;) { 22699 if (p >= s->buf_end) { 22700 eof_error: 22701 js_parse_error(s, "unexpected end of regexp"); 22702 goto fail; 22703 } 22704 c = *p++; 22705 if (c == '\n' || c == '\r') { 22706 goto eol_error; 22707 } else if (c == '/') { 22708 if (!in_class) 22709 break; 22710 } else if (c == '[') { 22711 in_class = TRUE; 22712 } else if (c == ']') { 22713 /* XXX: incorrect as the first character in a class */ 22714 in_class = FALSE; 22715 } else if (c == '\\') { 22716 if (string_buffer_putc8(b, c)) 22717 goto fail; 22718 c = *p++; 22719 if (c == '\n' || c == '\r') 22720 goto eol_error; 22721 else if (c == '\0' && p >= s->buf_end) 22722 goto eof_error; 22723 else if (c >= 0x80) { 22724 const uint8_t *p_next; 22725 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 22726 if (c > 0x10FFFF) { 22727 goto invalid_utf8; 22728 } 22729 p = p_next; 22730 if (c == CP_LS || c == CP_PS) 22731 goto eol_error; 22732 } 22733 } else if (c >= 0x80) { 22734 const uint8_t *p_next; 22735 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 22736 if (c > 0x10FFFF) { 22737 invalid_utf8: 22738 js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence"); 22739 goto fail; 22740 } 22741 /* LS or PS are considered as line terminator */ 22742 if (c == CP_LS || c == CP_PS) { 22743 eol_error: 22744 js_parse_error_pos(s, p - 1, "unexpected line terminator in regexp"); 22745 goto fail; 22746 } 22747 p = p_next; 22748 } 22749 if (string_buffer_putc(b, c)) 22750 goto fail; 22751 } 22752 22753 /* flags */ 22754 for(;;) { 22755 const uint8_t *p_next = p; 22756 c = *p_next++; 22757 if (c >= 0x80) { 22758 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 22759 if (c > 0x10FFFF) { 22760 p++; 22761 goto invalid_utf8; 22762 } 22763 } 22764 if (!lre_js_is_ident_next(c)) 22765 break; 22766 if (string_buffer_putc(b2, c)) 22767 goto fail; 22768 p = p_next; 22769 } 22770 22771 body_str = string_buffer_end(b); 22772 flags_str = string_buffer_end(b2); 22773 if (JS_IsException(body_str) || 22774 JS_IsException(flags_str)) { 22775 JS_FreeValue(s->ctx, body_str); 22776 JS_FreeValue(s->ctx, flags_str); 22777 return -1; 22778 } 22779 s->token.val = TOK_REGEXP; 22780 s->token.u.regexp.body = body_str; 22781 s->token.u.regexp.flags = flags_str; 22782 s->buf_ptr = p; 22783 return 0; 22784 fail: 22785 string_buffer_free(b); 22786 string_buffer_free(b2); 22787 return -1; 22788 } 22789 22790 static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, 22791 char *static_buf) 22792 { 22793 char *buf, *new_buf; 22794 size_t size, new_size; 22795 22796 buf = *pbuf; 22797 size = *psize; 22798 if (size >= (SIZE_MAX / 3) * 2) 22799 new_size = SIZE_MAX; 22800 else 22801 new_size = size + (size >> 1); 22802 if (buf == static_buf) { 22803 new_buf = js_malloc(ctx, new_size); 22804 if (!new_buf) 22805 return -1; 22806 memcpy(new_buf, buf, size); 22807 } else { 22808 new_buf = js_realloc(ctx, buf, new_size); 22809 if (!new_buf) 22810 return -1; 22811 } 22812 *pbuf = new_buf; 22813 *psize = new_size; 22814 return 0; 22815 } 22816 22817 /* convert a TOK_IDENT to a keyword when needed */ 22818 static void update_token_ident(JSParseState *s) 22819 { 22820 if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || 22821 (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && 22822 (s->cur_func->js_mode & JS_MODE_STRICT)) || 22823 (s->token.u.ident.atom == JS_ATOM_yield && 22824 ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || 22825 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && 22826 !s->cur_func->in_function_body && s->cur_func->parent && 22827 (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || 22828 (s->token.u.ident.atom == JS_ATOM_await && 22829 (s->is_module || 22830 (s->cur_func->func_kind & JS_FUNC_ASYNC) || 22831 s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || 22832 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && 22833 !s->cur_func->in_function_body && s->cur_func->parent && 22834 ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || 22835 s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { 22836 if (s->token.u.ident.has_escape) { 22837 s->token.u.ident.is_reserved = TRUE; 22838 s->token.val = TOK_IDENT; 22839 } else { 22840 /* The keywords atoms are pre allocated */ 22841 s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; 22842 } 22843 } 22844 } 22845 22846 /* if the current token is an identifier or keyword, reparse it 22847 according to the current function type */ 22848 static void reparse_ident_token(JSParseState *s) 22849 { 22850 if (s->token.val == TOK_IDENT || 22851 (s->token.val >= TOK_FIRST_KEYWORD && 22852 s->token.val <= TOK_LAST_KEYWORD)) { 22853 s->token.val = TOK_IDENT; 22854 s->token.u.ident.is_reserved = FALSE; 22855 update_token_ident(s); 22856 } 22857 } 22858 22859 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ 22860 static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, 22861 BOOL *pident_has_escape, int c, BOOL is_private) 22862 { 22863 const uint8_t *p, *p1; 22864 char ident_buf[128], *buf; 22865 size_t ident_size, ident_pos; 22866 JSAtom atom; 22867 22868 p = *pp; 22869 buf = ident_buf; 22870 ident_size = sizeof(ident_buf); 22871 ident_pos = 0; 22872 if (is_private) 22873 buf[ident_pos++] = '#'; 22874 for(;;) { 22875 p1 = p; 22876 22877 if (c < 128) { 22878 buf[ident_pos++] = c; 22879 } else { 22880 ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); 22881 } 22882 c = *p1++; 22883 if (c == '\\' && *p1 == 'u') { 22884 c = lre_parse_escape(&p1, TRUE); 22885 *pident_has_escape = TRUE; 22886 } else if (c >= 128) { 22887 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); 22888 } 22889 if (!lre_js_is_ident_next(c)) 22890 break; 22891 p = p1; 22892 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { 22893 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { 22894 atom = JS_ATOM_NULL; 22895 goto done; 22896 } 22897 } 22898 } 22899 atom = JS_NewAtomLen(s->ctx, buf, ident_pos); 22900 done: 22901 if (unlikely(buf != ident_buf)) 22902 js_free(s->ctx, buf); 22903 *pp = p; 22904 return atom; 22905 } 22906 22907 22908 static __exception int next_token(JSParseState *s) 22909 { 22910 const uint8_t *p; 22911 int c; 22912 BOOL ident_has_escape; 22913 JSAtom atom; 22914 22915 if (js_check_stack_overflow(s->ctx->rt, 0)) { 22916 return js_parse_error(s, "stack overflow"); 22917 } 22918 22919 free_token(s, &s->token); 22920 22921 p = s->last_ptr = s->buf_ptr; 22922 s->got_lf = FALSE; 22923 redo: 22924 s->token.ptr = p; 22925 c = *p; 22926 switch(c) { 22927 case 0: 22928 if (p >= s->buf_end) { 22929 s->token.val = TOK_EOF; 22930 } else { 22931 goto def_token; 22932 } 22933 break; 22934 case '`': 22935 if (js_parse_template_part(s, p + 1)) 22936 goto fail; 22937 p = s->buf_ptr; 22938 break; 22939 case '\'': 22940 case '\"': 22941 if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) 22942 goto fail; 22943 break; 22944 case '\r': /* accept DOS and MAC newline sequences */ 22945 if (p[1] == '\n') { 22946 p++; 22947 } 22948 /* fall thru */ 22949 case '\n': 22950 p++; 22951 line_terminator: 22952 s->got_lf = TRUE; 22953 goto redo; 22954 case '\f': 22955 case '\v': 22956 case ' ': 22957 case '\t': 22958 p++; 22959 goto redo; 22960 case '/': 22961 if (p[1] == '*') { 22962 /* comment */ 22963 p += 2; 22964 for(;;) { 22965 if (*p == '\0' && p >= s->buf_end) { 22966 js_parse_error(s, "unexpected end of comment"); 22967 goto fail; 22968 } 22969 if (p[0] == '*' && p[1] == '/') { 22970 p += 2; 22971 break; 22972 } 22973 if (*p == '\n' || *p == '\r') { 22974 s->got_lf = TRUE; /* considered as LF for ASI */ 22975 p++; 22976 } else if (*p >= 0x80) { 22977 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 22978 if (c == CP_LS || c == CP_PS) { 22979 s->got_lf = TRUE; /* considered as LF for ASI */ 22980 } else if (c == -1) { 22981 p++; /* skip invalid UTF-8 */ 22982 } 22983 } else { 22984 p++; 22985 } 22986 } 22987 goto redo; 22988 } else if (p[1] == '/') { 22989 /* line comment */ 22990 p += 2; 22991 skip_line_comment: 22992 for(;;) { 22993 if (*p == '\0' && p >= s->buf_end) 22994 break; 22995 if (*p == '\r' || *p == '\n') 22996 break; 22997 if (*p >= 0x80) { 22998 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 22999 /* LS or PS are considered as line terminator */ 23000 if (c == CP_LS || c == CP_PS) { 23001 break; 23002 } else if (c == -1) { 23003 p++; /* skip invalid UTF-8 */ 23004 } 23005 } else { 23006 p++; 23007 } 23008 } 23009 goto redo; 23010 } else if (p[1] == '=') { 23011 p += 2; 23012 s->token.val = TOK_DIV_ASSIGN; 23013 } else { 23014 p++; 23015 s->token.val = c; 23016 } 23017 break; 23018 case '\\': 23019 if (p[1] == 'u') { 23020 const uint8_t *p1 = p + 1; 23021 int c1 = lre_parse_escape(&p1, TRUE); 23022 if (c1 >= 0 && lre_js_is_ident_first(c1)) { 23023 c = c1; 23024 p = p1; 23025 ident_has_escape = TRUE; 23026 goto has_ident; 23027 } else { 23028 /* XXX: syntax error? */ 23029 } 23030 } 23031 goto def_token; 23032 case 'a': case 'b': case 'c': case 'd': 23033 case 'e': case 'f': case 'g': case 'h': 23034 case 'i': case 'j': case 'k': case 'l': 23035 case 'm': case 'n': case 'o': case 'p': 23036 case 'q': case 'r': case 's': case 't': 23037 case 'u': case 'v': case 'w': case 'x': 23038 case 'y': case 'z': 23039 case 'A': case 'B': case 'C': case 'D': 23040 case 'E': case 'F': case 'G': case 'H': 23041 case 'I': case 'J': case 'K': case 'L': 23042 case 'M': case 'N': case 'O': case 'P': 23043 case 'Q': case 'R': case 'S': case 'T': 23044 case 'U': case 'V': case 'W': case 'X': 23045 case 'Y': case 'Z': 23046 case '_': 23047 case '$': 23048 /* identifier */ 23049 p++; 23050 ident_has_escape = FALSE; 23051 has_ident: 23052 atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); 23053 if (atom == JS_ATOM_NULL) 23054 goto fail; 23055 s->token.u.ident.atom = atom; 23056 s->token.u.ident.has_escape = ident_has_escape; 23057 s->token.u.ident.is_reserved = FALSE; 23058 s->token.val = TOK_IDENT; 23059 update_token_ident(s); 23060 break; 23061 case '#': 23062 /* private name */ 23063 { 23064 const uint8_t *p1; 23065 p++; 23066 p1 = p; 23067 c = *p1++; 23068 if (c == '\\' && *p1 == 'u') { 23069 c = lre_parse_escape(&p1, TRUE); 23070 } else if (c >= 128) { 23071 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); 23072 } 23073 if (!lre_js_is_ident_first(c)) { 23074 js_parse_error(s, "invalid first character of private name"); 23075 goto fail; 23076 } 23077 p = p1; 23078 ident_has_escape = FALSE; /* not used */ 23079 atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); 23080 if (atom == JS_ATOM_NULL) 23081 goto fail; 23082 s->token.u.ident.atom = atom; 23083 s->token.val = TOK_PRIVATE_NAME; 23084 } 23085 break; 23086 case '.': 23087 if (p[1] == '.' && p[2] == '.') { 23088 p += 3; 23089 s->token.val = TOK_ELLIPSIS; 23090 break; 23091 } 23092 if (p[1] >= '0' && p[1] <= '9') { 23093 goto parse_number; 23094 } else { 23095 goto def_token; 23096 } 23097 break; 23098 case '0': 23099 /* in strict mode, octal literals are not accepted */ 23100 if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { 23101 js_parse_error(s, "octal literals are deprecated in strict mode"); 23102 goto fail; 23103 } 23104 goto parse_number; 23105 case '1': case '2': case '3': case '4': 23106 case '5': case '6': case '7': case '8': 23107 case '9': 23108 /* number */ 23109 parse_number: 23110 { 23111 JSValue ret; 23112 const uint8_t *p1; 23113 int flags; 23114 flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | 23115 ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; 23116 ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0, 23117 flags); 23118 if (JS_IsException(ret)) 23119 goto fail; 23120 /* reject `10instanceof Number` */ 23121 if (JS_VALUE_IS_NAN(ret) || 23122 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) { 23123 JS_FreeValue(s->ctx, ret); 23124 js_parse_error(s, "invalid number literal"); 23125 goto fail; 23126 } 23127 s->token.val = TOK_NUMBER; 23128 s->token.u.num.val = ret; 23129 } 23130 break; 23131 case '*': 23132 if (p[1] == '=') { 23133 p += 2; 23134 s->token.val = TOK_MUL_ASSIGN; 23135 } else if (p[1] == '*') { 23136 if (p[2] == '=') { 23137 p += 3; 23138 s->token.val = TOK_POW_ASSIGN; 23139 } else { 23140 p += 2; 23141 s->token.val = TOK_POW; 23142 } 23143 } else { 23144 goto def_token; 23145 } 23146 break; 23147 case '%': 23148 if (p[1] == '=') { 23149 p += 2; 23150 s->token.val = TOK_MOD_ASSIGN; 23151 } else { 23152 goto def_token; 23153 } 23154 break; 23155 case '+': 23156 if (p[1] == '=') { 23157 p += 2; 23158 s->token.val = TOK_PLUS_ASSIGN; 23159 } else if (p[1] == '+') { 23160 p += 2; 23161 s->token.val = TOK_INC; 23162 } else { 23163 goto def_token; 23164 } 23165 break; 23166 case '-': 23167 if (p[1] == '=') { 23168 p += 2; 23169 s->token.val = TOK_MINUS_ASSIGN; 23170 } else if (p[1] == '-') { 23171 if (s->allow_html_comments && p[2] == '>' && 23172 (s->got_lf || s->last_ptr == s->buf_start)) { 23173 /* Annex B: `-->` at beginning of line is an html comment end. 23174 It extends to the end of the line. 23175 */ 23176 goto skip_line_comment; 23177 } 23178 p += 2; 23179 s->token.val = TOK_DEC; 23180 } else { 23181 goto def_token; 23182 } 23183 break; 23184 case '<': 23185 if (p[1] == '=') { 23186 p += 2; 23187 s->token.val = TOK_LTE; 23188 } else if (p[1] == '<') { 23189 if (p[2] == '=') { 23190 p += 3; 23191 s->token.val = TOK_SHL_ASSIGN; 23192 } else { 23193 p += 2; 23194 s->token.val = TOK_SHL; 23195 } 23196 } else if (s->allow_html_comments && 23197 p[1] == '!' && p[2] == '-' && p[3] == '-') { 23198 /* Annex B: handle `<!--` single line html comments */ 23199 goto skip_line_comment; 23200 } else { 23201 goto def_token; 23202 } 23203 break; 23204 case '>': 23205 if (p[1] == '=') { 23206 p += 2; 23207 s->token.val = TOK_GTE; 23208 } else if (p[1] == '>') { 23209 if (p[2] == '>') { 23210 if (p[3] == '=') { 23211 p += 4; 23212 s->token.val = TOK_SHR_ASSIGN; 23213 } else { 23214 p += 3; 23215 s->token.val = TOK_SHR; 23216 } 23217 } else if (p[2] == '=') { 23218 p += 3; 23219 s->token.val = TOK_SAR_ASSIGN; 23220 } else { 23221 p += 2; 23222 s->token.val = TOK_SAR; 23223 } 23224 } else { 23225 goto def_token; 23226 } 23227 break; 23228 case '=': 23229 if (p[1] == '=') { 23230 if (p[2] == '=') { 23231 p += 3; 23232 s->token.val = TOK_STRICT_EQ; 23233 } else { 23234 p += 2; 23235 s->token.val = TOK_EQ; 23236 } 23237 } else if (p[1] == '>') { 23238 p += 2; 23239 s->token.val = TOK_ARROW; 23240 } else { 23241 goto def_token; 23242 } 23243 break; 23244 case '!': 23245 if (p[1] == '=') { 23246 if (p[2] == '=') { 23247 p += 3; 23248 s->token.val = TOK_STRICT_NEQ; 23249 } else { 23250 p += 2; 23251 s->token.val = TOK_NEQ; 23252 } 23253 } else { 23254 goto def_token; 23255 } 23256 break; 23257 case '&': 23258 if (p[1] == '=') { 23259 p += 2; 23260 s->token.val = TOK_AND_ASSIGN; 23261 } else if (p[1] == '&') { 23262 if (p[2] == '=') { 23263 p += 3; 23264 s->token.val = TOK_LAND_ASSIGN; 23265 } else { 23266 p += 2; 23267 s->token.val = TOK_LAND; 23268 } 23269 } else { 23270 goto def_token; 23271 } 23272 break; 23273 case '^': 23274 if (p[1] == '=') { 23275 p += 2; 23276 s->token.val = TOK_XOR_ASSIGN; 23277 } else { 23278 goto def_token; 23279 } 23280 break; 23281 case '|': 23282 if (p[1] == '=') { 23283 p += 2; 23284 s->token.val = TOK_OR_ASSIGN; 23285 } else if (p[1] == '|') { 23286 if (p[2] == '=') { 23287 p += 3; 23288 s->token.val = TOK_LOR_ASSIGN; 23289 } else { 23290 p += 2; 23291 s->token.val = TOK_LOR; 23292 } 23293 } else { 23294 goto def_token; 23295 } 23296 break; 23297 case '?': 23298 if (p[1] == '?') { 23299 if (p[2] == '=') { 23300 p += 3; 23301 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; 23302 } else { 23303 p += 2; 23304 s->token.val = TOK_DOUBLE_QUESTION_MARK; 23305 } 23306 } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { 23307 p += 2; 23308 s->token.val = TOK_QUESTION_MARK_DOT; 23309 } else { 23310 goto def_token; 23311 } 23312 break; 23313 default: 23314 if (c >= 128) { 23315 /* unicode value */ 23316 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 23317 switch(c) { 23318 case CP_PS: 23319 case CP_LS: 23320 /* XXX: should avoid incrementing line_number, but 23321 needed to handle HTML comments */ 23322 goto line_terminator; 23323 default: 23324 if (lre_is_space(c)) { 23325 goto redo; 23326 } else if (lre_js_is_ident_first(c)) { 23327 ident_has_escape = FALSE; 23328 goto has_ident; 23329 } else { 23330 js_parse_error(s, "unexpected character"); 23331 goto fail; 23332 } 23333 } 23334 } 23335 def_token: 23336 s->token.val = c; 23337 p++; 23338 break; 23339 } 23340 s->buf_ptr = p; 23341 23342 // dump_token(s, &s->token); 23343 return 0; 23344 23345 fail: 23346 s->token.val = TOK_ERROR; 23347 return -1; 23348 } 23349 23350 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ 23351 /* XXX: accept unicode identifiers as JSON5 ? */ 23352 static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) 23353 { 23354 const uint8_t *p; 23355 char ident_buf[128], *buf; 23356 size_t ident_size, ident_pos; 23357 JSAtom atom; 23358 23359 p = *pp; 23360 buf = ident_buf; 23361 ident_size = sizeof(ident_buf); 23362 ident_pos = 0; 23363 for(;;) { 23364 buf[ident_pos++] = c; 23365 c = *p; 23366 if (c >= 128 || !lre_is_id_continue_byte(c)) 23367 break; 23368 p++; 23369 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { 23370 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { 23371 atom = JS_ATOM_NULL; 23372 goto done; 23373 } 23374 } 23375 } 23376 atom = JS_NewAtomLen(s->ctx, buf, ident_pos); 23377 done: 23378 if (unlikely(buf != ident_buf)) 23379 js_free(s->ctx, buf); 23380 *pp = p; 23381 return atom; 23382 } 23383 23384 static int json_parse_string(JSParseState *s, const uint8_t **pp, int sep) 23385 { 23386 const uint8_t *p, *p_next; 23387 int i; 23388 uint32_t c; 23389 StringBuffer b_s, *b = &b_s; 23390 23391 if (string_buffer_init(s->ctx, b, 32)) 23392 goto fail; 23393 23394 p = *pp; 23395 for(;;) { 23396 if (p >= s->buf_end) { 23397 goto end_of_input; 23398 } 23399 c = *p++; 23400 if (c == sep) 23401 break; 23402 if (c < 0x20) { 23403 js_parse_error_pos(s, p - 1, "Bad control character in string literal"); 23404 goto fail; 23405 } 23406 if (c == '\\') { 23407 c = *p++; 23408 switch(c) { 23409 case 'b': c = '\b'; break; 23410 case 'f': c = '\f'; break; 23411 case 'n': c = '\n'; break; 23412 case 'r': c = '\r'; break; 23413 case 't': c = '\t'; break; 23414 case '\\': break; 23415 case '/': break; 23416 case 'u': 23417 c = 0; 23418 for(i = 0; i < 4; i++) { 23419 int h = from_hex(*p++); 23420 if (h < 0) { 23421 js_parse_error_pos(s, p - 1, "Bad Unicode escape"); 23422 goto fail; 23423 } 23424 c = (c << 4) | h; 23425 } 23426 break; 23427 case '\n': 23428 if (s->ext_json) 23429 continue; 23430 goto bad_escape; 23431 case 'v': 23432 if (s->ext_json) { 23433 c = '\v'; 23434 break; 23435 } 23436 goto bad_escape; 23437 default: 23438 if (c == sep) 23439 break; 23440 if (p > s->buf_end) 23441 goto end_of_input; 23442 bad_escape: 23443 js_parse_error_pos(s, p - 1, "Bad escaped character"); 23444 goto fail; 23445 } 23446 } else 23447 if (c >= 0x80) { 23448 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 23449 if (c > 0x10FFFF) { 23450 js_parse_error_pos(s, p - 1, "Bad UTF-8 sequence"); 23451 goto fail; 23452 } 23453 p = p_next; 23454 } 23455 if (string_buffer_putc(b, c)) 23456 goto fail; 23457 } 23458 s->token.val = TOK_STRING; 23459 s->token.u.str.sep = sep; 23460 s->token.u.str.str = string_buffer_end(b); 23461 *pp = p; 23462 return 0; 23463 23464 end_of_input: 23465 js_parse_error(s, "Unexpected end of JSON input"); 23466 fail: 23467 string_buffer_free(b); 23468 return -1; 23469 } 23470 23471 static int json_parse_number(JSParseState *s, const uint8_t **pp) 23472 { 23473 const uint8_t *p = *pp; 23474 const uint8_t *p_start = p; 23475 int radix; 23476 double d; 23477 JSATODTempMem atod_mem; 23478 23479 if (*p == '+' || *p == '-') 23480 p++; 23481 23482 if (!is_digit(*p)) { 23483 if (s->ext_json) { 23484 if (strstart((const char *)p, "Infinity", (const char **)&p)) { 23485 d = 1.0 / 0.0; 23486 if (*p_start == '-') 23487 d = -d; 23488 goto done; 23489 } else if (strstart((const char *)p, "NaN", (const char **)&p)) { 23490 d = NAN; 23491 goto done; 23492 } else if (*p != '.') { 23493 goto unexpected_token; 23494 } 23495 } else { 23496 goto unexpected_token; 23497 } 23498 } 23499 23500 if (p[0] == '0') { 23501 if (s->ext_json) { 23502 /* also accepts base 16, 8 and 2 prefix for integers */ 23503 radix = 10; 23504 if (p[1] == 'x' || p[1] == 'X') { 23505 p += 2; 23506 radix = 16; 23507 } else if ((p[1] == 'o' || p[1] == 'O')) { 23508 p += 2; 23509 radix = 8; 23510 } else if ((p[1] == 'b' || p[1] == 'B')) { 23511 p += 2; 23512 radix = 2; 23513 } 23514 if (radix != 10) { 23515 /* prefix is present */ 23516 if (to_digit(*p) >= radix) { 23517 unexpected_token: 23518 return js_parse_error_pos(s, p, "Unexpected token '%c'", *p); 23519 } 23520 d = js_atod((const char *)p_start, (const char **)&p, 0, 23521 JS_ATOD_INT_ONLY | JS_ATOD_ACCEPT_BIN_OCT, &atod_mem); 23522 goto done; 23523 } 23524 } 23525 if (is_digit(p[1])) 23526 return js_parse_error_pos(s, p, "Unexpected number"); 23527 } 23528 23529 while (is_digit(*p)) 23530 p++; 23531 23532 if (*p == '.') { 23533 p++; 23534 if (!is_digit(*p)) 23535 return js_parse_error_pos(s, p, "Unterminated fractional number"); 23536 while (is_digit(*p)) 23537 p++; 23538 } 23539 if (*p == 'e' || *p == 'E') { 23540 p++; 23541 if (*p == '+' || *p == '-') 23542 p++; 23543 if (!is_digit(*p)) 23544 return js_parse_error_pos(s, p, "Exponent part is missing a number"); 23545 while (is_digit(*p)) 23546 p++; 23547 } 23548 d = js_atod((const char *)p_start, NULL, 10, 0, &atod_mem); 23549 done: 23550 s->token.val = TOK_NUMBER; 23551 s->token.u.num.val = JS_NewFloat64(s->ctx, d); 23552 *pp = p; 23553 return 0; 23554 } 23555 23556 static __exception int json_next_token(JSParseState *s) 23557 { 23558 const uint8_t *p; 23559 int c; 23560 JSAtom atom; 23561 23562 if (js_check_stack_overflow(s->ctx->rt, 0)) { 23563 return js_parse_error(s, "stack overflow"); 23564 } 23565 23566 free_token(s, &s->token); 23567 23568 p = s->last_ptr = s->buf_ptr; 23569 redo: 23570 s->token.ptr = p; 23571 c = *p; 23572 switch(c) { 23573 case 0: 23574 if (p >= s->buf_end) { 23575 s->token.val = TOK_EOF; 23576 } else { 23577 goto def_token; 23578 } 23579 break; 23580 case '\'': 23581 if (!s->ext_json) { 23582 /* JSON does not accept single quoted strings */ 23583 goto def_token; 23584 } 23585 /* fall through */ 23586 case '\"': 23587 p++; 23588 if (json_parse_string(s, &p, c)) 23589 goto fail; 23590 break; 23591 case '\r': /* accept DOS and MAC newline sequences */ 23592 if (p[1] == '\n') { 23593 p++; 23594 } 23595 /* fall thru */ 23596 case '\n': 23597 p++; 23598 goto redo; 23599 case '\f': 23600 case '\v': 23601 if (!s->ext_json) { 23602 /* JSONWhitespace does not match <VT>, nor <FF> */ 23603 goto def_token; 23604 } 23605 /* fall through */ 23606 case ' ': 23607 case '\t': 23608 p++; 23609 goto redo; 23610 case '/': 23611 if (!s->ext_json) { 23612 /* JSON does not accept comments */ 23613 goto def_token; 23614 } 23615 if (p[1] == '*') { 23616 /* comment */ 23617 p += 2; 23618 for(;;) { 23619 if (*p == '\0' && p >= s->buf_end) { 23620 js_parse_error(s, "unexpected end of comment"); 23621 goto fail; 23622 } 23623 if (p[0] == '*' && p[1] == '/') { 23624 p += 2; 23625 break; 23626 } 23627 if (*p >= 0x80) { 23628 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 23629 if (c == -1) { 23630 p++; /* skip invalid UTF-8 */ 23631 } 23632 } else { 23633 p++; 23634 } 23635 } 23636 goto redo; 23637 } else if (p[1] == '/') { 23638 /* line comment */ 23639 p += 2; 23640 for(;;) { 23641 if (*p == '\0' && p >= s->buf_end) 23642 break; 23643 if (*p == '\r' || *p == '\n') 23644 break; 23645 if (*p >= 0x80) { 23646 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 23647 /* LS or PS are considered as line terminator */ 23648 if (c == CP_LS || c == CP_PS) { 23649 break; 23650 } else if (c == -1) { 23651 p++; /* skip invalid UTF-8 */ 23652 } 23653 } else { 23654 p++; 23655 } 23656 } 23657 goto redo; 23658 } else { 23659 goto def_token; 23660 } 23661 break; 23662 case 'a': case 'b': case 'c': case 'd': 23663 case 'e': case 'f': case 'g': case 'h': 23664 case 'i': case 'j': case 'k': case 'l': 23665 case 'm': case 'n': case 'o': case 'p': 23666 case 'q': case 'r': case 's': case 't': 23667 case 'u': case 'v': case 'w': case 'x': 23668 case 'y': case 'z': 23669 case 'A': case 'B': case 'C': case 'D': 23670 case 'E': case 'F': case 'G': case 'H': 23671 case 'I': case 'J': case 'K': case 'L': 23672 case 'M': case 'N': case 'O': case 'P': 23673 case 'Q': case 'R': case 'S': case 'T': 23674 case 'U': case 'V': case 'W': case 'X': 23675 case 'Y': case 'Z': 23676 case '_': 23677 case '$': 23678 p++; 23679 atom = json_parse_ident(s, &p, c); 23680 if (atom == JS_ATOM_NULL) 23681 goto fail; 23682 s->token.u.ident.atom = atom; 23683 s->token.u.ident.has_escape = FALSE; 23684 s->token.u.ident.is_reserved = FALSE; 23685 s->token.val = TOK_IDENT; 23686 break; 23687 case '+': 23688 if (!s->ext_json) 23689 goto def_token; 23690 goto parse_number; 23691 case '.': 23692 if (s->ext_json && is_digit(p[1])) 23693 goto parse_number; 23694 else 23695 goto def_token; 23696 case '-': 23697 case '0': 23698 case '1': case '2': case '3': case '4': 23699 case '5': case '6': case '7': case '8': 23700 case '9': 23701 /* number */ 23702 parse_number: 23703 if (json_parse_number(s, &p)) 23704 goto fail; 23705 break; 23706 default: 23707 if (c >= 128) { 23708 js_parse_error(s, "unexpected character"); 23709 goto fail; 23710 } 23711 def_token: 23712 s->token.val = c; 23713 p++; 23714 break; 23715 } 23716 s->buf_ptr = p; 23717 23718 // dump_token(s, &s->token); 23719 return 0; 23720 23721 fail: 23722 s->token.val = TOK_ERROR; 23723 return -1; 23724 } 23725 23726 static int match_identifier(const uint8_t *p, const char *s) { 23727 uint32_t c; 23728 while (*s) { 23729 if ((uint8_t)*s++ != *p++) 23730 return 0; 23731 } 23732 c = *p; 23733 if (c >= 128) 23734 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 23735 return !lre_js_is_ident_next(c); 23736 } 23737 23738 /* simple_next_token() is used to check for the next token in simple cases. 23739 It is only used for ':' and '=>', 'let' or 'function' look-ahead. 23740 (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule() 23741 Whitespace and comments are skipped correctly. 23742 Then the next token is analyzed, only for specific words. 23743 Return values: 23744 - '\n' if !no_line_terminator 23745 - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION 23746 - TOK_IDENT is returned for other identifiers and keywords 23747 - otherwise the next character or unicode codepoint is returned. 23748 */ 23749 static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) 23750 { 23751 const uint8_t *p; 23752 uint32_t c; 23753 23754 /* skip spaces and comments */ 23755 p = *pp; 23756 for (;;) { 23757 switch(c = *p++) { 23758 case '\r': 23759 case '\n': 23760 if (no_line_terminator) 23761 return '\n'; 23762 continue; 23763 case ' ': 23764 case '\t': 23765 case '\v': 23766 case '\f': 23767 continue; 23768 case '/': 23769 if (*p == '/') { 23770 if (no_line_terminator) 23771 return '\n'; 23772 while (*p && *p != '\r' && *p != '\n') 23773 p++; 23774 continue; 23775 } 23776 if (*p == '*') { 23777 while (*++p) { 23778 if ((*p == '\r' || *p == '\n') && no_line_terminator) 23779 return '\n'; 23780 if (*p == '*' && p[1] == '/') { 23781 p += 2; 23782 break; 23783 } 23784 } 23785 continue; 23786 } 23787 break; 23788 case '=': 23789 if (*p == '>') 23790 return TOK_ARROW; 23791 break; 23792 case 'i': 23793 if (match_identifier(p, "n")) 23794 return TOK_IN; 23795 if (match_identifier(p, "mport")) { 23796 *pp = p + 5; 23797 return TOK_IMPORT; 23798 } 23799 return TOK_IDENT; 23800 case 'o': 23801 if (match_identifier(p, "f")) 23802 return TOK_OF; 23803 return TOK_IDENT; 23804 case 'e': 23805 if (match_identifier(p, "xport")) 23806 return TOK_EXPORT; 23807 return TOK_IDENT; 23808 case 'f': 23809 if (match_identifier(p, "unction")) 23810 return TOK_FUNCTION; 23811 return TOK_IDENT; 23812 case '\\': 23813 if (*p == 'u') { 23814 if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE))) 23815 return TOK_IDENT; 23816 } 23817 break; 23818 default: 23819 if (c >= 128) { 23820 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p); 23821 if (no_line_terminator && (c == CP_PS || c == CP_LS)) 23822 return '\n'; 23823 } 23824 if (lre_is_space(c)) 23825 continue; 23826 if (lre_js_is_ident_first(c)) 23827 return TOK_IDENT; 23828 break; 23829 } 23830 return c; 23831 } 23832 } 23833 23834 static int peek_token(JSParseState *s, BOOL no_line_terminator) 23835 { 23836 const uint8_t *p = s->buf_ptr; 23837 return simple_next_token(&p, no_line_terminator); 23838 } 23839 23840 static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) 23841 { 23842 const uint8_t *p = *pp; 23843 int c; 23844 23845 if (p[0] == '#' && p[1] == '!') { 23846 p += 2; 23847 while (p < buf_end) { 23848 if (*p == '\n' || *p == '\r') { 23849 break; 23850 } else if (*p >= 0x80) { 23851 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 23852 if (c == CP_LS || c == CP_PS) { 23853 break; 23854 } else if (c == -1) { 23855 p++; /* skip invalid UTF-8 */ 23856 } 23857 } else { 23858 p++; 23859 } 23860 } 23861 *pp = p; 23862 } 23863 } 23864 23865 /* return true if 'input' contains the source of a module 23866 (heuristic). 'input' must be a zero terminated. 23867 23868 Heuristic: skip comments and expect 'import' keyword not followed 23869 by '(' or '.' or export keyword. 23870 */ 23871 BOOL JS_DetectModule(const char *input, size_t input_len) 23872 { 23873 const uint8_t *p = (const uint8_t *)input; 23874 int tok; 23875 23876 skip_shebang(&p, p + input_len); 23877 switch(simple_next_token(&p, FALSE)) { 23878 case TOK_IMPORT: 23879 tok = simple_next_token(&p, FALSE); 23880 return (tok != '.' && tok != '('); 23881 case TOK_EXPORT: 23882 return TRUE; 23883 default: 23884 return FALSE; 23885 } 23886 } 23887 23888 static inline int get_prev_opcode(JSFunctionDef *fd) { 23889 if (fd->last_opcode_pos < 0 || dbuf_error(&fd->byte_code)) 23890 return OP_invalid; 23891 else 23892 return fd->byte_code.buf[fd->last_opcode_pos]; 23893 } 23894 23895 static BOOL js_is_live_code(JSParseState *s) { 23896 switch (get_prev_opcode(s->cur_func)) { 23897 case OP_tail_call: 23898 case OP_tail_call_method: 23899 case OP_return: 23900 case OP_return_undef: 23901 case OP_return_async: 23902 case OP_throw: 23903 case OP_throw_error: 23904 case OP_goto: 23905 #if SHORT_OPCODES 23906 case OP_goto8: 23907 case OP_goto16: 23908 #endif 23909 case OP_ret: 23910 return FALSE; 23911 default: 23912 return TRUE; 23913 } 23914 } 23915 23916 static void emit_u8(JSParseState *s, uint8_t val) 23917 { 23918 dbuf_putc(&s->cur_func->byte_code, val); 23919 } 23920 23921 static void emit_u16(JSParseState *s, uint16_t val) 23922 { 23923 dbuf_put_u16(&s->cur_func->byte_code, val); 23924 } 23925 23926 static void emit_u32(JSParseState *s, uint32_t val) 23927 { 23928 dbuf_put_u32(&s->cur_func->byte_code, val); 23929 } 23930 23931 static void emit_source_pos(JSParseState *s, const uint8_t *source_ptr) 23932 { 23933 JSFunctionDef *fd = s->cur_func; 23934 DynBuf *bc = &fd->byte_code; 23935 23936 if (unlikely(fd->last_opcode_source_ptr != source_ptr)) { 23937 dbuf_putc(bc, OP_line_num); 23938 dbuf_put_u32(bc, source_ptr - s->buf_start); 23939 fd->last_opcode_source_ptr = source_ptr; 23940 } 23941 } 23942 23943 static void emit_op(JSParseState *s, uint8_t val) 23944 { 23945 JSFunctionDef *fd = s->cur_func; 23946 DynBuf *bc = &fd->byte_code; 23947 23948 fd->last_opcode_pos = bc->size; 23949 dbuf_putc(bc, val); 23950 } 23951 23952 static void emit_atom(JSParseState *s, JSAtom name) 23953 { 23954 DynBuf *bc = &s->cur_func->byte_code; 23955 if (dbuf_claim(bc, 4)) 23956 return; /* not enough memory : don't duplicate the atom */ 23957 put_u32(bc->buf + bc->size, JS_DupAtom(s->ctx, name)); 23958 bc->size += 4; 23959 } 23960 23961 static int update_label(JSFunctionDef *s, int label, int delta) 23962 { 23963 LabelSlot *ls; 23964 23965 assert(label >= 0 && label < s->label_count); 23966 ls = &s->label_slots[label]; 23967 ls->ref_count += delta; 23968 assert(ls->ref_count >= 0); 23969 return ls->ref_count; 23970 } 23971 23972 static int new_label_fd(JSFunctionDef *fd) 23973 { 23974 int label; 23975 LabelSlot *ls; 23976 23977 if (js_resize_array(fd->ctx, (void *)&fd->label_slots, 23978 sizeof(fd->label_slots[0]), 23979 &fd->label_size, fd->label_count + 1)) 23980 return -1; 23981 label = fd->label_count++; 23982 ls = &fd->label_slots[label]; 23983 ls->ref_count = 0; 23984 ls->pos = -1; 23985 ls->pos2 = -1; 23986 ls->addr = -1; 23987 ls->first_reloc = NULL; 23988 return label; 23989 } 23990 23991 static int new_label(JSParseState *s) 23992 { 23993 int label; 23994 label = new_label_fd(s->cur_func); 23995 if (unlikely(label < 0)) { 23996 dbuf_set_error(&s->cur_func->byte_code); 23997 } 23998 return label; 23999 } 24000 24001 /* don't update the last opcode and don't emit line number info */ 24002 static void emit_label_raw(JSParseState *s, int label) 24003 { 24004 emit_u8(s, OP_label); 24005 emit_u32(s, label); 24006 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; 24007 } 24008 24009 /* return the label ID offset */ 24010 static int emit_label(JSParseState *s, int label) 24011 { 24012 if (label >= 0) { 24013 emit_op(s, OP_label); 24014 emit_u32(s, label); 24015 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; 24016 return s->cur_func->byte_code.size - 4; 24017 } else { 24018 return -1; 24019 } 24020 } 24021 24022 /* return label or -1 if dead code */ 24023 static int emit_goto(JSParseState *s, int opcode, int label) 24024 { 24025 if (js_is_live_code(s)) { 24026 if (label < 0) { 24027 label = new_label(s); 24028 if (label < 0) 24029 return -1; 24030 } 24031 emit_op(s, opcode); 24032 emit_u32(s, label); 24033 s->cur_func->label_slots[label].ref_count++; 24034 return label; 24035 } 24036 return -1; 24037 } 24038 24039 /* return the constant pool index. 'val' is not duplicated. */ 24040 static int cpool_add(JSParseState *s, JSValue val) 24041 { 24042 JSFunctionDef *fd = s->cur_func; 24043 24044 if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), 24045 &fd->cpool_size, fd->cpool_count + 1)) { 24046 JS_FreeValue(s->ctx, val); 24047 return -1; 24048 } 24049 fd->cpool[fd->cpool_count++] = val; 24050 return fd->cpool_count - 1; 24051 } 24052 24053 static __exception int emit_push_const(JSParseState *s, JSValueConst val, 24054 BOOL as_atom) 24055 { 24056 int idx; 24057 24058 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { 24059 JSAtom atom; 24060 /* warning: JS_NewAtomStr frees the string value */ 24061 JS_DupValue(s->ctx, val); 24062 atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); 24063 if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { 24064 emit_op(s, OP_push_atom_value); 24065 emit_u32(s, atom); 24066 return 0; 24067 } 24068 } 24069 24070 idx = cpool_add(s, JS_DupValue(s->ctx, val)); 24071 if (idx < 0) 24072 return -1; 24073 emit_op(s, OP_push_const); 24074 emit_u32(s, idx); 24075 return 0; 24076 } 24077 24078 /* return the variable index or -1 if not found, 24079 add ARGUMENT_VAR_OFFSET for argument variables */ 24080 static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 24081 { 24082 int i; 24083 for(i = fd->arg_count; i-- > 0;) { 24084 if (fd->args[i].var_name == name) 24085 return i | ARGUMENT_VAR_OFFSET; 24086 } 24087 return -1; 24088 } 24089 24090 static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 24091 { 24092 int i; 24093 for(i = fd->var_count; i-- > 0;) { 24094 if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0) 24095 return i; 24096 } 24097 return find_arg(ctx, fd, name); 24098 } 24099 24100 /* find a variable declaration in a given scope */ 24101 static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, 24102 JSAtom name, int scope_level) 24103 { 24104 int scope_idx; 24105 for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; 24106 scope_idx = fd->vars[scope_idx].scope_next) { 24107 if (fd->vars[scope_idx].scope_level != scope_level) 24108 break; 24109 if (fd->vars[scope_idx].var_name == name) 24110 return scope_idx; 24111 } 24112 return -1; 24113 } 24114 24115 /* return true if scope == parent_scope or if scope is a child of 24116 parent_scope */ 24117 static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, 24118 int scope, int parent_scope) 24119 { 24120 while (scope >= 0) { 24121 if (scope == parent_scope) 24122 return TRUE; 24123 scope = fd->scopes[scope].parent; 24124 } 24125 return FALSE; 24126 } 24127 24128 /* find a 'var' declaration in the same scope or a child scope */ 24129 static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, 24130 JSAtom name, int scope_level) 24131 { 24132 int i; 24133 for(i = 0; i < fd->var_count; i++) { 24134 JSVarDef *vd = &fd->vars[i]; 24135 if (vd->var_name == name && vd->scope_level == 0) { 24136 if (is_child_scope(ctx, fd, vd->scope_next, 24137 scope_level)) 24138 return i; 24139 } 24140 } 24141 return -1; 24142 } 24143 24144 24145 static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) 24146 { 24147 int i; 24148 for(i = 0; i < fd->global_var_count; i++) { 24149 JSGlobalVar *hf = &fd->global_vars[i]; 24150 if (hf->var_name == name) 24151 return hf; 24152 } 24153 return NULL; 24154 24155 } 24156 24157 static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) 24158 { 24159 JSGlobalVar *hf = find_global_var(fd, name); 24160 if (hf && hf->is_lexical) 24161 return hf; 24162 else 24163 return NULL; 24164 } 24165 24166 static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, 24167 int scope_idx, BOOL check_catch_var) 24168 { 24169 while (scope_idx >= 0) { 24170 JSVarDef *vd = &fd->vars[scope_idx]; 24171 if (vd->var_name == name && 24172 (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH && 24173 check_catch_var))) 24174 return scope_idx; 24175 scope_idx = vd->scope_next; 24176 } 24177 24178 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { 24179 if (find_lexical_global_var(fd, name)) 24180 return GLOBAL_VAR_OFFSET; 24181 } 24182 return -1; 24183 } 24184 24185 static int push_scope(JSParseState *s) { 24186 if (s->cur_func) { 24187 JSFunctionDef *fd = s->cur_func; 24188 int scope = fd->scope_count; 24189 /* XXX: should check for scope overflow */ 24190 if ((fd->scope_count + 1) > fd->scope_size) { 24191 int new_size; 24192 size_t slack; 24193 JSVarScope *new_buf; 24194 /* XXX: potential arithmetic overflow */ 24195 new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2); 24196 if (fd->scopes == fd->def_scope_array) { 24197 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack); 24198 if (!new_buf) 24199 return -1; 24200 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes)); 24201 } else { 24202 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack); 24203 if (!new_buf) 24204 return -1; 24205 } 24206 new_size += slack / sizeof(*new_buf); 24207 fd->scopes = new_buf; 24208 fd->scope_size = new_size; 24209 } 24210 fd->scope_count++; 24211 fd->scopes[scope].parent = fd->scope_level; 24212 fd->scopes[scope].first = fd->scope_first; 24213 emit_op(s, OP_enter_scope); 24214 emit_u16(s, scope); 24215 return fd->scope_level = scope; 24216 } 24217 return 0; 24218 } 24219 24220 static int get_first_lexical_var(JSFunctionDef *fd, int scope) 24221 { 24222 while (scope >= 0) { 24223 int scope_idx = fd->scopes[scope].first; 24224 if (scope_idx >= 0) 24225 return scope_idx; 24226 scope = fd->scopes[scope].parent; 24227 } 24228 return -1; 24229 } 24230 24231 static void pop_scope(JSParseState *s) { 24232 if (s->cur_func) { 24233 /* disable scoped variables */ 24234 JSFunctionDef *fd = s->cur_func; 24235 int scope = fd->scope_level; 24236 emit_op(s, OP_leave_scope); 24237 emit_u16(s, scope); 24238 fd->scope_level = fd->scopes[scope].parent; 24239 fd->scope_first = get_first_lexical_var(fd, fd->scope_level); 24240 } 24241 } 24242 24243 static void close_scopes(JSParseState *s, int scope, int scope_stop) 24244 { 24245 while (scope > scope_stop) { 24246 emit_op(s, OP_leave_scope); 24247 emit_u16(s, scope); 24248 scope = s->cur_func->scopes[scope].parent; 24249 } 24250 } 24251 24252 /* return the variable index or -1 if error */ 24253 static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 24254 { 24255 JSVarDef *vd; 24256 24257 /* the local variable indexes are currently stored on 16 bits */ 24258 if (fd->var_count >= JS_MAX_LOCAL_VARS) { 24259 JS_ThrowInternalError(ctx, "too many local variables"); 24260 return -1; 24261 } 24262 if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), 24263 &fd->var_size, fd->var_count + 1)) 24264 return -1; 24265 vd = &fd->vars[fd->var_count++]; 24266 memset(vd, 0, sizeof(*vd)); 24267 vd->var_name = JS_DupAtom(ctx, name); 24268 vd->func_pool_idx = -1; 24269 return fd->var_count - 1; 24270 } 24271 24272 static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name, 24273 JSVarKindEnum var_kind) 24274 { 24275 int idx = add_var(ctx, fd, name); 24276 if (idx >= 0) { 24277 JSVarDef *vd = &fd->vars[idx]; 24278 vd->var_kind = var_kind; 24279 vd->scope_level = fd->scope_level; 24280 vd->scope_next = fd->scope_first; 24281 fd->scopes[fd->scope_level].first = idx; 24282 fd->scope_first = idx; 24283 } 24284 return idx; 24285 } 24286 24287 static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 24288 { 24289 int idx = fd->func_var_idx; 24290 if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { 24291 fd->func_var_idx = idx; 24292 fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; 24293 if (fd->js_mode & JS_MODE_STRICT) 24294 fd->vars[idx].is_const = TRUE; 24295 } 24296 return idx; 24297 } 24298 24299 static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) 24300 { 24301 int idx = fd->arguments_var_idx; 24302 if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { 24303 fd->arguments_var_idx = idx; 24304 } 24305 return idx; 24306 } 24307 24308 /* add an argument definition in the argument scope. Only needed when 24309 "eval()" may be called in the argument scope. Return 0 if OK. */ 24310 static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) 24311 { 24312 int idx; 24313 if (fd->arguments_arg_idx < 0) { 24314 idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); 24315 if (idx < 0) { 24316 /* XXX: the scope links are not fully updated. May be an 24317 issue if there are child scopes of the argument 24318 scope */ 24319 idx = add_var(ctx, fd, JS_ATOM_arguments); 24320 if (idx < 0) 24321 return -1; 24322 fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; 24323 fd->scopes[ARG_SCOPE_INDEX].first = idx; 24324 fd->vars[idx].scope_level = ARG_SCOPE_INDEX; 24325 fd->vars[idx].is_lexical = TRUE; 24326 24327 fd->arguments_arg_idx = idx; 24328 } 24329 } 24330 return 0; 24331 } 24332 24333 static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 24334 { 24335 JSVarDef *vd; 24336 24337 /* the local variable indexes are currently stored on 16 bits */ 24338 if (fd->arg_count >= JS_MAX_LOCAL_VARS) { 24339 JS_ThrowInternalError(ctx, "too many arguments"); 24340 return -1; 24341 } 24342 if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), 24343 &fd->arg_size, fd->arg_count + 1)) 24344 return -1; 24345 vd = &fd->args[fd->arg_count++]; 24346 memset(vd, 0, sizeof(*vd)); 24347 vd->var_name = JS_DupAtom(ctx, name); 24348 vd->func_pool_idx = -1; 24349 return fd->arg_count - 1; 24350 } 24351 24352 /* add a global variable definition */ 24353 static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, 24354 JSAtom name) 24355 { 24356 JSGlobalVar *hf; 24357 24358 if (js_resize_array(ctx, (void **)&s->global_vars, 24359 sizeof(s->global_vars[0]), 24360 &s->global_var_size, s->global_var_count + 1)) 24361 return NULL; 24362 hf = &s->global_vars[s->global_var_count++]; 24363 hf->cpool_idx = -1; 24364 hf->force_init = FALSE; 24365 hf->is_lexical = FALSE; 24366 hf->is_const = FALSE; 24367 hf->scope_level = s->scope_level; 24368 hf->var_name = JS_DupAtom(ctx, name); 24369 return hf; 24370 } 24371 24372 typedef enum { 24373 JS_VAR_DEF_WITH, 24374 JS_VAR_DEF_LET, 24375 JS_VAR_DEF_CONST, 24376 JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ 24377 JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ 24378 JS_VAR_DEF_CATCH, 24379 JS_VAR_DEF_VAR, 24380 } JSVarDefEnum; 24381 24382 static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, 24383 JSVarDefEnum var_def_type) 24384 { 24385 JSContext *ctx = s->ctx; 24386 JSVarDef *vd; 24387 int idx; 24388 24389 switch (var_def_type) { 24390 case JS_VAR_DEF_WITH: 24391 idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL); 24392 break; 24393 24394 case JS_VAR_DEF_LET: 24395 case JS_VAR_DEF_CONST: 24396 case JS_VAR_DEF_FUNCTION_DECL: 24397 case JS_VAR_DEF_NEW_FUNCTION_DECL: 24398 idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE); 24399 if (idx >= 0) { 24400 if (idx < GLOBAL_VAR_OFFSET) { 24401 if (fd->vars[idx].scope_level == fd->scope_level) { 24402 /* same scope: in non strict mode, functions 24403 can be redefined (annex B.3.3.4). */ 24404 if (!(!(fd->js_mode & JS_MODE_STRICT) && 24405 var_def_type == JS_VAR_DEF_FUNCTION_DECL && 24406 fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) { 24407 goto redef_lex_error; 24408 } 24409 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) { 24410 goto redef_lex_error; 24411 } 24412 } else { 24413 if (fd->scope_level == fd->body_scope) { 24414 redef_lex_error: 24415 /* redefining a scoped var in the same scope: error */ 24416 return js_parse_error(s, "invalid redefinition of lexical identifier"); 24417 } 24418 } 24419 } 24420 if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && 24421 var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && 24422 fd->scope_level == fd->body_scope && 24423 find_arg(ctx, fd, name) >= 0) { 24424 /* lexical variable redefines a parameter name */ 24425 return js_parse_error(s, "invalid redefinition of parameter name"); 24426 } 24427 24428 if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) { 24429 return js_parse_error(s, "invalid redefinition of a variable"); 24430 } 24431 24432 if (fd->is_global_var) { 24433 JSGlobalVar *hf; 24434 hf = find_global_var(fd, name); 24435 if (hf && is_child_scope(ctx, fd, hf->scope_level, 24436 fd->scope_level)) { 24437 return js_parse_error(s, "invalid redefinition of global identifier"); 24438 } 24439 } 24440 24441 if (fd->is_eval && 24442 (fd->eval_type == JS_EVAL_TYPE_GLOBAL || 24443 fd->eval_type == JS_EVAL_TYPE_MODULE) && 24444 fd->scope_level == fd->body_scope) { 24445 JSGlobalVar *hf; 24446 hf = add_global_var(s->ctx, fd, name); 24447 if (!hf) 24448 return -1; 24449 hf->is_lexical = TRUE; 24450 hf->is_const = (var_def_type == JS_VAR_DEF_CONST); 24451 idx = GLOBAL_VAR_OFFSET; 24452 } else { 24453 JSVarKindEnum var_kind; 24454 if (var_def_type == JS_VAR_DEF_FUNCTION_DECL) 24455 var_kind = JS_VAR_FUNCTION_DECL; 24456 else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL) 24457 var_kind = JS_VAR_NEW_FUNCTION_DECL; 24458 else 24459 var_kind = JS_VAR_NORMAL; 24460 idx = add_scope_var(ctx, fd, name, var_kind); 24461 if (idx >= 0) { 24462 vd = &fd->vars[idx]; 24463 vd->is_lexical = 1; 24464 vd->is_const = (var_def_type == JS_VAR_DEF_CONST); 24465 } 24466 } 24467 break; 24468 24469 case JS_VAR_DEF_CATCH: 24470 idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH); 24471 break; 24472 24473 case JS_VAR_DEF_VAR: 24474 if (find_lexical_decl(ctx, fd, name, fd->scope_first, 24475 FALSE) >= 0) { 24476 invalid_lexical_redefinition: 24477 /* error to redefine a var that inside a lexical scope */ 24478 return js_parse_error(s, "invalid redefinition of lexical identifier"); 24479 } 24480 if (fd->is_global_var) { 24481 JSGlobalVar *hf; 24482 hf = find_global_var(fd, name); 24483 if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && 24484 fd->eval_type == JS_EVAL_TYPE_MODULE) { 24485 goto invalid_lexical_redefinition; 24486 } 24487 hf = add_global_var(s->ctx, fd, name); 24488 if (!hf) 24489 return -1; 24490 idx = GLOBAL_VAR_OFFSET; 24491 } else { 24492 /* if the variable already exists, don't add it again */ 24493 idx = find_var(ctx, fd, name); 24494 if (idx >= 0) 24495 break; 24496 idx = add_var(ctx, fd, name); 24497 if (idx >= 0) { 24498 if (name == JS_ATOM_arguments && fd->has_arguments_binding) 24499 fd->arguments_var_idx = idx; 24500 fd->vars[idx].scope_next = fd->scope_level; 24501 } 24502 } 24503 break; 24504 default: 24505 abort(); 24506 } 24507 return idx; 24508 } 24509 24510 /* add a private field variable in the current scope */ 24511 static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, 24512 JSAtom name, JSVarKindEnum var_kind, BOOL is_static) 24513 { 24514 JSContext *ctx = s->ctx; 24515 JSVarDef *vd; 24516 int idx; 24517 24518 idx = add_scope_var(ctx, fd, name, var_kind); 24519 if (idx < 0) 24520 return idx; 24521 vd = &fd->vars[idx]; 24522 vd->is_lexical = 1; 24523 vd->is_const = 1; 24524 vd->is_static_private = is_static; 24525 return idx; 24526 } 24527 24528 static __exception int js_parse_expr(JSParseState *s); 24529 static __exception int js_parse_function_decl(JSParseState *s, 24530 JSParseFunctionEnum func_type, 24531 JSFunctionKindEnum func_kind, 24532 JSAtom func_name, const uint8_t *ptr); 24533 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); 24534 static __exception int js_parse_function_decl2(JSParseState *s, 24535 JSParseFunctionEnum func_type, 24536 JSFunctionKindEnum func_kind, 24537 JSAtom func_name, 24538 const uint8_t *ptr, 24539 JSParseExportEnum export_flag, 24540 JSFunctionDef **pfd); 24541 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); 24542 static __exception int js_parse_assign_expr(JSParseState *s); 24543 static __exception int js_parse_unary(JSParseState *s, int parse_flags); 24544 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, 24545 JSAtom label_name, 24546 int label_break, int label_cont, 24547 int drop_count); 24548 static void pop_break_entry(JSFunctionDef *fd); 24549 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, 24550 JSAtom local_name, JSAtom export_name, 24551 JSExportTypeEnum export_type); 24552 24553 /* Note: all the fields are already sealed except length */ 24554 static int seal_template_obj(JSContext *ctx, JSValueConst obj) 24555 { 24556 JSObject *p; 24557 JSShapeProperty *prs; 24558 24559 p = JS_VALUE_GET_OBJ(obj); 24560 prs = find_own_property1(p, JS_ATOM_length); 24561 if (prs) { 24562 if (js_update_property_flags(ctx, p, &prs, 24563 prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE))) 24564 return -1; 24565 } 24566 p->extensible = FALSE; 24567 return 0; 24568 } 24569 24570 static __exception int js_parse_template(JSParseState *s, int call, int *argc) 24571 { 24572 JSContext *ctx = s->ctx; 24573 JSValue raw_array, template_object; 24574 JSToken cooked; 24575 int depth, ret; 24576 24577 raw_array = JS_UNDEFINED; /* avoid warning */ 24578 template_object = JS_UNDEFINED; /* avoid warning */ 24579 if (call) { 24580 /* Create a template object: an array of cooked strings */ 24581 /* Create an array of raw strings and store it to the raw property */ 24582 template_object = JS_NewArray(ctx); 24583 if (JS_IsException(template_object)) 24584 return -1; 24585 // pool_idx = s->cur_func->cpool_count; 24586 ret = emit_push_const(s, template_object, 0); 24587 JS_FreeValue(ctx, template_object); 24588 if (ret) 24589 return -1; 24590 raw_array = JS_NewArray(ctx); 24591 if (JS_IsException(raw_array)) 24592 return -1; 24593 if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw, 24594 raw_array, JS_PROP_THROW) < 0) { 24595 return -1; 24596 } 24597 } 24598 24599 depth = 0; 24600 while (s->token.val == TOK_TEMPLATE) { 24601 const uint8_t *p = s->token.ptr + 1; 24602 cooked = s->token; 24603 if (call) { 24604 if (JS_DefinePropertyValueUint32(ctx, raw_array, depth, 24605 JS_DupValue(ctx, s->token.u.str.str), 24606 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { 24607 return -1; 24608 } 24609 /* re-parse the string with escape sequences but do not throw a 24610 syntax error if it contains invalid sequences 24611 */ 24612 if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) { 24613 cooked.u.str.str = JS_UNDEFINED; 24614 } 24615 if (JS_DefinePropertyValueUint32(ctx, template_object, depth, 24616 cooked.u.str.str, 24617 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { 24618 return -1; 24619 } 24620 } else { 24621 JSString *str; 24622 /* re-parse the string with escape sequences and throw a 24623 syntax error if it contains invalid sequences 24624 */ 24625 JS_FreeValue(ctx, s->token.u.str.str); 24626 s->token.u.str.str = JS_UNDEFINED; 24627 if (js_parse_string(s, '`', TRUE, p, &cooked, &p)) 24628 return -1; 24629 str = JS_VALUE_GET_STRING(cooked.u.str.str); 24630 if (str->len != 0 || depth == 0) { 24631 ret = emit_push_const(s, cooked.u.str.str, 1); 24632 JS_FreeValue(s->ctx, cooked.u.str.str); 24633 if (ret) 24634 return -1; 24635 if (depth == 0) { 24636 if (s->token.u.str.sep == '`') 24637 goto done1; 24638 emit_op(s, OP_get_field2); 24639 emit_atom(s, JS_ATOM_concat); 24640 } 24641 depth++; 24642 } else { 24643 JS_FreeValue(s->ctx, cooked.u.str.str); 24644 } 24645 } 24646 if (s->token.u.str.sep == '`') 24647 goto done; 24648 if (next_token(s)) 24649 return -1; 24650 if (js_parse_expr(s)) 24651 return -1; 24652 depth++; 24653 if (s->token.val != '}') { 24654 return js_parse_error(s, "expected '}' after template expression"); 24655 } 24656 /* XXX: should convert to string at this stage? */ 24657 free_token(s, &s->token); 24658 /* Resume TOK_TEMPLATE parsing (s->token.line_num and 24659 * s->token.ptr are OK) */ 24660 s->got_lf = FALSE; 24661 if (js_parse_template_part(s, s->buf_ptr)) 24662 return -1; 24663 } 24664 return js_parse_expect(s, TOK_TEMPLATE); 24665 24666 done: 24667 if (call) { 24668 /* Seal the objects */ 24669 seal_template_obj(ctx, raw_array); 24670 seal_template_obj(ctx, template_object); 24671 *argc = depth + 1; 24672 } else { 24673 emit_op(s, OP_call_method); 24674 emit_u16(s, depth - 1); 24675 } 24676 done1: 24677 return next_token(s); 24678 } 24679 24680 24681 #define PROP_TYPE_IDENT 0 24682 #define PROP_TYPE_VAR 1 24683 #define PROP_TYPE_GET 2 24684 #define PROP_TYPE_SET 3 24685 #define PROP_TYPE_STAR 4 24686 #define PROP_TYPE_ASYNC 5 24687 #define PROP_TYPE_ASYNC_STAR 6 24688 24689 #define PROP_TYPE_PRIVATE (1 << 4) 24690 24691 static BOOL token_is_ident(int tok) 24692 { 24693 /* Accept keywords and reserved words as property names */ 24694 return (tok == TOK_IDENT || 24695 (tok >= TOK_FIRST_KEYWORD && 24696 tok <= TOK_LAST_KEYWORD)); 24697 } 24698 24699 /* if the property is an expression, name = JS_ATOM_NULL */ 24700 static int __exception js_parse_property_name(JSParseState *s, 24701 JSAtom *pname, 24702 BOOL allow_method, BOOL allow_var, 24703 BOOL allow_private) 24704 { 24705 int is_private = 0; 24706 BOOL is_non_reserved_ident; 24707 JSAtom name; 24708 int prop_type; 24709 24710 prop_type = PROP_TYPE_IDENT; 24711 if (allow_method) { 24712 /* if allow_private is true (for class field parsing) and 24713 get/set is following by ';' (or LF with ASI), then it 24714 is a field name */ 24715 if ((token_is_pseudo_keyword(s, JS_ATOM_get) || 24716 token_is_pseudo_keyword(s, JS_ATOM_set)) && 24717 (!allow_private || peek_token(s, TRUE) != '\n')) { 24718 /* get x(), set x() */ 24719 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24720 if (next_token(s)) 24721 goto fail1; 24722 if (s->token.val == ':' || s->token.val == ',' || 24723 s->token.val == '}' || s->token.val == '(' || 24724 s->token.val == '=' || 24725 (s->token.val == ';' && allow_private)) { 24726 is_non_reserved_ident = TRUE; 24727 goto ident_found; 24728 } 24729 prop_type = PROP_TYPE_GET + (name == JS_ATOM_set); 24730 JS_FreeAtom(s->ctx, name); 24731 } else if (s->token.val == '*') { 24732 if (next_token(s)) 24733 goto fail; 24734 prop_type = PROP_TYPE_STAR; 24735 } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && 24736 peek_token(s, TRUE) != '\n') { 24737 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24738 if (next_token(s)) 24739 goto fail1; 24740 if (s->token.val == ':' || s->token.val == ',' || 24741 s->token.val == '}' || s->token.val == '(' || 24742 s->token.val == '=') { 24743 is_non_reserved_ident = TRUE; 24744 goto ident_found; 24745 } 24746 JS_FreeAtom(s->ctx, name); 24747 if (s->token.val == '*') { 24748 if (next_token(s)) 24749 goto fail; 24750 prop_type = PROP_TYPE_ASYNC_STAR; 24751 } else { 24752 prop_type = PROP_TYPE_ASYNC; 24753 } 24754 } 24755 } 24756 24757 if (token_is_ident(s->token.val)) { 24758 /* variable can only be a non-reserved identifier */ 24759 is_non_reserved_ident = 24760 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved); 24761 /* keywords and reserved words have a valid atom */ 24762 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24763 if (next_token(s)) 24764 goto fail1; 24765 ident_found: 24766 if (is_non_reserved_ident && 24767 prop_type == PROP_TYPE_IDENT && allow_var) { 24768 if (!(s->token.val == ':' || 24769 (s->token.val == '(' && allow_method))) { 24770 prop_type = PROP_TYPE_VAR; 24771 } 24772 } 24773 } else if (s->token.val == TOK_STRING) { 24774 name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 24775 if (name == JS_ATOM_NULL) 24776 goto fail; 24777 if (next_token(s)) 24778 goto fail1; 24779 } else if (s->token.val == TOK_NUMBER) { 24780 JSValue val; 24781 val = s->token.u.num.val; 24782 name = JS_ValueToAtom(s->ctx, val); 24783 if (name == JS_ATOM_NULL) 24784 goto fail; 24785 if (next_token(s)) 24786 goto fail1; 24787 } else if (s->token.val == '[') { 24788 if (next_token(s)) 24789 goto fail; 24790 if (js_parse_assign_expr(s)) 24791 goto fail; 24792 if (js_parse_expect(s, ']')) 24793 goto fail; 24794 name = JS_ATOM_NULL; 24795 } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) { 24796 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24797 if (next_token(s)) 24798 goto fail1; 24799 is_private = PROP_TYPE_PRIVATE; 24800 } else { 24801 goto invalid_prop; 24802 } 24803 if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR && 24804 s->token.val != '(') { 24805 JS_FreeAtom(s->ctx, name); 24806 invalid_prop: 24807 js_parse_error(s, "invalid property name"); 24808 goto fail; 24809 } 24810 *pname = name; 24811 return prop_type | is_private; 24812 fail1: 24813 JS_FreeAtom(s->ctx, name); 24814 fail: 24815 *pname = JS_ATOM_NULL; 24816 return -1; 24817 } 24818 24819 typedef struct JSParsePos { 24820 BOOL got_lf; 24821 const uint8_t *ptr; 24822 } JSParsePos; 24823 24824 static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) 24825 { 24826 sp->ptr = s->token.ptr; 24827 sp->got_lf = s->got_lf; 24828 return 0; 24829 } 24830 24831 static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) 24832 { 24833 s->buf_ptr = sp->ptr; 24834 s->got_lf = sp->got_lf; 24835 return next_token(s); 24836 } 24837 24838 /* return TRUE if a regexp literal is allowed after this token */ 24839 static BOOL is_regexp_allowed(int tok) 24840 { 24841 switch (tok) { 24842 case TOK_NUMBER: 24843 case TOK_STRING: 24844 case TOK_REGEXP: 24845 case TOK_DEC: 24846 case TOK_INC: 24847 case TOK_NULL: 24848 case TOK_FALSE: 24849 case TOK_TRUE: 24850 case TOK_THIS: 24851 case ')': 24852 case ']': 24853 case '}': /* XXX: regexp may occur after */ 24854 case TOK_IDENT: 24855 return FALSE; 24856 default: 24857 return TRUE; 24858 } 24859 } 24860 24861 #define SKIP_HAS_SEMI (1 << 0) 24862 #define SKIP_HAS_ELLIPSIS (1 << 1) 24863 #define SKIP_HAS_ASSIGNMENT (1 << 2) 24864 24865 static BOOL has_lf_in_range(const uint8_t *p1, const uint8_t *p2) 24866 { 24867 const uint8_t *tmp; 24868 if (p1 > p2) { 24869 tmp = p1; 24870 p1 = p2; 24871 p2 = tmp; 24872 } 24873 return (memchr(p1, '\n', p2 - p1) != NULL); 24874 } 24875 24876 /* XXX: improve speed with early bailout */ 24877 /* XXX: no longer works if regexps are present. Could use previous 24878 regexp parsing heuristics to handle most cases */ 24879 static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator) 24880 { 24881 char state[256]; 24882 size_t level = 0; 24883 JSParsePos pos; 24884 int last_tok, tok = TOK_EOF; 24885 int c, tok_len, bits = 0; 24886 const uint8_t *last_token_ptr; 24887 24888 /* protect from underflow */ 24889 state[level++] = 0; 24890 24891 js_parse_get_pos(s, &pos); 24892 last_tok = 0; 24893 for (;;) { 24894 switch(s->token.val) { 24895 case '(': 24896 case '[': 24897 case '{': 24898 if (level >= sizeof(state)) 24899 goto done; 24900 state[level++] = s->token.val; 24901 break; 24902 case ')': 24903 if (state[--level] != '(') 24904 goto done; 24905 break; 24906 case ']': 24907 if (state[--level] != '[') 24908 goto done; 24909 break; 24910 case '}': 24911 c = state[--level]; 24912 if (c == '`') { 24913 /* continue the parsing of the template */ 24914 free_token(s, &s->token); 24915 /* Resume TOK_TEMPLATE parsing (s->token.line_num and 24916 * s->token.ptr are OK) */ 24917 s->got_lf = FALSE; 24918 if (js_parse_template_part(s, s->buf_ptr)) 24919 goto done; 24920 goto handle_template; 24921 } else if (c != '{') { 24922 goto done; 24923 } 24924 break; 24925 case TOK_TEMPLATE: 24926 handle_template: 24927 if (s->token.u.str.sep != '`') { 24928 /* '${' inside the template : closing '}' and continue 24929 parsing the template */ 24930 if (level >= sizeof(state)) 24931 goto done; 24932 state[level++] = '`'; 24933 } 24934 break; 24935 case TOK_EOF: 24936 goto done; 24937 case ';': 24938 if (level == 2) { 24939 bits |= SKIP_HAS_SEMI; 24940 } 24941 break; 24942 case TOK_ELLIPSIS: 24943 if (level == 2) { 24944 bits |= SKIP_HAS_ELLIPSIS; 24945 } 24946 break; 24947 case '=': 24948 bits |= SKIP_HAS_ASSIGNMENT; 24949 break; 24950 24951 case TOK_DIV_ASSIGN: 24952 tok_len = 2; 24953 goto parse_regexp; 24954 case '/': 24955 tok_len = 1; 24956 parse_regexp: 24957 if (is_regexp_allowed(last_tok)) { 24958 s->buf_ptr -= tok_len; 24959 if (js_parse_regexp(s)) { 24960 /* XXX: should clear the exception */ 24961 goto done; 24962 } 24963 } 24964 break; 24965 } 24966 /* last_tok is only used to recognize regexps */ 24967 if (s->token.val == TOK_IDENT && 24968 (token_is_pseudo_keyword(s, JS_ATOM_of) || 24969 token_is_pseudo_keyword(s, JS_ATOM_yield))) { 24970 last_tok = TOK_OF; 24971 } else { 24972 last_tok = s->token.val; 24973 } 24974 last_token_ptr = s->token.ptr; 24975 if (next_token(s)) { 24976 /* XXX: should clear the exception generated by next_token() */ 24977 break; 24978 } 24979 if (level <= 1) { 24980 tok = s->token.val; 24981 if (token_is_pseudo_keyword(s, JS_ATOM_of)) 24982 tok = TOK_OF; 24983 if (no_line_terminator && has_lf_in_range(last_token_ptr, s->token.ptr)) 24984 tok = '\n'; 24985 break; 24986 } 24987 } 24988 done: 24989 if (pbits) { 24990 *pbits = bits; 24991 } 24992 if (js_parse_seek_token(s, &pos)) 24993 return -1; 24994 return tok; 24995 } 24996 24997 static void set_object_name(JSParseState *s, JSAtom name) 24998 { 24999 JSFunctionDef *fd = s->cur_func; 25000 int opcode; 25001 25002 opcode = get_prev_opcode(fd); 25003 if (opcode == OP_set_name) { 25004 /* XXX: should free atom after OP_set_name? */ 25005 fd->byte_code.size = fd->last_opcode_pos; 25006 fd->last_opcode_pos = -1; 25007 emit_op(s, OP_set_name); 25008 emit_atom(s, name); 25009 } else if (opcode == OP_set_class_name) { 25010 int define_class_pos; 25011 JSAtom atom; 25012 define_class_pos = fd->last_opcode_pos + 1 - 25013 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 25014 assert(fd->byte_code.buf[define_class_pos] == OP_define_class); 25015 /* for consistency we free the previous atom which is 25016 JS_ATOM_empty_string */ 25017 atom = get_u32(fd->byte_code.buf + define_class_pos + 1); 25018 JS_FreeAtom(s->ctx, atom); 25019 put_u32(fd->byte_code.buf + define_class_pos + 1, 25020 JS_DupAtom(s->ctx, name)); 25021 fd->last_opcode_pos = -1; 25022 } 25023 } 25024 25025 static void set_object_name_computed(JSParseState *s) 25026 { 25027 JSFunctionDef *fd = s->cur_func; 25028 int opcode; 25029 25030 opcode = get_prev_opcode(fd); 25031 if (opcode == OP_set_name) { 25032 /* XXX: should free atom after OP_set_name? */ 25033 fd->byte_code.size = fd->last_opcode_pos; 25034 fd->last_opcode_pos = -1; 25035 emit_op(s, OP_set_name_computed); 25036 } else if (opcode == OP_set_class_name) { 25037 int define_class_pos; 25038 define_class_pos = fd->last_opcode_pos + 1 - 25039 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 25040 assert(fd->byte_code.buf[define_class_pos] == OP_define_class); 25041 fd->byte_code.buf[define_class_pos] = OP_define_class_computed; 25042 fd->last_opcode_pos = -1; 25043 } 25044 } 25045 25046 static __exception int js_parse_object_literal(JSParseState *s) 25047 { 25048 JSAtom name = JS_ATOM_NULL; 25049 const uint8_t *start_ptr; 25050 int prop_type; 25051 BOOL has_proto; 25052 25053 if (next_token(s)) 25054 goto fail; 25055 /* XXX: add an initial length that will be patched back */ 25056 emit_op(s, OP_object); 25057 has_proto = FALSE; 25058 while (s->token.val != '}') { 25059 /* specific case for getter/setter */ 25060 start_ptr = s->token.ptr; 25061 25062 if (s->token.val == TOK_ELLIPSIS) { 25063 if (next_token(s)) 25064 return -1; 25065 if (js_parse_assign_expr(s)) 25066 return -1; 25067 emit_op(s, OP_null); /* dummy excludeList */ 25068 emit_op(s, OP_copy_data_properties); 25069 emit_u8(s, 2 | (1 << 2) | (0 << 5)); 25070 emit_op(s, OP_drop); /* pop excludeList */ 25071 emit_op(s, OP_drop); /* pop src object */ 25072 goto next; 25073 } 25074 25075 prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE); 25076 if (prop_type < 0) 25077 goto fail; 25078 25079 if (prop_type == PROP_TYPE_VAR) { 25080 /* shortcut for x: x */ 25081 emit_op(s, OP_scope_get_var); 25082 emit_atom(s, name); 25083 emit_u16(s, s->cur_func->scope_level); 25084 emit_op(s, OP_define_field); 25085 emit_atom(s, name); 25086 } else if (s->token.val == '(') { 25087 BOOL is_getset = (prop_type == PROP_TYPE_GET || 25088 prop_type == PROP_TYPE_SET); 25089 JSParseFunctionEnum func_type; 25090 JSFunctionKindEnum func_kind; 25091 int op_flags; 25092 25093 func_kind = JS_FUNC_NORMAL; 25094 if (is_getset) { 25095 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET; 25096 } else { 25097 func_type = JS_PARSE_FUNC_METHOD; 25098 if (prop_type == PROP_TYPE_STAR) 25099 func_kind = JS_FUNC_GENERATOR; 25100 else if (prop_type == PROP_TYPE_ASYNC) 25101 func_kind = JS_FUNC_ASYNC; 25102 else if (prop_type == PROP_TYPE_ASYNC_STAR) 25103 func_kind = JS_FUNC_ASYNC_GENERATOR; 25104 } 25105 if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, 25106 start_ptr)) 25107 goto fail; 25108 if (name == JS_ATOM_NULL) { 25109 emit_op(s, OP_define_method_computed); 25110 } else { 25111 emit_op(s, OP_define_method); 25112 emit_atom(s, name); 25113 } 25114 if (is_getset) { 25115 op_flags = OP_DEFINE_METHOD_GETTER + 25116 prop_type - PROP_TYPE_GET; 25117 } else { 25118 op_flags = OP_DEFINE_METHOD_METHOD; 25119 } 25120 emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); 25121 } else { 25122 if (name == JS_ATOM_NULL) { 25123 /* must be done before evaluating expr */ 25124 emit_op(s, OP_to_propkey); 25125 } 25126 if (js_parse_expect(s, ':')) 25127 goto fail; 25128 if (js_parse_assign_expr(s)) 25129 goto fail; 25130 if (name == JS_ATOM_NULL) { 25131 set_object_name_computed(s); 25132 emit_op(s, OP_define_array_el); 25133 emit_op(s, OP_drop); 25134 } else if (name == JS_ATOM___proto__) { 25135 if (has_proto) { 25136 js_parse_error(s, "duplicate __proto__ property name"); 25137 goto fail; 25138 } 25139 emit_op(s, OP_set_proto); 25140 has_proto = TRUE; 25141 } else { 25142 set_object_name(s, name); 25143 emit_op(s, OP_define_field); 25144 emit_atom(s, name); 25145 } 25146 } 25147 JS_FreeAtom(s->ctx, name); 25148 next: 25149 name = JS_ATOM_NULL; 25150 if (s->token.val != ',') 25151 break; 25152 if (next_token(s)) 25153 goto fail; 25154 } 25155 if (js_parse_expect(s, '}')) 25156 goto fail; 25157 return 0; 25158 fail: 25159 JS_FreeAtom(s->ctx, name); 25160 return -1; 25161 } 25162 25163 /* allow the 'in' binary operator */ 25164 #define PF_IN_ACCEPTED (1 << 0) 25165 /* allow function calls parsing in js_parse_postfix_expr() */ 25166 #define PF_POSTFIX_CALL (1 << 1) 25167 /* allow the exponentiation operator in js_parse_unary() */ 25168 #define PF_POW_ALLOWED (1 << 2) 25169 /* forbid the exponentiation operator in js_parse_unary() */ 25170 #define PF_POW_FORBIDDEN (1 << 3) 25171 25172 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); 25173 static void emit_class_field_init(JSParseState *s); 25174 static JSFunctionDef *js_new_function_def(JSContext *ctx, 25175 JSFunctionDef *parent, 25176 BOOL is_eval, 25177 BOOL is_func_expr, 25178 const char *filename, 25179 const uint8_t *source_ptr, 25180 GetLineColCache *get_line_col_cache); 25181 static void emit_return(JSParseState *s, BOOL hasval); 25182 25183 static __exception int js_parse_left_hand_side_expr(JSParseState *s) 25184 { 25185 return js_parse_postfix_expr(s, PF_POSTFIX_CALL); 25186 } 25187 25188 static __exception int js_parse_class_default_ctor(JSParseState *s, 25189 BOOL has_super, 25190 JSFunctionDef **pfd) 25191 { 25192 JSParseFunctionEnum func_type; 25193 JSFunctionDef *fd = s->cur_func; 25194 int idx; 25195 25196 fd = js_new_function_def(s->ctx, fd, FALSE, FALSE, s->filename, 25197 s->token.ptr, &s->get_line_col_cache); 25198 if (!fd) 25199 return -1; 25200 25201 s->cur_func = fd; 25202 fd->has_home_object = TRUE; 25203 fd->super_allowed = TRUE; 25204 fd->has_prototype = FALSE; 25205 fd->has_this_binding = TRUE; 25206 fd->new_target_allowed = TRUE; 25207 25208 push_scope(s); /* enter body scope */ 25209 fd->body_scope = fd->scope_level; 25210 if (has_super) { 25211 fd->is_derived_class_constructor = TRUE; 25212 fd->super_call_allowed = TRUE; 25213 fd->arguments_allowed = TRUE; 25214 fd->has_arguments_binding = TRUE; 25215 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; 25216 emit_op(s, OP_init_ctor); 25217 // TODO(bnoordhuis) roll into OP_init_ctor 25218 emit_op(s, OP_scope_put_var_init); 25219 emit_atom(s, JS_ATOM_this); 25220 emit_u16(s, 0); 25221 emit_class_field_init(s); 25222 } else { 25223 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; 25224 /* error if not invoked as a constructor */ 25225 emit_op(s, OP_check_ctor); 25226 emit_class_field_init(s); 25227 } 25228 25229 fd->func_kind = JS_FUNC_NORMAL; 25230 fd->func_type = func_type; 25231 emit_return(s, FALSE); 25232 25233 s->cur_func = fd->parent; 25234 if (pfd) 25235 *pfd = fd; 25236 25237 /* the real object will be set at the end of the compilation */ 25238 idx = cpool_add(s, JS_NULL); 25239 fd->parent_cpool_idx = idx; 25240 25241 return 0; 25242 } 25243 25244 /* find field in the current scope */ 25245 static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd, 25246 JSAtom name, int scope_level) 25247 { 25248 int idx; 25249 idx = fd->scopes[scope_level].first; 25250 while (idx != -1) { 25251 if (fd->vars[idx].scope_level != scope_level) 25252 break; 25253 if (fd->vars[idx].var_name == name) 25254 return idx; 25255 idx = fd->vars[idx].scope_next; 25256 } 25257 return -1; 25258 } 25259 25260 /* initialize the class fields, called by the constructor. Note: 25261 super() can be called in an arrow function, so <this> and 25262 <class_fields_init> can be variable references */ 25263 static void emit_class_field_init(JSParseState *s) 25264 { 25265 int label_next; 25266 25267 emit_op(s, OP_scope_get_var); 25268 emit_atom(s, JS_ATOM_class_fields_init); 25269 emit_u16(s, s->cur_func->scope_level); 25270 25271 /* no need to call the class field initializer if not defined */ 25272 emit_op(s, OP_dup); 25273 label_next = emit_goto(s, OP_if_false, -1); 25274 25275 emit_op(s, OP_scope_get_var); 25276 emit_atom(s, JS_ATOM_this); 25277 emit_u16(s, 0); 25278 25279 emit_op(s, OP_swap); 25280 25281 emit_op(s, OP_call_method); 25282 emit_u16(s, 0); 25283 25284 emit_label(s, label_next); 25285 emit_op(s, OP_drop); 25286 } 25287 25288 /* build a private setter function name from the private getter name */ 25289 static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) 25290 { 25291 return js_atom_concat_str(ctx, name, "<set>"); 25292 } 25293 25294 typedef struct { 25295 JSFunctionDef *fields_init_fd; 25296 int computed_fields_count; 25297 BOOL need_brand; 25298 int brand_push_pos; 25299 BOOL is_static; 25300 } ClassFieldsDef; 25301 25302 static __exception int emit_class_init_start(JSParseState *s, 25303 ClassFieldsDef *cf) 25304 { 25305 int label_add_brand; 25306 25307 cf->fields_init_fd = js_parse_function_class_fields_init(s); 25308 if (!cf->fields_init_fd) 25309 return -1; 25310 25311 s->cur_func = cf->fields_init_fd; 25312 25313 if (!cf->is_static) { 25314 /* add the brand to the newly created instance */ 25315 /* XXX: would be better to add the code only if needed, maybe in a 25316 later pass */ 25317 emit_op(s, OP_push_false); /* will be patched later */ 25318 cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; 25319 label_add_brand = emit_goto(s, OP_if_false, -1); 25320 25321 emit_op(s, OP_scope_get_var); 25322 emit_atom(s, JS_ATOM_this); 25323 emit_u16(s, 0); 25324 25325 emit_op(s, OP_scope_get_var); 25326 emit_atom(s, JS_ATOM_home_object); 25327 emit_u16(s, 0); 25328 25329 emit_op(s, OP_add_brand); 25330 25331 emit_label(s, label_add_brand); 25332 } 25333 s->cur_func = s->cur_func->parent; 25334 return 0; 25335 } 25336 25337 static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) 25338 { 25339 int cpool_idx; 25340 25341 s->cur_func = cf->fields_init_fd; 25342 emit_op(s, OP_return_undef); 25343 s->cur_func = s->cur_func->parent; 25344 25345 cpool_idx = cpool_add(s, JS_NULL); 25346 cf->fields_init_fd->parent_cpool_idx = cpool_idx; 25347 emit_op(s, OP_fclosure); 25348 emit_u32(s, cpool_idx); 25349 emit_op(s, OP_set_home_object); 25350 } 25351 25352 25353 static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, 25354 JSParseExportEnum export_flag) 25355 { 25356 JSContext *ctx = s->ctx; 25357 JSFunctionDef *fd = s->cur_func; 25358 JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1; 25359 JSAtom class_var_name = JS_ATOM_NULL; 25360 JSFunctionDef *method_fd, *ctor_fd; 25361 int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset; 25362 int class_flags = 0, i, define_class_offset; 25363 BOOL is_static, is_private; 25364 const uint8_t *class_start_ptr = s->token.ptr; 25365 const uint8_t *start_ptr; 25366 ClassFieldsDef class_fields[2]; 25367 25368 /* classes are parsed and executed in strict mode */ 25369 saved_js_mode = fd->js_mode; 25370 fd->js_mode |= JS_MODE_STRICT; 25371 if (next_token(s)) 25372 goto fail; 25373 if (s->token.val == TOK_IDENT) { 25374 if (s->token.u.ident.is_reserved) { 25375 js_parse_error_reserved_identifier(s); 25376 goto fail; 25377 } 25378 class_name = JS_DupAtom(ctx, s->token.u.ident.atom); 25379 if (next_token(s)) 25380 goto fail; 25381 } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) { 25382 js_parse_error(s, "class statement requires a name"); 25383 goto fail; 25384 } 25385 if (!is_class_expr) { 25386 if (class_name == JS_ATOM_NULL) 25387 class_var_name = JS_ATOM__default_; /* export default */ 25388 else 25389 class_var_name = class_name; 25390 class_var_name = JS_DupAtom(ctx, class_var_name); 25391 } 25392 25393 push_scope(s); 25394 25395 if (s->token.val == TOK_EXTENDS) { 25396 class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; 25397 if (next_token(s)) 25398 goto fail; 25399 if (js_parse_left_hand_side_expr(s)) 25400 goto fail; 25401 } else { 25402 emit_op(s, OP_undefined); 25403 } 25404 25405 /* add a 'const' definition for the class name */ 25406 if (class_name != JS_ATOM_NULL) { 25407 class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST); 25408 if (class_name_var_idx < 0) 25409 goto fail; 25410 } 25411 25412 if (js_parse_expect(s, '{')) 25413 goto fail; 25414 25415 /* this scope contains the private fields */ 25416 push_scope(s); 25417 25418 emit_op(s, OP_push_const); 25419 ctor_cpool_offset = fd->byte_code.size; 25420 emit_u32(s, 0); /* will be patched at the end of the class parsing */ 25421 25422 if (class_name == JS_ATOM_NULL) { 25423 if (class_var_name != JS_ATOM_NULL) 25424 class_name1 = JS_ATOM_default; 25425 else 25426 class_name1 = JS_ATOM_empty_string; 25427 } else { 25428 class_name1 = class_name; 25429 } 25430 25431 emit_op(s, OP_define_class); 25432 emit_atom(s, class_name1); 25433 emit_u8(s, class_flags); 25434 define_class_offset = fd->last_opcode_pos; 25435 25436 for(i = 0; i < 2; i++) { 25437 ClassFieldsDef *cf = &class_fields[i]; 25438 cf->fields_init_fd = NULL; 25439 cf->computed_fields_count = 0; 25440 cf->need_brand = FALSE; 25441 cf->is_static = i; 25442 } 25443 25444 ctor_fd = NULL; 25445 while (s->token.val != '}') { 25446 if (s->token.val == ';') { 25447 if (next_token(s)) 25448 goto fail; 25449 continue; 25450 } 25451 is_static = FALSE; 25452 if (s->token.val == TOK_STATIC) { 25453 int next = peek_token(s, TRUE); 25454 if (!(next == ';' || next == '}' || next == '(' || next == '=')) 25455 is_static = TRUE; 25456 } 25457 prop_type = -1; 25458 if (is_static) { 25459 if (next_token(s)) 25460 goto fail; 25461 if (s->token.val == '{') { 25462 ClassFieldsDef *cf = &class_fields[is_static]; 25463 JSFunctionDef *init; 25464 if (!cf->fields_init_fd) { 25465 if (emit_class_init_start(s, cf)) 25466 goto fail; 25467 } 25468 s->cur_func = cf->fields_init_fd; 25469 /* XXX: could try to avoid creating a new function and 25470 reuse 'fields_init_fd' with a specific 'var' 25471 scope */ 25472 // stack is now: <empty> 25473 if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, 25474 JS_FUNC_NORMAL, JS_ATOM_NULL, 25475 s->token.ptr, 25476 JS_PARSE_EXPORT_NONE, &init) < 0) { 25477 goto fail; 25478 } 25479 // stack is now: fclosure 25480 push_scope(s); 25481 emit_op(s, OP_scope_get_var); 25482 emit_atom(s, JS_ATOM_this); 25483 emit_u16(s, 0); 25484 // stack is now: fclosure this 25485 emit_op(s, OP_swap); 25486 // stack is now: this fclosure 25487 emit_op(s, OP_call_method); 25488 emit_u16(s, 0); 25489 // stack is now: returnvalue 25490 emit_op(s, OP_drop); 25491 // stack is now: <empty> 25492 pop_scope(s); 25493 s->cur_func = s->cur_func->parent; 25494 continue; 25495 } 25496 /* allow "static" field name */ 25497 if (s->token.val == ';' || s->token.val == '=') { 25498 is_static = FALSE; 25499 name = JS_DupAtom(ctx, JS_ATOM_static); 25500 prop_type = PROP_TYPE_IDENT; 25501 } 25502 } 25503 if (is_static) 25504 emit_op(s, OP_swap); 25505 start_ptr = s->token.ptr; 25506 if (prop_type < 0) { 25507 prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); 25508 if (prop_type < 0) 25509 goto fail; 25510 } 25511 is_private = prop_type & PROP_TYPE_PRIVATE; 25512 prop_type &= ~PROP_TYPE_PRIVATE; 25513 25514 if ((name == JS_ATOM_constructor && !is_static && 25515 prop_type != PROP_TYPE_IDENT) || 25516 (name == JS_ATOM_prototype && is_static) || 25517 name == JS_ATOM_hash_constructor) { 25518 js_parse_error(s, "invalid method name"); 25519 goto fail; 25520 } 25521 if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) { 25522 BOOL is_set = prop_type - PROP_TYPE_GET; 25523 JSFunctionDef *method_fd; 25524 25525 if (is_private) { 25526 int idx, var_kind, is_static1; 25527 idx = find_private_class_field(ctx, fd, name, fd->scope_level); 25528 if (idx >= 0) { 25529 var_kind = fd->vars[idx].var_kind; 25530 is_static1 = fd->vars[idx].is_static_private; 25531 if (var_kind == JS_VAR_PRIVATE_FIELD || 25532 var_kind == JS_VAR_PRIVATE_METHOD || 25533 var_kind == JS_VAR_PRIVATE_GETTER_SETTER || 25534 var_kind == (JS_VAR_PRIVATE_GETTER + is_set) || 25535 (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) && 25536 is_static != is_static1)) { 25537 goto private_field_already_defined; 25538 } 25539 fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; 25540 } else { 25541 if (add_private_class_field(s, fd, name, 25542 JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0) 25543 goto fail; 25544 } 25545 class_fields[is_static].need_brand = TRUE; 25546 } 25547 25548 if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, 25549 JS_FUNC_NORMAL, JS_ATOM_NULL, 25550 start_ptr, 25551 JS_PARSE_EXPORT_NONE, &method_fd)) 25552 goto fail; 25553 if (is_private) { 25554 method_fd->need_home_object = TRUE; /* needed for brand check */ 25555 emit_op(s, OP_set_home_object); 25556 /* XXX: missing function name */ 25557 emit_op(s, OP_scope_put_var_init); 25558 if (is_set) { 25559 JSAtom setter_name; 25560 int ret; 25561 25562 setter_name = get_private_setter_name(ctx, name); 25563 if (setter_name == JS_ATOM_NULL) 25564 goto fail; 25565 emit_atom(s, setter_name); 25566 ret = add_private_class_field(s, fd, setter_name, 25567 JS_VAR_PRIVATE_SETTER, is_static); 25568 JS_FreeAtom(ctx, setter_name); 25569 if (ret < 0) 25570 goto fail; 25571 } else { 25572 emit_atom(s, name); 25573 } 25574 emit_u16(s, s->cur_func->scope_level); 25575 } else { 25576 if (name == JS_ATOM_NULL) { 25577 emit_op(s, OP_define_method_computed); 25578 } else { 25579 emit_op(s, OP_define_method); 25580 emit_atom(s, name); 25581 } 25582 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set); 25583 } 25584 } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { 25585 ClassFieldsDef *cf = &class_fields[is_static]; 25586 JSAtom field_var_name = JS_ATOM_NULL; 25587 25588 /* class field */ 25589 25590 /* XXX: spec: not consistent with method name checks */ 25591 if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) { 25592 js_parse_error(s, "invalid field name"); 25593 goto fail; 25594 } 25595 25596 if (is_private) { 25597 if (find_private_class_field(ctx, fd, name, 25598 fd->scope_level) >= 0) { 25599 goto private_field_already_defined; 25600 } 25601 if (add_private_class_field(s, fd, name, 25602 JS_VAR_PRIVATE_FIELD, is_static) < 0) 25603 goto fail; 25604 emit_op(s, OP_private_symbol); 25605 emit_atom(s, name); 25606 emit_op(s, OP_scope_put_var_init); 25607 emit_atom(s, name); 25608 emit_u16(s, s->cur_func->scope_level); 25609 } 25610 25611 if (!cf->fields_init_fd) { 25612 if (emit_class_init_start(s, cf)) 25613 goto fail; 25614 } 25615 if (name == JS_ATOM_NULL ) { 25616 /* save the computed field name into a variable */ 25617 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count); 25618 if (field_var_name == JS_ATOM_NULL) 25619 goto fail; 25620 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) { 25621 JS_FreeAtom(ctx, field_var_name); 25622 goto fail; 25623 } 25624 emit_op(s, OP_to_propkey); 25625 emit_op(s, OP_scope_put_var_init); 25626 emit_atom(s, field_var_name); 25627 emit_u16(s, s->cur_func->scope_level); 25628 } 25629 s->cur_func = cf->fields_init_fd; 25630 emit_op(s, OP_scope_get_var); 25631 emit_atom(s, JS_ATOM_this); 25632 emit_u16(s, 0); 25633 25634 if (name == JS_ATOM_NULL) { 25635 emit_op(s, OP_scope_get_var); 25636 emit_atom(s, field_var_name); 25637 emit_u16(s, s->cur_func->scope_level); 25638 cf->computed_fields_count++; 25639 JS_FreeAtom(ctx, field_var_name); 25640 } else if (is_private) { 25641 emit_op(s, OP_scope_get_var); 25642 emit_atom(s, name); 25643 emit_u16(s, s->cur_func->scope_level); 25644 } 25645 25646 if (s->token.val == '=') { 25647 if (next_token(s)) 25648 goto fail; 25649 if (js_parse_assign_expr(s)) 25650 goto fail; 25651 } else { 25652 emit_op(s, OP_undefined); 25653 } 25654 if (is_private) { 25655 set_object_name_computed(s); 25656 emit_op(s, OP_define_private_field); 25657 } else if (name == JS_ATOM_NULL) { 25658 set_object_name_computed(s); 25659 emit_op(s, OP_define_array_el); 25660 emit_op(s, OP_drop); 25661 } else { 25662 set_object_name(s, name); 25663 emit_op(s, OP_define_field); 25664 emit_atom(s, name); 25665 } 25666 s->cur_func = s->cur_func->parent; 25667 if (js_parse_expect_semi(s)) 25668 goto fail; 25669 } else { 25670 JSParseFunctionEnum func_type; 25671 JSFunctionKindEnum func_kind; 25672 25673 func_type = JS_PARSE_FUNC_METHOD; 25674 func_kind = JS_FUNC_NORMAL; 25675 if (prop_type == PROP_TYPE_STAR) { 25676 func_kind = JS_FUNC_GENERATOR; 25677 } else if (prop_type == PROP_TYPE_ASYNC) { 25678 func_kind = JS_FUNC_ASYNC; 25679 } else if (prop_type == PROP_TYPE_ASYNC_STAR) { 25680 func_kind = JS_FUNC_ASYNC_GENERATOR; 25681 } else if (name == JS_ATOM_constructor && !is_static) { 25682 if (ctor_fd) { 25683 js_parse_error(s, "property constructor appears more than once"); 25684 goto fail; 25685 } 25686 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) 25687 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; 25688 else 25689 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; 25690 } 25691 if (is_private) { 25692 class_fields[is_static].need_brand = TRUE; 25693 } 25694 if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, JS_PARSE_EXPORT_NONE, &method_fd)) 25695 goto fail; 25696 if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || 25697 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { 25698 ctor_fd = method_fd; 25699 } else if (is_private) { 25700 method_fd->need_home_object = TRUE; /* needed for brand check */ 25701 if (find_private_class_field(ctx, fd, name, 25702 fd->scope_level) >= 0) { 25703 private_field_already_defined: 25704 js_parse_error(s, "private class field is already defined"); 25705 goto fail; 25706 } 25707 if (add_private_class_field(s, fd, name, 25708 JS_VAR_PRIVATE_METHOD, is_static) < 0) 25709 goto fail; 25710 emit_op(s, OP_set_home_object); 25711 emit_op(s, OP_set_name); 25712 emit_atom(s, name); 25713 emit_op(s, OP_scope_put_var_init); 25714 emit_atom(s, name); 25715 emit_u16(s, s->cur_func->scope_level); 25716 } else { 25717 if (name == JS_ATOM_NULL) { 25718 emit_op(s, OP_define_method_computed); 25719 } else { 25720 emit_op(s, OP_define_method); 25721 emit_atom(s, name); 25722 } 25723 emit_u8(s, OP_DEFINE_METHOD_METHOD); 25724 } 25725 } 25726 if (is_static) 25727 emit_op(s, OP_swap); 25728 JS_FreeAtom(ctx, name); 25729 name = JS_ATOM_NULL; 25730 } 25731 25732 if (s->token.val != '}') { 25733 js_parse_error(s, "expecting '%c'", '}'); 25734 goto fail; 25735 } 25736 25737 if (!ctor_fd) { 25738 if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd)) 25739 goto fail; 25740 } 25741 /* patch the constant pool index for the constructor */ 25742 put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); 25743 25744 /* store the class source code in the constructor. */ 25745 if (!fd->strip_source) { 25746 js_free(ctx, ctor_fd->source); 25747 ctor_fd->source_len = s->buf_ptr - class_start_ptr; 25748 ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, 25749 ctor_fd->source_len); 25750 if (!ctor_fd->source) 25751 goto fail; 25752 } 25753 25754 /* consume the '}' */ 25755 if (next_token(s)) 25756 goto fail; 25757 25758 { 25759 ClassFieldsDef *cf = &class_fields[0]; 25760 int var_idx; 25761 25762 if (cf->need_brand) { 25763 /* add a private brand to the prototype */ 25764 emit_op(s, OP_dup); 25765 emit_op(s, OP_null); 25766 emit_op(s, OP_swap); 25767 emit_op(s, OP_add_brand); 25768 25769 /* define the brand field in 'this' of the initializer */ 25770 if (!cf->fields_init_fd) { 25771 if (emit_class_init_start(s, cf)) 25772 goto fail; 25773 } 25774 /* patch the start of the function to enable the 25775 OP_add_brand_instance code */ 25776 cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; 25777 } 25778 25779 /* store the function to initialize the fields to that it can be 25780 referenced by the constructor */ 25781 var_idx = define_var(s, fd, JS_ATOM_class_fields_init, 25782 JS_VAR_DEF_CONST); 25783 if (var_idx < 0) 25784 goto fail; 25785 if (cf->fields_init_fd) { 25786 emit_class_init_end(s, cf); 25787 } else { 25788 emit_op(s, OP_undefined); 25789 } 25790 emit_op(s, OP_scope_put_var_init); 25791 emit_atom(s, JS_ATOM_class_fields_init); 25792 emit_u16(s, s->cur_func->scope_level); 25793 } 25794 25795 /* drop the prototype */ 25796 emit_op(s, OP_drop); 25797 25798 if (class_fields[1].need_brand) { 25799 /* add a private brand to the class */ 25800 emit_op(s, OP_dup); 25801 emit_op(s, OP_dup); 25802 emit_op(s, OP_add_brand); 25803 } 25804 25805 if (class_name != JS_ATOM_NULL) { 25806 /* store the class name in the scoped class name variable (it 25807 is independent from the class statement variable 25808 definition) */ 25809 emit_op(s, OP_dup); 25810 emit_op(s, OP_scope_put_var_init); 25811 emit_atom(s, class_name); 25812 emit_u16(s, fd->scope_level); 25813 } 25814 25815 /* initialize the static fields */ 25816 if (class_fields[1].fields_init_fd != NULL) { 25817 ClassFieldsDef *cf = &class_fields[1]; 25818 emit_op(s, OP_dup); 25819 emit_class_init_end(s, cf); 25820 emit_op(s, OP_call_method); 25821 emit_u16(s, 0); 25822 emit_op(s, OP_drop); 25823 } 25824 25825 pop_scope(s); 25826 pop_scope(s); 25827 25828 /* the class statements have a block level scope */ 25829 if (class_var_name != JS_ATOM_NULL) { 25830 if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0) 25831 goto fail; 25832 emit_op(s, OP_scope_put_var_init); 25833 emit_atom(s, class_var_name); 25834 emit_u16(s, fd->scope_level); 25835 } else { 25836 if (class_name == JS_ATOM_NULL) { 25837 /* cannot use OP_set_name because the name of the class 25838 must be defined before the static initializers are 25839 executed */ 25840 emit_op(s, OP_set_class_name); 25841 emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset); 25842 } 25843 } 25844 25845 if (export_flag != JS_PARSE_EXPORT_NONE) { 25846 if (!add_export_entry(s, fd->module, 25847 class_var_name, 25848 export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default, 25849 JS_EXPORT_TYPE_LOCAL)) 25850 goto fail; 25851 } 25852 25853 JS_FreeAtom(ctx, class_name); 25854 JS_FreeAtom(ctx, class_var_name); 25855 fd->js_mode = saved_js_mode; 25856 return 0; 25857 fail: 25858 JS_FreeAtom(ctx, name); 25859 JS_FreeAtom(ctx, class_name); 25860 JS_FreeAtom(ctx, class_var_name); 25861 fd->js_mode = saved_js_mode; 25862 return -1; 25863 } 25864 25865 static __exception int js_parse_array_literal(JSParseState *s) 25866 { 25867 uint32_t idx; 25868 BOOL need_length; 25869 25870 if (next_token(s)) 25871 return -1; 25872 /* small regular arrays are created on the stack */ 25873 idx = 0; 25874 while (s->token.val != ']' && idx < 32) { 25875 if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) 25876 break; 25877 if (js_parse_assign_expr(s)) 25878 return -1; 25879 idx++; 25880 /* accept trailing comma */ 25881 if (s->token.val == ',') { 25882 if (next_token(s)) 25883 return -1; 25884 } else 25885 if (s->token.val != ']') 25886 goto done; 25887 } 25888 emit_op(s, OP_array_from); 25889 emit_u16(s, idx); 25890 25891 /* larger arrays and holes are handled with explicit indices */ 25892 need_length = FALSE; 25893 while (s->token.val != ']' && idx < 0x7fffffff) { 25894 if (s->token.val == TOK_ELLIPSIS) 25895 break; 25896 need_length = TRUE; 25897 if (s->token.val != ',') { 25898 if (js_parse_assign_expr(s)) 25899 return -1; 25900 emit_op(s, OP_define_field); 25901 emit_u32(s, __JS_AtomFromUInt32(idx)); 25902 need_length = FALSE; 25903 } 25904 idx++; 25905 /* accept trailing comma */ 25906 if (s->token.val == ',') { 25907 if (next_token(s)) 25908 return -1; 25909 } 25910 } 25911 if (s->token.val == ']') { 25912 if (need_length) { 25913 /* Set the length: Cannot use OP_define_field because 25914 length is not configurable */ 25915 emit_op(s, OP_dup); 25916 emit_op(s, OP_push_i32); 25917 emit_u32(s, idx); 25918 emit_op(s, OP_put_field); 25919 emit_atom(s, JS_ATOM_length); 25920 } 25921 goto done; 25922 } 25923 25924 /* huge arrays and spread elements require a dynamic index on the stack */ 25925 emit_op(s, OP_push_i32); 25926 emit_u32(s, idx); 25927 25928 /* stack has array, index */ 25929 while (s->token.val != ']') { 25930 if (s->token.val == TOK_ELLIPSIS) { 25931 if (next_token(s)) 25932 return -1; 25933 if (js_parse_assign_expr(s)) 25934 return -1; 25935 #if 1 25936 emit_op(s, OP_append); 25937 #else 25938 int label_next, label_done; 25939 label_next = new_label(s); 25940 label_done = new_label(s); 25941 /* enumerate object */ 25942 emit_op(s, OP_for_of_start); 25943 emit_op(s, OP_rot5l); 25944 emit_op(s, OP_rot5l); 25945 emit_label(s, label_next); 25946 /* on stack: enum_rec array idx */ 25947 emit_op(s, OP_for_of_next); 25948 emit_u8(s, 2); 25949 emit_goto(s, OP_if_true, label_done); 25950 /* append element */ 25951 /* enum_rec array idx val -> enum_rec array new_idx */ 25952 emit_op(s, OP_define_array_el); 25953 emit_op(s, OP_inc); 25954 emit_goto(s, OP_goto, label_next); 25955 emit_label(s, label_done); 25956 /* close enumeration */ 25957 emit_op(s, OP_drop); /* drop undef val */ 25958 emit_op(s, OP_nip1); /* drop enum_rec */ 25959 emit_op(s, OP_nip1); 25960 emit_op(s, OP_nip1); 25961 #endif 25962 } else { 25963 need_length = TRUE; 25964 if (s->token.val != ',') { 25965 if (js_parse_assign_expr(s)) 25966 return -1; 25967 /* a idx val */ 25968 emit_op(s, OP_define_array_el); 25969 need_length = FALSE; 25970 } 25971 emit_op(s, OP_inc); 25972 } 25973 if (s->token.val != ',') 25974 break; 25975 if (next_token(s)) 25976 return -1; 25977 } 25978 if (need_length) { 25979 /* Set the length: cannot use OP_define_field because 25980 length is not configurable */ 25981 emit_op(s, OP_dup1); /* array length - array array length */ 25982 emit_op(s, OP_put_field); 25983 emit_atom(s, JS_ATOM_length); 25984 } else { 25985 emit_op(s, OP_drop); /* array length - array */ 25986 } 25987 done: 25988 return js_parse_expect(s, ']'); 25989 } 25990 25991 /* check if scope chain contains a with statement */ 25992 static BOOL has_with_scope(JSFunctionDef *s, int scope_level) 25993 { 25994 while (s) { 25995 /* no with in strict mode */ 25996 if (!(s->js_mode & JS_MODE_STRICT)) { 25997 int scope_idx = s->scopes[scope_level].first; 25998 while (scope_idx >= 0) { 25999 JSVarDef *vd = &s->vars[scope_idx]; 26000 if (vd->var_name == JS_ATOM__with_) 26001 return TRUE; 26002 scope_idx = vd->scope_next; 26003 } 26004 } 26005 /* check parent scopes */ 26006 scope_level = s->parent_scope_level; 26007 s = s->parent; 26008 } 26009 return FALSE; 26010 } 26011 26012 static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, 26013 JSAtom *pname, int *plabel, int *pdepth, BOOL keep, 26014 int tok) 26015 { 26016 JSFunctionDef *fd; 26017 int opcode, scope, label, depth; 26018 JSAtom name; 26019 26020 /* we check the last opcode to get the lvalue type */ 26021 fd = s->cur_func; 26022 scope = 0; 26023 name = JS_ATOM_NULL; 26024 label = -1; 26025 depth = 0; 26026 switch(opcode = get_prev_opcode(fd)) { 26027 case OP_scope_get_var: 26028 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 26029 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 26030 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) && 26031 (fd->js_mode & JS_MODE_STRICT)) { 26032 return js_parse_error(s, "invalid lvalue in strict mode"); 26033 } 26034 if (name == JS_ATOM_this || name == JS_ATOM_new_target) 26035 goto invalid_lvalue; 26036 if (has_with_scope(fd, scope)) { 26037 depth = 2; /* will generate OP_get_ref_value */ 26038 } else { 26039 depth = 0; 26040 } 26041 break; 26042 case OP_get_field: 26043 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 26044 depth = 1; 26045 break; 26046 case OP_scope_get_private_field: 26047 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 26048 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 26049 depth = 1; 26050 break; 26051 case OP_get_array_el: 26052 depth = 2; 26053 break; 26054 case OP_get_super_value: 26055 depth = 3; 26056 break; 26057 default: 26058 invalid_lvalue: 26059 if (tok == TOK_FOR) { 26060 return js_parse_error(s, "invalid for in/of left hand-side"); 26061 } else if (tok == TOK_INC || tok == TOK_DEC) { 26062 return js_parse_error(s, "invalid increment/decrement operand"); 26063 } else if (tok == '[' || tok == '{') { 26064 return js_parse_error(s, "invalid destructuring target"); 26065 } else { 26066 return js_parse_error(s, "invalid assignment left-hand side"); 26067 } 26068 } 26069 /* remove the last opcode */ 26070 fd->byte_code.size = fd->last_opcode_pos; 26071 fd->last_opcode_pos = -1; 26072 26073 if (keep) { 26074 /* get the value but keep the object/fields on the stack */ 26075 switch(opcode) { 26076 case OP_scope_get_var: 26077 if (depth != 0) { 26078 label = new_label(s); 26079 if (label < 0) 26080 return -1; 26081 emit_op(s, OP_scope_make_ref); 26082 emit_atom(s, name); 26083 emit_u32(s, label); 26084 emit_u16(s, scope); 26085 update_label(fd, label, 1); 26086 emit_op(s, OP_get_ref_value); 26087 opcode = OP_get_ref_value; 26088 } else { 26089 emit_op(s, OP_scope_get_var); 26090 emit_atom(s, name); 26091 emit_u16(s, scope); 26092 } 26093 break; 26094 case OP_get_field: 26095 emit_op(s, OP_get_field2); 26096 emit_atom(s, name); 26097 break; 26098 case OP_scope_get_private_field: 26099 emit_op(s, OP_scope_get_private_field2); 26100 emit_atom(s, name); 26101 emit_u16(s, scope); 26102 break; 26103 case OP_get_array_el: 26104 emit_op(s, OP_get_array_el3); 26105 break; 26106 case OP_get_super_value: 26107 emit_op(s, OP_to_propkey); 26108 emit_op(s, OP_dup3); 26109 emit_op(s, OP_get_super_value); 26110 break; 26111 default: 26112 abort(); 26113 } 26114 } else { 26115 switch(opcode) { 26116 case OP_scope_get_var: 26117 if (depth != 0) { 26118 label = new_label(s); 26119 if (label < 0) 26120 return -1; 26121 emit_op(s, OP_scope_make_ref); 26122 emit_atom(s, name); 26123 emit_u32(s, label); 26124 emit_u16(s, scope); 26125 update_label(fd, label, 1); 26126 opcode = OP_get_ref_value; 26127 } 26128 break; 26129 default: 26130 break; 26131 } 26132 } 26133 26134 *popcode = opcode; 26135 *pscope = scope; 26136 /* name has refcount for OP_get_field and OP_get_ref_value, 26137 and JS_ATOM_NULL for other opcodes */ 26138 *pname = name; 26139 *plabel = label; 26140 if (pdepth) 26141 *pdepth = depth; 26142 return 0; 26143 } 26144 26145 typedef enum { 26146 PUT_LVALUE_NOKEEP, /* [depth] v -> */ 26147 PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently 26148 just disable optimizations) */ 26149 PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ 26150 PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ 26151 PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ 26152 } PutLValueEnum; 26153 26154 /* name has a live reference. 'is_let' is only used with opcode = 26155 OP_scope_get_var which is never generated by get_lvalue(). */ 26156 static void put_lvalue(JSParseState *s, int opcode, int scope, 26157 JSAtom name, int label, PutLValueEnum special, 26158 BOOL is_let) 26159 { 26160 switch(opcode) { 26161 case OP_scope_get_var: 26162 /* depth = 0 */ 26163 switch(special) { 26164 case PUT_LVALUE_NOKEEP: 26165 case PUT_LVALUE_NOKEEP_DEPTH: 26166 case PUT_LVALUE_KEEP_SECOND: 26167 case PUT_LVALUE_NOKEEP_BOTTOM: 26168 break; 26169 case PUT_LVALUE_KEEP_TOP: 26170 emit_op(s, OP_dup); 26171 break; 26172 default: 26173 abort(); 26174 } 26175 break; 26176 case OP_get_field: 26177 case OP_scope_get_private_field: 26178 /* depth = 1 */ 26179 switch(special) { 26180 case PUT_LVALUE_NOKEEP: 26181 case PUT_LVALUE_NOKEEP_DEPTH: 26182 break; 26183 case PUT_LVALUE_KEEP_TOP: 26184 emit_op(s, OP_insert2); /* obj v -> v obj v */ 26185 break; 26186 case PUT_LVALUE_KEEP_SECOND: 26187 emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ 26188 break; 26189 case PUT_LVALUE_NOKEEP_BOTTOM: 26190 emit_op(s, OP_swap); 26191 break; 26192 default: 26193 abort(); 26194 } 26195 break; 26196 case OP_get_array_el: 26197 case OP_get_ref_value: 26198 /* depth = 2 */ 26199 if (opcode == OP_get_ref_value) { 26200 JS_FreeAtom(s->ctx, name); 26201 emit_label(s, label); 26202 } 26203 switch(special) { 26204 case PUT_LVALUE_NOKEEP: 26205 emit_op(s, OP_nop); /* will trigger optimization */ 26206 break; 26207 case PUT_LVALUE_NOKEEP_DEPTH: 26208 break; 26209 case PUT_LVALUE_KEEP_TOP: 26210 emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ 26211 break; 26212 case PUT_LVALUE_KEEP_SECOND: 26213 emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ 26214 break; 26215 case PUT_LVALUE_NOKEEP_BOTTOM: 26216 emit_op(s, OP_rot3l); 26217 break; 26218 default: 26219 abort(); 26220 } 26221 break; 26222 case OP_get_super_value: 26223 /* depth = 3 */ 26224 switch(special) { 26225 case PUT_LVALUE_NOKEEP: 26226 case PUT_LVALUE_NOKEEP_DEPTH: 26227 break; 26228 case PUT_LVALUE_KEEP_TOP: 26229 emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ 26230 break; 26231 case PUT_LVALUE_KEEP_SECOND: 26232 emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ 26233 break; 26234 case PUT_LVALUE_NOKEEP_BOTTOM: 26235 emit_op(s, OP_rot4l); 26236 break; 26237 default: 26238 abort(); 26239 } 26240 break; 26241 default: 26242 break; 26243 } 26244 26245 switch(opcode) { 26246 case OP_scope_get_var: /* val -- */ 26247 emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); 26248 emit_u32(s, name); /* has refcount */ 26249 emit_u16(s, scope); 26250 break; 26251 case OP_get_field: 26252 emit_op(s, OP_put_field); 26253 emit_u32(s, name); /* name has refcount */ 26254 break; 26255 case OP_scope_get_private_field: 26256 emit_op(s, OP_scope_put_private_field); 26257 emit_u32(s, name); /* name has refcount */ 26258 emit_u16(s, scope); 26259 break; 26260 case OP_get_array_el: 26261 emit_op(s, OP_put_array_el); 26262 break; 26263 case OP_get_ref_value: 26264 emit_op(s, OP_put_ref_value); 26265 break; 26266 case OP_get_super_value: 26267 emit_op(s, OP_put_super_value); 26268 break; 26269 default: 26270 abort(); 26271 } 26272 } 26273 26274 static __exception int js_parse_expr_paren(JSParseState *s) 26275 { 26276 if (js_parse_expect(s, '(')) 26277 return -1; 26278 if (js_parse_expr(s)) 26279 return -1; 26280 if (js_parse_expect(s, ')')) 26281 return -1; 26282 return 0; 26283 } 26284 26285 static int js_unsupported_keyword(JSParseState *s, JSAtom atom) 26286 { 26287 char buf[ATOM_GET_STR_BUF_SIZE]; 26288 return js_parse_error(s, "unsupported keyword: %s", 26289 JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); 26290 } 26291 26292 static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) 26293 { 26294 JSFunctionDef *fd = s->cur_func; 26295 JSVarDefEnum var_def_type; 26296 26297 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { 26298 return js_parse_error(s, "yield is a reserved identifier"); 26299 } 26300 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) 26301 && (fd->js_mode & JS_MODE_STRICT)) { 26302 return js_parse_error(s, "invalid variable name in strict mode"); 26303 } 26304 if (name == JS_ATOM_let 26305 && (tok == TOK_LET || tok == TOK_CONST)) { 26306 return js_parse_error(s, "invalid lexical variable name"); 26307 } 26308 switch(tok) { 26309 case TOK_LET: 26310 var_def_type = JS_VAR_DEF_LET; 26311 break; 26312 case TOK_CONST: 26313 var_def_type = JS_VAR_DEF_CONST; 26314 break; 26315 case TOK_VAR: 26316 var_def_type = JS_VAR_DEF_VAR; 26317 break; 26318 case TOK_CATCH: 26319 var_def_type = JS_VAR_DEF_CATCH; 26320 break; 26321 default: 26322 abort(); 26323 } 26324 if (define_var(s, fd, name, var_def_type) < 0) 26325 return -1; 26326 return 0; 26327 } 26328 26329 static void js_emit_spread_code(JSParseState *s, int depth) 26330 { 26331 int label_rest_next, label_rest_done; 26332 26333 /* XXX: could check if enum object is an actual array and optimize 26334 slice extraction. enumeration record and target array are in a 26335 different order from OP_append case. */ 26336 /* enum_rec xxx -- enum_rec xxx array 0 */ 26337 emit_op(s, OP_array_from); 26338 emit_u16(s, 0); 26339 emit_op(s, OP_push_i32); 26340 emit_u32(s, 0); 26341 emit_label(s, label_rest_next = new_label(s)); 26342 emit_op(s, OP_for_of_next); 26343 emit_u8(s, 2 + depth); 26344 label_rest_done = emit_goto(s, OP_if_true, -1); 26345 /* array idx val -- array idx */ 26346 emit_op(s, OP_define_array_el); 26347 emit_op(s, OP_inc); 26348 emit_goto(s, OP_goto, label_rest_next); 26349 emit_label(s, label_rest_done); 26350 /* enum_rec xxx array idx undef -- enum_rec xxx array */ 26351 emit_op(s, OP_drop); 26352 emit_op(s, OP_drop); 26353 } 26354 26355 static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) 26356 { 26357 /* Check for duplicate parameter names */ 26358 JSFunctionDef *fd = s->cur_func; 26359 int i; 26360 for (i = 0; i < fd->arg_count; i++) { 26361 if (fd->args[i].var_name == name) 26362 goto duplicate; 26363 } 26364 for (i = 0; i < fd->var_count; i++) { 26365 if (fd->vars[i].var_name == name) 26366 goto duplicate; 26367 } 26368 return 0; 26369 26370 duplicate: 26371 return js_parse_error(s, "duplicate parameter names not allowed in this context"); 26372 } 26373 26374 /* tok = TOK_VAR, TOK_LET or TOK_CONST. Return whether a reference 26375 must be taken to the variable for proper 'with' or global variable 26376 evaluation */ 26377 /* Note: this function is needed only because variable references are 26378 not yet optimized in destructuring */ 26379 static BOOL need_var_reference(JSParseState *s, int tok) 26380 { 26381 JSFunctionDef *fd = s->cur_func; 26382 if (tok != TOK_VAR) 26383 return FALSE; /* no reference for let/const */ 26384 if (fd->js_mode & JS_MODE_STRICT) { 26385 if (!fd->is_global_var) 26386 return FALSE; /* local definitions in strict mode in function or direct eval */ 26387 if (s->is_module) 26388 return FALSE; /* in a module global variables are like closure variables */ 26389 } 26390 return TRUE; 26391 } 26392 26393 static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) 26394 { 26395 JSAtom name; 26396 26397 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) 26398 || ((s->cur_func->js_mode & JS_MODE_STRICT) && 26399 (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) { 26400 js_parse_error(s, "invalid destructuring target"); 26401 return JS_ATOM_NULL; 26402 } 26403 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 26404 if (is_arg && js_parse_check_duplicate_parameter(s, name)) 26405 goto fail; 26406 if (next_token(s)) 26407 goto fail; 26408 26409 return name; 26410 fail: 26411 JS_FreeAtom(s->ctx, name); 26412 return JS_ATOM_NULL; 26413 } 26414 26415 /* Return -1 if error, 0 if no initializer, 1 if an initializer is 26416 present at the top level. */ 26417 static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, 26418 int hasval, int has_ellipsis, 26419 BOOL allow_initializer, BOOL export_flag) 26420 { 26421 int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; 26422 int start_addr, assign_addr; 26423 JSAtom prop_name, var_name; 26424 int opcode, scope, tok1, skip_bits; 26425 BOOL has_initializer; 26426 26427 if (has_ellipsis < 0) { 26428 /* pre-parse destructuration target for spread detection */ 26429 js_parse_skip_parens_token(s, &skip_bits, FALSE); 26430 has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS; 26431 } 26432 26433 label_parse = new_label(s); 26434 label_assign = new_label(s); 26435 26436 start_addr = s->cur_func->byte_code.size; 26437 if (hasval) { 26438 /* consume value from the stack */ 26439 emit_op(s, OP_dup); 26440 emit_op(s, OP_undefined); 26441 emit_op(s, OP_strict_eq); 26442 emit_goto(s, OP_if_true, label_parse); 26443 emit_label(s, label_assign); 26444 } else { 26445 emit_goto(s, OP_goto, label_parse); 26446 emit_label(s, label_assign); 26447 /* leave value on the stack */ 26448 emit_op(s, OP_dup); 26449 } 26450 assign_addr = s->cur_func->byte_code.size; 26451 if (s->token.val == '{') { 26452 if (next_token(s)) 26453 return -1; 26454 /* throw an exception if the value cannot be converted to an object */ 26455 emit_op(s, OP_to_object); 26456 if (has_ellipsis) { 26457 /* add excludeList on stack just below src object */ 26458 emit_op(s, OP_object); 26459 emit_op(s, OP_swap); 26460 } 26461 while (s->token.val != '}') { 26462 int prop_type; 26463 if (s->token.val == TOK_ELLIPSIS) { 26464 if (!has_ellipsis) { 26465 JS_ThrowInternalError(s->ctx, "unexpected ellipsis token"); 26466 return -1; 26467 } 26468 if (next_token(s)) 26469 return -1; 26470 if (tok) { 26471 var_name = js_parse_destructuring_var(s, tok, is_arg); 26472 if (var_name == JS_ATOM_NULL) 26473 return -1; 26474 if (need_var_reference(s, tok)) { 26475 /* Must make a reference for proper `with` semantics */ 26476 emit_op(s, OP_scope_get_var); 26477 emit_atom(s, var_name); 26478 emit_u16(s, s->cur_func->scope_level); 26479 JS_FreeAtom(s->ctx, var_name); 26480 goto lvalue0; 26481 } else { 26482 opcode = OP_scope_get_var; 26483 scope = s->cur_func->scope_level; 26484 label_lvalue = -1; 26485 depth_lvalue = 0; 26486 } 26487 } else { 26488 if (js_parse_left_hand_side_expr(s)) 26489 return -1; 26490 lvalue0: 26491 if (get_lvalue(s, &opcode, &scope, &var_name, 26492 &label_lvalue, &depth_lvalue, FALSE, '{')) 26493 return -1; 26494 } 26495 if (s->token.val != '}') { 26496 js_parse_error(s, "assignment rest property must be last"); 26497 goto var_error; 26498 } 26499 emit_op(s, OP_object); /* target */ 26500 emit_op(s, OP_copy_data_properties); 26501 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5)); 26502 goto set_val; 26503 } 26504 prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE); 26505 if (prop_type < 0) 26506 return -1; 26507 var_name = JS_ATOM_NULL; 26508 if (prop_type == PROP_TYPE_IDENT) { 26509 if (next_token(s)) 26510 goto prop_error; 26511 if ((s->token.val == '[' || s->token.val == '{') 26512 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || 26513 tok1 == '=' || tok1 == '}')) { 26514 if (prop_name == JS_ATOM_NULL) { 26515 /* computed property name on stack */ 26516 if (has_ellipsis) { 26517 /* define the property in excludeList */ 26518 emit_op(s, OP_to_propkey); /* avoid calling ToString twice */ 26519 emit_op(s, OP_perm3); /* TOS: src excludeList prop */ 26520 emit_op(s, OP_null); /* TOS: src excludeList prop null */ 26521 emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */ 26522 emit_op(s, OP_perm3); /* TOS: excludeList src prop */ 26523 } 26524 /* get the computed property from the source object */ 26525 emit_op(s, OP_get_array_el2); 26526 } else { 26527 /* named property */ 26528 if (has_ellipsis) { 26529 /* define the property in excludeList */ 26530 emit_op(s, OP_swap); /* TOS: src excludeList */ 26531 emit_op(s, OP_null); /* TOS: src excludeList null */ 26532 emit_op(s, OP_define_field); /* TOS: src excludeList */ 26533 emit_atom(s, prop_name); 26534 emit_op(s, OP_swap); /* TOS: excludeList src */ 26535 } 26536 /* get the named property from the source object */ 26537 emit_op(s, OP_get_field2); 26538 emit_u32(s, prop_name); 26539 } 26540 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE, export_flag) < 0) 26541 return -1; 26542 if (s->token.val == '}') 26543 break; 26544 /* accept a trailing comma before the '}' */ 26545 if (js_parse_expect(s, ',')) 26546 return -1; 26547 continue; 26548 } 26549 if (prop_name == JS_ATOM_NULL) { 26550 emit_op(s, OP_to_propkey); 26551 if (has_ellipsis) { 26552 /* define the property in excludeList */ 26553 emit_op(s, OP_perm3); 26554 emit_op(s, OP_null); 26555 emit_op(s, OP_define_array_el); 26556 emit_op(s, OP_perm3); 26557 } 26558 /* source prop -- source source prop */ 26559 emit_op(s, OP_dup1); 26560 } else { 26561 if (has_ellipsis) { 26562 /* define the property in excludeList */ 26563 emit_op(s, OP_swap); 26564 emit_op(s, OP_null); 26565 emit_op(s, OP_define_field); 26566 emit_atom(s, prop_name); 26567 emit_op(s, OP_swap); 26568 } 26569 /* source -- source source */ 26570 emit_op(s, OP_dup); 26571 } 26572 if (tok) { 26573 var_name = js_parse_destructuring_var(s, tok, is_arg); 26574 if (var_name == JS_ATOM_NULL) 26575 goto prop_error; 26576 if (need_var_reference(s, tok)) { 26577 /* Must make a reference for proper `with` semantics */ 26578 emit_op(s, OP_scope_get_var); 26579 emit_atom(s, var_name); 26580 emit_u16(s, s->cur_func->scope_level); 26581 JS_FreeAtom(s->ctx, var_name); 26582 goto lvalue1; 26583 } else { 26584 /* no need to make a reference for let/const */ 26585 opcode = OP_scope_get_var; 26586 scope = s->cur_func->scope_level; 26587 label_lvalue = -1; 26588 depth_lvalue = 0; 26589 } 26590 } else { 26591 if (js_parse_left_hand_side_expr(s)) 26592 goto prop_error; 26593 lvalue1: 26594 if (get_lvalue(s, &opcode, &scope, &var_name, 26595 &label_lvalue, &depth_lvalue, FALSE, '{')) 26596 goto prop_error; 26597 /* swap ref and lvalue object if any */ 26598 if (prop_name == JS_ATOM_NULL) { 26599 switch(depth_lvalue) { 26600 case 0: 26601 break; 26602 case 1: 26603 /* source prop x -> x source prop */ 26604 emit_op(s, OP_rot3r); 26605 break; 26606 case 2: 26607 /* source prop x y -> x y source prop */ 26608 emit_op(s, OP_swap2); /* t p2 s p1 */ 26609 break; 26610 case 3: 26611 /* source prop x y z -> x y z source prop */ 26612 emit_op(s, OP_rot5l); 26613 emit_op(s, OP_rot5l); 26614 break; 26615 default: 26616 abort(); 26617 } 26618 } else { 26619 switch(depth_lvalue) { 26620 case 0: 26621 break; 26622 case 1: 26623 /* source x -> x source */ 26624 emit_op(s, OP_swap); 26625 break; 26626 case 2: 26627 /* source x y -> x y source */ 26628 emit_op(s, OP_rot3l); 26629 break; 26630 case 3: 26631 /* source x y z -> x y z source */ 26632 emit_op(s, OP_rot4l); 26633 break; 26634 default: 26635 abort(); 26636 } 26637 } 26638 } 26639 if (prop_name == JS_ATOM_NULL) { 26640 /* computed property name on stack */ 26641 /* XXX: should have OP_get_array_el2x with depth */ 26642 /* source prop -- val */ 26643 emit_op(s, OP_get_array_el); 26644 } else { 26645 /* named property */ 26646 /* XXX: should have OP_get_field2x with depth */ 26647 /* source -- val */ 26648 emit_op(s, OP_get_field); 26649 emit_u32(s, prop_name); 26650 } 26651 } else { 26652 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ 26653 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name)) 26654 goto prop_error; 26655 if ((s->cur_func->js_mode & JS_MODE_STRICT) && 26656 (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) { 26657 js_parse_error(s, "invalid destructuring target"); 26658 goto prop_error; 26659 } 26660 if (has_ellipsis) { 26661 /* define the property in excludeList */ 26662 emit_op(s, OP_swap); 26663 emit_op(s, OP_null); 26664 emit_op(s, OP_define_field); 26665 emit_atom(s, prop_name); 26666 emit_op(s, OP_swap); 26667 } 26668 if (!tok || need_var_reference(s, tok)) { 26669 /* generate reference */ 26670 /* source -- source source */ 26671 emit_op(s, OP_dup); 26672 emit_op(s, OP_scope_get_var); 26673 emit_atom(s, prop_name); 26674 emit_u16(s, s->cur_func->scope_level); 26675 goto lvalue1; 26676 } else { 26677 /* no need to make a reference for let/const */ 26678 var_name = JS_DupAtom(s->ctx, prop_name); 26679 opcode = OP_scope_get_var; 26680 scope = s->cur_func->scope_level; 26681 label_lvalue = -1; 26682 depth_lvalue = 0; 26683 26684 /* source -- source val */ 26685 emit_op(s, OP_get_field2); 26686 emit_u32(s, prop_name); 26687 } 26688 } 26689 set_val: 26690 if (tok) { 26691 if (js_define_var(s, var_name, tok)) 26692 goto var_error; 26693 if (export_flag) { 26694 if (!add_export_entry(s, s->cur_func->module, var_name, var_name, 26695 JS_EXPORT_TYPE_LOCAL)) 26696 goto var_error; 26697 } 26698 scope = s->cur_func->scope_level; /* XXX: check */ 26699 } 26700 if (s->token.val == '=') { /* handle optional default value */ 26701 int label_hasval; 26702 emit_op(s, OP_dup); 26703 emit_op(s, OP_undefined); 26704 emit_op(s, OP_strict_eq); 26705 label_hasval = emit_goto(s, OP_if_false, -1); 26706 if (next_token(s)) 26707 goto var_error; 26708 emit_op(s, OP_drop); 26709 if (js_parse_assign_expr(s)) 26710 goto var_error; 26711 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) 26712 set_object_name(s, var_name); 26713 emit_label(s, label_hasval); 26714 } 26715 /* store value into lvalue object */ 26716 put_lvalue(s, opcode, scope, var_name, label_lvalue, 26717 PUT_LVALUE_NOKEEP_DEPTH, 26718 (tok == TOK_CONST || tok == TOK_LET)); 26719 if (s->token.val == '}') 26720 break; 26721 /* accept a trailing comma before the '}' */ 26722 if (js_parse_expect(s, ',')) 26723 return -1; 26724 } 26725 /* drop the source object */ 26726 emit_op(s, OP_drop); 26727 if (has_ellipsis) { 26728 emit_op(s, OP_drop); /* pop excludeList */ 26729 } 26730 if (next_token(s)) 26731 return -1; 26732 } else if (s->token.val == '[') { 26733 BOOL has_spread; 26734 int enum_depth; 26735 BlockEnv block_env; 26736 26737 if (next_token(s)) 26738 return -1; 26739 /* the block environment is only needed in generators in case 26740 'yield' triggers a 'return' */ 26741 push_break_entry(s->cur_func, &block_env, 26742 JS_ATOM_NULL, -1, -1, 2); 26743 block_env.has_iterator = TRUE; 26744 emit_op(s, OP_for_of_start); 26745 has_spread = FALSE; 26746 while (s->token.val != ']') { 26747 /* get the next value */ 26748 if (s->token.val == TOK_ELLIPSIS) { 26749 if (next_token(s)) 26750 return -1; 26751 if (s->token.val == ',' || s->token.val == ']') 26752 return js_parse_error(s, "missing binding pattern..."); 26753 has_spread = TRUE; 26754 } 26755 if (s->token.val == ',') { 26756 /* do nothing, skip the value, has_spread is false */ 26757 emit_op(s, OP_for_of_next); 26758 emit_u8(s, 0); 26759 emit_op(s, OP_drop); 26760 emit_op(s, OP_drop); 26761 } else if ((s->token.val == '[' || s->token.val == '{') 26762 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || 26763 tok1 == '=' || tok1 == ']')) { 26764 if (has_spread) { 26765 if (tok1 == '=') 26766 return js_parse_error(s, "rest element cannot have a default value"); 26767 js_emit_spread_code(s, 0); 26768 } else { 26769 emit_op(s, OP_for_of_next); 26770 emit_u8(s, 0); 26771 emit_op(s, OP_drop); 26772 } 26773 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0) 26774 return -1; 26775 } else { 26776 var_name = JS_ATOM_NULL; 26777 if (tok) { 26778 var_name = js_parse_destructuring_var(s, tok, is_arg); 26779 if (var_name == JS_ATOM_NULL) 26780 goto var_error; 26781 if (js_define_var(s, var_name, tok)) 26782 goto var_error; 26783 if (need_var_reference(s, tok)) { 26784 /* Must make a reference for proper `with` semantics */ 26785 emit_op(s, OP_scope_get_var); 26786 emit_atom(s, var_name); 26787 emit_u16(s, s->cur_func->scope_level); 26788 JS_FreeAtom(s->ctx, var_name); 26789 goto lvalue2; 26790 } else { 26791 /* no need to make a reference for let/const */ 26792 opcode = OP_scope_get_var; 26793 scope = s->cur_func->scope_level; 26794 label_lvalue = -1; 26795 enum_depth = 0; 26796 } 26797 } else { 26798 if (js_parse_left_hand_side_expr(s)) 26799 return -1; 26800 lvalue2: 26801 if (get_lvalue(s, &opcode, &scope, &var_name, 26802 &label_lvalue, &enum_depth, FALSE, '[')) { 26803 return -1; 26804 } 26805 } 26806 if (has_spread) { 26807 js_emit_spread_code(s, enum_depth); 26808 } else { 26809 emit_op(s, OP_for_of_next); 26810 emit_u8(s, enum_depth); 26811 emit_op(s, OP_drop); 26812 } 26813 if (s->token.val == '=' && !has_spread) { 26814 /* handle optional default value */ 26815 int label_hasval; 26816 emit_op(s, OP_dup); 26817 emit_op(s, OP_undefined); 26818 emit_op(s, OP_strict_eq); 26819 label_hasval = emit_goto(s, OP_if_false, -1); 26820 if (next_token(s)) 26821 goto var_error; 26822 emit_op(s, OP_drop); 26823 if (js_parse_assign_expr(s)) 26824 goto var_error; 26825 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) 26826 set_object_name(s, var_name); 26827 emit_label(s, label_hasval); 26828 } 26829 /* store value into lvalue object */ 26830 put_lvalue(s, opcode, scope, var_name, 26831 label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, 26832 (tok == TOK_CONST || tok == TOK_LET)); 26833 } 26834 if (s->token.val == ']') 26835 break; 26836 if (has_spread) 26837 return js_parse_error(s, "rest element must be the last one"); 26838 /* accept a trailing comma before the ']' */ 26839 if (js_parse_expect(s, ',')) 26840 return -1; 26841 } 26842 /* close iterator object: 26843 if completed, enum_obj has been replaced by undefined */ 26844 emit_op(s, OP_iterator_close); 26845 pop_break_entry(s->cur_func); 26846 if (next_token(s)) 26847 return -1; 26848 } else { 26849 return js_parse_error(s, "invalid assignment syntax"); 26850 } 26851 if (s->token.val == '=' && allow_initializer) { 26852 label_done = emit_goto(s, OP_goto, -1); 26853 if (next_token(s)) 26854 return -1; 26855 emit_label(s, label_parse); 26856 if (hasval) 26857 emit_op(s, OP_drop); 26858 if (js_parse_assign_expr(s)) 26859 return -1; 26860 emit_goto(s, OP_goto, label_assign); 26861 emit_label(s, label_done); 26862 has_initializer = TRUE; 26863 } else { 26864 /* normally hasval is true except if 26865 js_parse_skip_parens_token() was wrong in the parsing */ 26866 // assert(hasval); 26867 if (!hasval) { 26868 js_parse_error(s, "too complicated destructuring expression"); 26869 return -1; 26870 } 26871 /* remove test and decrement label ref count */ 26872 memset(s->cur_func->byte_code.buf + start_addr, OP_nop, 26873 assign_addr - start_addr); 26874 s->cur_func->label_slots[label_parse].ref_count--; 26875 has_initializer = FALSE; 26876 } 26877 return has_initializer; 26878 26879 prop_error: 26880 JS_FreeAtom(s->ctx, prop_name); 26881 var_error: 26882 JS_FreeAtom(s->ctx, var_name); 26883 return -1; 26884 } 26885 26886 typedef enum FuncCallType { 26887 FUNC_CALL_NORMAL, 26888 FUNC_CALL_NEW, 26889 FUNC_CALL_SUPER_CTOR, 26890 FUNC_CALL_TEMPLATE, 26891 } FuncCallType; 26892 26893 static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, 26894 int drop_count) 26895 { 26896 int label_next, i; 26897 if (*poptional_chaining_label < 0) 26898 *poptional_chaining_label = new_label(s); 26899 /* XXX: could be more efficient with a specific opcode */ 26900 emit_op(s, OP_dup); 26901 emit_op(s, OP_is_undefined_or_null); 26902 label_next = emit_goto(s, OP_if_false, -1); 26903 for(i = 0; i < drop_count; i++) 26904 emit_op(s, OP_drop); 26905 emit_op(s, OP_undefined); 26906 emit_goto(s, OP_goto, *poptional_chaining_label); 26907 emit_label(s, label_next); 26908 } 26909 26910 /* allowed parse_flags: PF_POSTFIX_CALL */ 26911 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) 26912 { 26913 FuncCallType call_type; 26914 int optional_chaining_label; 26915 BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; 26916 const uint8_t *op_token_ptr; 26917 26918 call_type = FUNC_CALL_NORMAL; 26919 switch(s->token.val) { 26920 case TOK_NUMBER: 26921 { 26922 JSValue val; 26923 val = s->token.u.num.val; 26924 26925 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { 26926 emit_op(s, OP_push_i32); 26927 emit_u32(s, JS_VALUE_GET_INT(val)); 26928 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) { 26929 int64_t v; 26930 v = JS_VALUE_GET_SHORT_BIG_INT(val); 26931 if (v >= INT32_MIN && v <= INT32_MAX) { 26932 emit_op(s, OP_push_bigint_i32); 26933 emit_u32(s, v); 26934 } else { 26935 goto large_number; 26936 } 26937 } else { 26938 large_number: 26939 if (emit_push_const(s, val, 0) < 0) 26940 return -1; 26941 } 26942 } 26943 if (next_token(s)) 26944 return -1; 26945 break; 26946 case TOK_TEMPLATE: 26947 if (js_parse_template(s, 0, NULL)) 26948 return -1; 26949 break; 26950 case TOK_STRING: 26951 if (emit_push_const(s, s->token.u.str.str, 1)) 26952 return -1; 26953 if (next_token(s)) 26954 return -1; 26955 break; 26956 26957 case TOK_DIV_ASSIGN: 26958 s->buf_ptr -= 2; 26959 goto parse_regexp; 26960 case '/': 26961 s->buf_ptr--; 26962 parse_regexp: 26963 { 26964 JSValue str; 26965 int ret; 26966 if (!s->ctx->compile_regexp) 26967 return js_parse_error(s, "RegExp are not supported"); 26968 /* the previous token is '/' or '/=', so no need to free */ 26969 if (js_parse_regexp(s)) 26970 return -1; 26971 ret = emit_push_const(s, s->token.u.regexp.body, 0); 26972 str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body, 26973 s->token.u.regexp.flags); 26974 if (JS_IsException(str)) { 26975 /* add the line number info */ 26976 int line_num, col_num; 26977 line_num = get_line_col(&col_num, s->buf_start, s->token.ptr - s->buf_start); 26978 build_backtrace(s->ctx, s->ctx->rt->current_exception, 26979 s->filename, line_num + 1, col_num + 1, 0); 26980 return -1; 26981 } 26982 ret = emit_push_const(s, str, 0); 26983 JS_FreeValue(s->ctx, str); 26984 if (ret) 26985 return -1; 26986 /* we use a specific opcode to be sure the correct 26987 function is called (otherwise the bytecode would have 26988 to be verified by the RegExp constructor) */ 26989 emit_op(s, OP_regexp); 26990 if (next_token(s)) 26991 return -1; 26992 } 26993 break; 26994 case '(': 26995 if (js_parse_expr_paren(s)) 26996 return -1; 26997 break; 26998 case TOK_FUNCTION: 26999 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, 27000 JS_FUNC_NORMAL, JS_ATOM_NULL, 27001 s->token.ptr)) 27002 return -1; 27003 break; 27004 case TOK_CLASS: 27005 if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE)) 27006 return -1; 27007 break; 27008 case TOK_NULL: 27009 if (next_token(s)) 27010 return -1; 27011 emit_op(s, OP_null); 27012 break; 27013 case TOK_THIS: 27014 if (next_token(s)) 27015 return -1; 27016 emit_op(s, OP_scope_get_var); 27017 emit_atom(s, JS_ATOM_this); 27018 emit_u16(s, 0); 27019 break; 27020 case TOK_FALSE: 27021 if (next_token(s)) 27022 return -1; 27023 emit_op(s, OP_push_false); 27024 break; 27025 case TOK_TRUE: 27026 if (next_token(s)) 27027 return -1; 27028 emit_op(s, OP_push_true); 27029 break; 27030 case TOK_IDENT: 27031 { 27032 JSAtom name; 27033 const uint8_t *source_ptr; 27034 if (s->token.u.ident.is_reserved) { 27035 return js_parse_error_reserved_identifier(s); 27036 } 27037 source_ptr = s->token.ptr; 27038 if (token_is_pseudo_keyword(s, JS_ATOM_async) && 27039 peek_token(s, TRUE) != '\n') { 27040 if (next_token(s)) 27041 return -1; 27042 if (s->token.val == TOK_FUNCTION) { 27043 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, 27044 JS_FUNC_ASYNC, JS_ATOM_NULL, 27045 source_ptr)) 27046 return -1; 27047 } else { 27048 name = JS_DupAtom(s->ctx, JS_ATOM_async); 27049 goto do_get_var; 27050 } 27051 } else { 27052 if (s->token.u.ident.atom == JS_ATOM_arguments && 27053 !s->cur_func->arguments_allowed) { 27054 js_parse_error(s, "'arguments' identifier is not allowed in class field initializer"); 27055 return -1; 27056 } 27057 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 27058 if (next_token(s)) { 27059 JS_FreeAtom(s->ctx, name); 27060 return -1; 27061 } 27062 do_get_var: 27063 emit_source_pos(s, source_ptr); 27064 emit_op(s, OP_scope_get_var); 27065 emit_u32(s, name); 27066 emit_u16(s, s->cur_func->scope_level); 27067 } 27068 } 27069 break; 27070 case '{': 27071 case '[': 27072 if (s->token.val == '{') { 27073 if (js_parse_object_literal(s)) 27074 return -1; 27075 } else { 27076 if (js_parse_array_literal(s)) 27077 return -1; 27078 } 27079 break; 27080 case TOK_NEW: 27081 if (next_token(s)) 27082 return -1; 27083 if (s->token.val == '.') { 27084 if (next_token(s)) 27085 return -1; 27086 if (!token_is_pseudo_keyword(s, JS_ATOM_target)) 27087 return js_parse_error(s, "expecting target"); 27088 if (!s->cur_func->new_target_allowed) 27089 return js_parse_error(s, "new.target only allowed within functions"); 27090 if (next_token(s)) 27091 return -1; 27092 emit_op(s, OP_scope_get_var); 27093 emit_atom(s, JS_ATOM_new_target); 27094 emit_u16(s, 0); 27095 } else { 27096 if (js_parse_postfix_expr(s, 0)) 27097 return -1; 27098 accept_lparen = TRUE; 27099 if (s->token.val != '(') { 27100 /* new operator on an object */ 27101 emit_source_pos(s, s->token.ptr); 27102 emit_op(s, OP_dup); 27103 emit_op(s, OP_call_constructor); 27104 emit_u16(s, 0); 27105 } else { 27106 call_type = FUNC_CALL_NEW; 27107 } 27108 } 27109 break; 27110 case TOK_SUPER: 27111 if (next_token(s)) 27112 return -1; 27113 if (s->token.val == '(') { 27114 if (!s->cur_func->super_call_allowed) 27115 return js_parse_error(s, "super() is only valid in a derived class constructor"); 27116 call_type = FUNC_CALL_SUPER_CTOR; 27117 } else if (s->token.val == '.' || s->token.val == '[') { 27118 if (!s->cur_func->super_allowed) 27119 return js_parse_error(s, "'super' is only valid in a method"); 27120 emit_op(s, OP_scope_get_var); 27121 emit_atom(s, JS_ATOM_this); 27122 emit_u16(s, 0); 27123 emit_op(s, OP_scope_get_var); 27124 emit_atom(s, JS_ATOM_home_object); 27125 emit_u16(s, 0); 27126 emit_op(s, OP_get_super); 27127 } else { 27128 return js_parse_error(s, "invalid use of 'super'"); 27129 } 27130 break; 27131 case TOK_IMPORT: 27132 if (next_token(s)) 27133 return -1; 27134 if (s->token.val == '.') { 27135 if (next_token(s)) 27136 return -1; 27137 if (!token_is_pseudo_keyword(s, JS_ATOM_meta)) 27138 return js_parse_error(s, "meta expected"); 27139 if (!s->is_module) 27140 return js_parse_error(s, "import.meta only valid in module code"); 27141 if (next_token(s)) 27142 return -1; 27143 emit_op(s, OP_special_object); 27144 emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META); 27145 } else { 27146 if (js_parse_expect(s, '(')) 27147 return -1; 27148 if (!accept_lparen) 27149 return js_parse_error(s, "invalid use of 'import()'"); 27150 if (js_parse_assign_expr(s)) 27151 return -1; 27152 if (s->token.val == ',') { 27153 if (next_token(s)) 27154 return -1; 27155 if (s->token.val != ')') { 27156 if (js_parse_assign_expr(s)) 27157 return -1; 27158 /* accept a trailing comma */ 27159 if (s->token.val == ',') { 27160 if (next_token(s)) 27161 return -1; 27162 } 27163 } else { 27164 emit_op(s, OP_undefined); 27165 } 27166 } else { 27167 emit_op(s, OP_undefined); 27168 } 27169 if (js_parse_expect(s, ')')) 27170 return -1; 27171 emit_op(s, OP_import); 27172 } 27173 break; 27174 default: 27175 return js_parse_error(s, "unexpected token in expression: '%.*s'", 27176 (int)(s->buf_ptr - s->token.ptr), s->token.ptr); 27177 } 27178 27179 optional_chaining_label = -1; 27180 for(;;) { 27181 JSFunctionDef *fd = s->cur_func; 27182 BOOL has_optional_chain = FALSE; 27183 27184 if (s->token.val == TOK_QUESTION_MARK_DOT) { 27185 if ((parse_flags & PF_POSTFIX_CALL) == 0) 27186 return js_parse_error(s, "new keyword cannot be used with an optional chain"); 27187 op_token_ptr = s->token.ptr; 27188 /* optional chaining */ 27189 if (next_token(s)) 27190 return -1; 27191 has_optional_chain = TRUE; 27192 if (s->token.val == '(' && accept_lparen) { 27193 goto parse_func_call; 27194 } else if (s->token.val == '[') { 27195 goto parse_array_access; 27196 } else { 27197 goto parse_property; 27198 } 27199 } else if (s->token.val == TOK_TEMPLATE && 27200 call_type == FUNC_CALL_NORMAL) { 27201 if (optional_chaining_label >= 0) { 27202 return js_parse_error(s, "template literal cannot appear in an optional chain"); 27203 } 27204 call_type = FUNC_CALL_TEMPLATE; 27205 op_token_ptr = s->token.ptr; /* XXX: check if right position */ 27206 goto parse_func_call2; 27207 } else if (s->token.val == '(' && accept_lparen) { 27208 int opcode, arg_count, drop_count; 27209 27210 /* function call */ 27211 parse_func_call: 27212 op_token_ptr = s->token.ptr; 27213 if (next_token(s)) 27214 return -1; 27215 27216 if (call_type == FUNC_CALL_NORMAL) { 27217 parse_func_call2: 27218 switch(opcode = get_prev_opcode(fd)) { 27219 case OP_get_field: 27220 /* keep the object on the stack */ 27221 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; 27222 drop_count = 2; 27223 break; 27224 case OP_get_field_opt_chain: 27225 { 27226 int opt_chain_label, next_label; 27227 opt_chain_label = get_u32(fd->byte_code.buf + 27228 fd->last_opcode_pos + 1 + 4 + 1); 27229 /* keep the object on the stack */ 27230 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; 27231 fd->byte_code.size = fd->last_opcode_pos + 1 + 4; 27232 next_label = emit_goto(s, OP_goto, -1); 27233 emit_label(s, opt_chain_label); 27234 /* need an additional undefined value for the 27235 case where the optional field does not 27236 exists */ 27237 emit_op(s, OP_undefined); 27238 emit_label(s, next_label); 27239 drop_count = 2; 27240 opcode = OP_get_field; 27241 } 27242 break; 27243 case OP_scope_get_private_field: 27244 /* keep the object on the stack */ 27245 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; 27246 drop_count = 2; 27247 break; 27248 case OP_get_array_el: 27249 /* keep the object on the stack */ 27250 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; 27251 drop_count = 2; 27252 break; 27253 case OP_get_array_el_opt_chain: 27254 { 27255 int opt_chain_label, next_label; 27256 opt_chain_label = get_u32(fd->byte_code.buf + 27257 fd->last_opcode_pos + 1 + 1); 27258 /* keep the object on the stack */ 27259 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; 27260 fd->byte_code.size = fd->last_opcode_pos + 1; 27261 next_label = emit_goto(s, OP_goto, -1); 27262 emit_label(s, opt_chain_label); 27263 /* need an additional undefined value for the 27264 case where the optional field does not 27265 exists */ 27266 emit_op(s, OP_undefined); 27267 emit_label(s, next_label); 27268 drop_count = 2; 27269 opcode = OP_get_array_el; 27270 } 27271 break; 27272 case OP_scope_get_var: 27273 { 27274 JSAtom name; 27275 int scope; 27276 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 27277 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 27278 if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { 27279 /* direct 'eval' */ 27280 opcode = OP_eval; 27281 } else { 27282 /* verify if function name resolves to a simple 27283 get_loc/get_arg: a function call inside a `with` 27284 statement can resolve to a method call of the 27285 `with` context object 27286 */ 27287 /* XXX: always generate the OP_scope_get_ref 27288 and remove it in variable resolution 27289 pass ? */ 27290 if (has_with_scope(fd, scope)) { 27291 opcode = OP_scope_get_ref; 27292 fd->byte_code.buf[fd->last_opcode_pos] = opcode; 27293 } 27294 } 27295 drop_count = 1; 27296 } 27297 break; 27298 case OP_get_super_value: 27299 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; 27300 /* on stack: this func_obj */ 27301 opcode = OP_get_array_el; 27302 drop_count = 2; 27303 break; 27304 default: 27305 opcode = OP_invalid; 27306 drop_count = 1; 27307 break; 27308 } 27309 if (has_optional_chain) { 27310 optional_chain_test(s, &optional_chaining_label, 27311 drop_count); 27312 } 27313 } else { 27314 opcode = OP_invalid; 27315 } 27316 27317 if (call_type == FUNC_CALL_TEMPLATE) { 27318 if (js_parse_template(s, 1, &arg_count)) 27319 return -1; 27320 goto emit_func_call; 27321 } else if (call_type == FUNC_CALL_SUPER_CTOR) { 27322 emit_op(s, OP_scope_get_var); 27323 emit_atom(s, JS_ATOM_this_active_func); 27324 emit_u16(s, 0); 27325 27326 emit_op(s, OP_get_super); 27327 27328 emit_op(s, OP_scope_get_var); 27329 emit_atom(s, JS_ATOM_new_target); 27330 emit_u16(s, 0); 27331 } else if (call_type == FUNC_CALL_NEW) { 27332 emit_op(s, OP_dup); /* new.target = function */ 27333 } 27334 27335 /* parse arguments */ 27336 arg_count = 0; 27337 while (s->token.val != ')') { 27338 if (arg_count >= 65535) { 27339 return js_parse_error(s, "Too many call arguments"); 27340 } 27341 if (s->token.val == TOK_ELLIPSIS) 27342 break; 27343 if (js_parse_assign_expr(s)) 27344 return -1; 27345 arg_count++; 27346 if (s->token.val == ')') 27347 break; 27348 /* accept a trailing comma before the ')' */ 27349 if (js_parse_expect(s, ',')) 27350 return -1; 27351 } 27352 if (s->token.val == TOK_ELLIPSIS) { 27353 emit_op(s, OP_array_from); 27354 emit_u16(s, arg_count); 27355 emit_op(s, OP_push_i32); 27356 emit_u32(s, arg_count); 27357 27358 /* on stack: array idx */ 27359 while (s->token.val != ')') { 27360 if (s->token.val == TOK_ELLIPSIS) { 27361 if (next_token(s)) 27362 return -1; 27363 if (js_parse_assign_expr(s)) 27364 return -1; 27365 #if 1 27366 /* XXX: could pass is_last indicator? */ 27367 emit_op(s, OP_append); 27368 #else 27369 int label_next, label_done; 27370 label_next = new_label(s); 27371 label_done = new_label(s); 27372 /* push enumerate object below array/idx pair */ 27373 emit_op(s, OP_for_of_start); 27374 emit_op(s, OP_rot5l); 27375 emit_op(s, OP_rot5l); 27376 emit_label(s, label_next); 27377 /* on stack: enum_rec array idx */ 27378 emit_op(s, OP_for_of_next); 27379 emit_u8(s, 2); 27380 emit_goto(s, OP_if_true, label_done); 27381 /* append element */ 27382 /* enum_rec array idx val -> enum_rec array new_idx */ 27383 emit_op(s, OP_define_array_el); 27384 emit_op(s, OP_inc); 27385 emit_goto(s, OP_goto, label_next); 27386 emit_label(s, label_done); 27387 /* close enumeration, drop enum_rec and idx */ 27388 emit_op(s, OP_drop); /* drop undef */ 27389 emit_op(s, OP_nip1); /* drop enum_rec */ 27390 emit_op(s, OP_nip1); 27391 emit_op(s, OP_nip1); 27392 #endif 27393 } else { 27394 if (js_parse_assign_expr(s)) 27395 return -1; 27396 /* array idx val */ 27397 emit_op(s, OP_define_array_el); 27398 emit_op(s, OP_inc); 27399 } 27400 if (s->token.val == ')') 27401 break; 27402 /* accept a trailing comma before the ')' */ 27403 if (js_parse_expect(s, ',')) 27404 return -1; 27405 } 27406 if (next_token(s)) 27407 return -1; 27408 /* drop the index */ 27409 emit_op(s, OP_drop); 27410 27411 emit_source_pos(s, op_token_ptr); 27412 /* apply function call */ 27413 switch(opcode) { 27414 case OP_get_field: 27415 case OP_scope_get_private_field: 27416 case OP_get_array_el: 27417 case OP_scope_get_ref: 27418 /* obj func array -> func obj array */ 27419 emit_op(s, OP_perm3); 27420 emit_op(s, OP_apply); 27421 emit_u16(s, call_type == FUNC_CALL_NEW); 27422 break; 27423 case OP_eval: 27424 emit_op(s, OP_apply_eval); 27425 emit_u16(s, fd->scope_level); 27426 fd->has_eval_call = TRUE; 27427 break; 27428 default: 27429 if (call_type == FUNC_CALL_SUPER_CTOR) { 27430 emit_op(s, OP_apply); 27431 emit_u16(s, 1); 27432 /* set the 'this' value */ 27433 emit_op(s, OP_dup); 27434 emit_op(s, OP_scope_put_var_init); 27435 emit_atom(s, JS_ATOM_this); 27436 emit_u16(s, 0); 27437 27438 emit_class_field_init(s); 27439 } else if (call_type == FUNC_CALL_NEW) { 27440 /* obj func array -> func obj array */ 27441 emit_op(s, OP_perm3); 27442 emit_op(s, OP_apply); 27443 emit_u16(s, 1); 27444 } else { 27445 /* func array -> func undef array */ 27446 emit_op(s, OP_undefined); 27447 emit_op(s, OP_swap); 27448 emit_op(s, OP_apply); 27449 emit_u16(s, 0); 27450 } 27451 break; 27452 } 27453 } else { 27454 if (next_token(s)) 27455 return -1; 27456 emit_func_call: 27457 emit_source_pos(s, op_token_ptr); 27458 switch(opcode) { 27459 case OP_get_field: 27460 case OP_scope_get_private_field: 27461 case OP_get_array_el: 27462 case OP_scope_get_ref: 27463 emit_op(s, OP_call_method); 27464 emit_u16(s, arg_count); 27465 break; 27466 case OP_eval: 27467 emit_op(s, OP_eval); 27468 emit_u16(s, arg_count); 27469 emit_u16(s, fd->scope_level); 27470 fd->has_eval_call = TRUE; 27471 break; 27472 default: 27473 if (call_type == FUNC_CALL_SUPER_CTOR) { 27474 emit_op(s, OP_call_constructor); 27475 emit_u16(s, arg_count); 27476 27477 /* set the 'this' value */ 27478 emit_op(s, OP_dup); 27479 emit_op(s, OP_scope_put_var_init); 27480 emit_atom(s, JS_ATOM_this); 27481 emit_u16(s, 0); 27482 27483 emit_class_field_init(s); 27484 } else if (call_type == FUNC_CALL_NEW) { 27485 emit_op(s, OP_call_constructor); 27486 emit_u16(s, arg_count); 27487 } else { 27488 emit_op(s, OP_call); 27489 emit_u16(s, arg_count); 27490 } 27491 break; 27492 } 27493 } 27494 call_type = FUNC_CALL_NORMAL; 27495 } else if (s->token.val == '.') { 27496 op_token_ptr = s->token.ptr; 27497 if (next_token(s)) 27498 return -1; 27499 parse_property: 27500 emit_source_pos(s, op_token_ptr); 27501 if (s->token.val == TOK_PRIVATE_NAME) { 27502 /* private class field */ 27503 if (get_prev_opcode(fd) == OP_get_super) { 27504 return js_parse_error(s, "private class field forbidden after super"); 27505 } 27506 if (has_optional_chain) { 27507 optional_chain_test(s, &optional_chaining_label, 1); 27508 } 27509 emit_op(s, OP_scope_get_private_field); 27510 emit_atom(s, s->token.u.ident.atom); 27511 emit_u16(s, s->cur_func->scope_level); 27512 } else { 27513 if (!token_is_ident(s->token.val)) { 27514 return js_parse_error(s, "expecting field name"); 27515 } 27516 if (get_prev_opcode(fd) == OP_get_super) { 27517 JSValue val; 27518 int ret; 27519 val = JS_AtomToValue(s->ctx, s->token.u.ident.atom); 27520 ret = emit_push_const(s, val, 1); 27521 JS_FreeValue(s->ctx, val); 27522 if (ret) 27523 return -1; 27524 emit_op(s, OP_get_super_value); 27525 } else { 27526 if (has_optional_chain) { 27527 optional_chain_test(s, &optional_chaining_label, 1); 27528 } 27529 emit_op(s, OP_get_field); 27530 emit_atom(s, s->token.u.ident.atom); 27531 } 27532 } 27533 if (next_token(s)) 27534 return -1; 27535 } else if (s->token.val == '[') { 27536 int prev_op; 27537 op_token_ptr = s->token.ptr; 27538 parse_array_access: 27539 prev_op = get_prev_opcode(fd); 27540 if (has_optional_chain) { 27541 optional_chain_test(s, &optional_chaining_label, 1); 27542 } 27543 if (next_token(s)) 27544 return -1; 27545 if (js_parse_expr(s)) 27546 return -1; 27547 if (js_parse_expect(s, ']')) 27548 return -1; 27549 emit_source_pos(s, op_token_ptr); 27550 if (prev_op == OP_get_super) { 27551 emit_op(s, OP_get_super_value); 27552 } else { 27553 emit_op(s, OP_get_array_el); 27554 } 27555 } else { 27556 break; 27557 } 27558 } 27559 if (optional_chaining_label >= 0) { 27560 JSFunctionDef *fd = s->cur_func; 27561 int opcode; 27562 emit_label_raw(s, optional_chaining_label); 27563 /* modify the last opcode so that it is an indicator of an 27564 optional chain */ 27565 opcode = get_prev_opcode(fd); 27566 if (opcode == OP_get_field || opcode == OP_get_array_el) { 27567 if (opcode == OP_get_field) 27568 opcode = OP_get_field_opt_chain; 27569 else 27570 opcode = OP_get_array_el_opt_chain; 27571 fd->byte_code.buf[fd->last_opcode_pos] = opcode; 27572 } else { 27573 fd->last_opcode_pos = -1; 27574 } 27575 } 27576 return 0; 27577 } 27578 27579 static __exception int js_parse_delete(JSParseState *s) 27580 { 27581 JSFunctionDef *fd = s->cur_func; 27582 JSAtom name; 27583 int opcode; 27584 27585 if (next_token(s)) 27586 return -1; 27587 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 27588 return -1; 27589 switch(opcode = get_prev_opcode(fd)) { 27590 case OP_get_field: 27591 case OP_get_field_opt_chain: 27592 { 27593 JSValue val; 27594 int ret, opt_chain_label, next_label; 27595 if (opcode == OP_get_field_opt_chain) { 27596 opt_chain_label = get_u32(fd->byte_code.buf + 27597 fd->last_opcode_pos + 1 + 4 + 1); 27598 } else { 27599 opt_chain_label = -1; 27600 } 27601 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 27602 fd->byte_code.size = fd->last_opcode_pos; 27603 val = JS_AtomToValue(s->ctx, name); 27604 ret = emit_push_const(s, val, 1); 27605 JS_FreeValue(s->ctx, val); 27606 JS_FreeAtom(s->ctx, name); 27607 if (ret) 27608 return ret; 27609 emit_op(s, OP_delete); 27610 if (opt_chain_label >= 0) { 27611 next_label = emit_goto(s, OP_goto, -1); 27612 emit_label(s, opt_chain_label); 27613 /* if the optional chain is not taken, return 'true' */ 27614 emit_op(s, OP_drop); 27615 emit_op(s, OP_push_true); 27616 emit_label(s, next_label); 27617 } 27618 fd->last_opcode_pos = -1; 27619 } 27620 break; 27621 case OP_get_array_el: 27622 fd->byte_code.size = fd->last_opcode_pos; 27623 fd->last_opcode_pos = -1; 27624 emit_op(s, OP_delete); 27625 break; 27626 case OP_get_array_el_opt_chain: 27627 { 27628 int opt_chain_label, next_label; 27629 opt_chain_label = get_u32(fd->byte_code.buf + 27630 fd->last_opcode_pos + 1 + 1); 27631 fd->byte_code.size = fd->last_opcode_pos; 27632 emit_op(s, OP_delete); 27633 next_label = emit_goto(s, OP_goto, -1); 27634 emit_label(s, opt_chain_label); 27635 /* if the optional chain is not taken, return 'true' */ 27636 emit_op(s, OP_drop); 27637 emit_op(s, OP_push_true); 27638 emit_label(s, next_label); 27639 fd->last_opcode_pos = -1; 27640 } 27641 break; 27642 case OP_scope_get_var: 27643 /* 'delete this': this is not a reference */ 27644 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 27645 if (name == JS_ATOM_this || name == JS_ATOM_new_target) 27646 goto ret_true; 27647 if (fd->js_mode & JS_MODE_STRICT) { 27648 return js_parse_error(s, "cannot delete a direct reference in strict mode"); 27649 } else { 27650 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var; 27651 } 27652 break; 27653 case OP_scope_get_private_field: 27654 return js_parse_error(s, "cannot delete a private class field"); 27655 case OP_get_super_value: 27656 fd->byte_code.size = fd->last_opcode_pos; 27657 fd->last_opcode_pos = -1; 27658 emit_op(s, OP_throw_error); 27659 emit_atom(s, JS_ATOM_NULL); 27660 emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); 27661 break; 27662 default: 27663 ret_true: 27664 emit_op(s, OP_drop); 27665 emit_op(s, OP_push_true); 27666 break; 27667 } 27668 return 0; 27669 } 27670 27671 /* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ 27672 static __exception int js_parse_unary(JSParseState *s, int parse_flags) 27673 { 27674 int op; 27675 const uint8_t *op_token_ptr; 27676 27677 switch(s->token.val) { 27678 case '+': 27679 case '-': 27680 case '!': 27681 case '~': 27682 case TOK_VOID: 27683 op_token_ptr = s->token.ptr; 27684 op = s->token.val; 27685 if (next_token(s)) 27686 return -1; 27687 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 27688 return -1; 27689 switch(op) { 27690 case '-': 27691 emit_source_pos(s, op_token_ptr); 27692 emit_op(s, OP_neg); 27693 break; 27694 case '+': 27695 emit_source_pos(s, op_token_ptr); 27696 emit_op(s, OP_plus); 27697 break; 27698 case '!': 27699 emit_op(s, OP_lnot); 27700 break; 27701 case '~': 27702 emit_source_pos(s, op_token_ptr); 27703 emit_op(s, OP_not); 27704 break; 27705 case TOK_VOID: 27706 emit_op(s, OP_drop); 27707 emit_op(s, OP_undefined); 27708 break; 27709 default: 27710 abort(); 27711 } 27712 parse_flags = 0; 27713 break; 27714 case TOK_DEC: 27715 case TOK_INC: 27716 { 27717 int opcode, op, scope, label; 27718 JSAtom name; 27719 op = s->token.val; 27720 op_token_ptr = s->token.ptr; 27721 if (next_token(s)) 27722 return -1; 27723 if (js_parse_unary(s, 0)) 27724 return -1; 27725 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) 27726 return -1; 27727 emit_source_pos(s, op_token_ptr); 27728 emit_op(s, OP_dec + op - TOK_DEC); 27729 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, 27730 FALSE); 27731 } 27732 break; 27733 case TOK_TYPEOF: 27734 { 27735 JSFunctionDef *fd; 27736 if (next_token(s)) 27737 return -1; 27738 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 27739 return -1; 27740 /* reference access should not return an exception, so we 27741 patch the get_var */ 27742 fd = s->cur_func; 27743 if (get_prev_opcode(fd) == OP_scope_get_var) { 27744 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; 27745 } 27746 emit_op(s, OP_typeof); 27747 parse_flags = 0; 27748 } 27749 break; 27750 case TOK_DELETE: 27751 if (js_parse_delete(s)) 27752 return -1; 27753 parse_flags = 0; 27754 break; 27755 case TOK_AWAIT: 27756 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) 27757 return js_parse_error(s, "unexpected 'await' keyword"); 27758 if (!s->cur_func->in_function_body) 27759 return js_parse_error(s, "await in default expression"); 27760 if (next_token(s)) 27761 return -1; 27762 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 27763 return -1; 27764 s->cur_func->has_await = TRUE; 27765 emit_op(s, OP_await); 27766 parse_flags = 0; 27767 break; 27768 default: 27769 if (js_parse_postfix_expr(s, PF_POSTFIX_CALL)) 27770 return -1; 27771 if (!s->got_lf && 27772 (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { 27773 int opcode, op, scope, label; 27774 JSAtom name; 27775 op = s->token.val; 27776 op_token_ptr = s->token.ptr; 27777 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) 27778 return -1; 27779 emit_source_pos(s, op_token_ptr); 27780 emit_op(s, OP_post_dec + op - TOK_DEC); 27781 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, 27782 FALSE); 27783 if (next_token(s)) 27784 return -1; 27785 } 27786 break; 27787 } 27788 if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { 27789 if (s->token.val == TOK_POW) { 27790 /* Strict ES7 exponentiation syntax rules: To solve 27791 conficting semantics between different implementations 27792 regarding the precedence of prefix operators and the 27793 postifx exponential, ES7 specifies that -2**2 is a 27794 syntax error. */ 27795 if (parse_flags & PF_POW_FORBIDDEN) { 27796 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); 27797 return -1; 27798 } 27799 op_token_ptr = s->token.ptr; 27800 if (next_token(s)) 27801 return -1; 27802 if (js_parse_unary(s, PF_POW_ALLOWED)) 27803 return -1; 27804 emit_source_pos(s, op_token_ptr); 27805 emit_op(s, OP_pow); 27806 } 27807 } 27808 return 0; 27809 } 27810 27811 /* allowed parse_flags: PF_IN_ACCEPTED */ 27812 static __exception int js_parse_expr_binary(JSParseState *s, int level, 27813 int parse_flags) 27814 { 27815 int op, opcode; 27816 const uint8_t *op_token_ptr; 27817 27818 if (level == 0) { 27819 return js_parse_unary(s, PF_POW_ALLOWED); 27820 } else if (s->token.val == TOK_PRIVATE_NAME && 27821 (parse_flags & PF_IN_ACCEPTED) && level == 4 && 27822 peek_token(s, FALSE) == TOK_IN) { 27823 JSAtom atom; 27824 27825 atom = JS_DupAtom(s->ctx, s->token.u.ident.atom); 27826 if (next_token(s)) 27827 goto fail_private_in; 27828 if (s->token.val != TOK_IN) 27829 goto fail_private_in; 27830 if (next_token(s)) 27831 goto fail_private_in; 27832 if (js_parse_expr_binary(s, level - 1, parse_flags)) { 27833 fail_private_in: 27834 JS_FreeAtom(s->ctx, atom); 27835 return -1; 27836 } 27837 emit_op(s, OP_scope_in_private_field); 27838 emit_atom(s, atom); 27839 emit_u16(s, s->cur_func->scope_level); 27840 JS_FreeAtom(s->ctx, atom); 27841 return 0; 27842 } else { 27843 if (js_parse_expr_binary(s, level - 1, parse_flags)) 27844 return -1; 27845 } 27846 for(;;) { 27847 op = s->token.val; 27848 op_token_ptr = s->token.ptr; 27849 switch(level) { 27850 case 1: 27851 switch(op) { 27852 case '*': 27853 opcode = OP_mul; 27854 break; 27855 case '/': 27856 opcode = OP_div; 27857 break; 27858 case '%': 27859 opcode = OP_mod; 27860 break; 27861 default: 27862 return 0; 27863 } 27864 break; 27865 case 2: 27866 switch(op) { 27867 case '+': 27868 opcode = OP_add; 27869 break; 27870 case '-': 27871 opcode = OP_sub; 27872 break; 27873 default: 27874 return 0; 27875 } 27876 break; 27877 case 3: 27878 switch(op) { 27879 case TOK_SHL: 27880 opcode = OP_shl; 27881 break; 27882 case TOK_SAR: 27883 opcode = OP_sar; 27884 break; 27885 case TOK_SHR: 27886 opcode = OP_shr; 27887 break; 27888 default: 27889 return 0; 27890 } 27891 break; 27892 case 4: 27893 switch(op) { 27894 case '<': 27895 opcode = OP_lt; 27896 break; 27897 case '>': 27898 opcode = OP_gt; 27899 break; 27900 case TOK_LTE: 27901 opcode = OP_lte; 27902 break; 27903 case TOK_GTE: 27904 opcode = OP_gte; 27905 break; 27906 case TOK_INSTANCEOF: 27907 opcode = OP_instanceof; 27908 break; 27909 case TOK_IN: 27910 if (parse_flags & PF_IN_ACCEPTED) { 27911 opcode = OP_in; 27912 } else { 27913 return 0; 27914 } 27915 break; 27916 default: 27917 return 0; 27918 } 27919 break; 27920 case 5: 27921 switch(op) { 27922 case TOK_EQ: 27923 opcode = OP_eq; 27924 break; 27925 case TOK_NEQ: 27926 opcode = OP_neq; 27927 break; 27928 case TOK_STRICT_EQ: 27929 opcode = OP_strict_eq; 27930 break; 27931 case TOK_STRICT_NEQ: 27932 opcode = OP_strict_neq; 27933 break; 27934 default: 27935 return 0; 27936 } 27937 break; 27938 case 6: 27939 switch(op) { 27940 case '&': 27941 opcode = OP_and; 27942 break; 27943 default: 27944 return 0; 27945 } 27946 break; 27947 case 7: 27948 switch(op) { 27949 case '^': 27950 opcode = OP_xor; 27951 break; 27952 default: 27953 return 0; 27954 } 27955 break; 27956 case 8: 27957 switch(op) { 27958 case '|': 27959 opcode = OP_or; 27960 break; 27961 default: 27962 return 0; 27963 } 27964 break; 27965 default: 27966 abort(); 27967 } 27968 if (next_token(s)) 27969 return -1; 27970 if (js_parse_expr_binary(s, level - 1, parse_flags)) 27971 return -1; 27972 emit_source_pos(s, op_token_ptr); 27973 emit_op(s, opcode); 27974 } 27975 return 0; 27976 } 27977 27978 /* allowed parse_flags: PF_IN_ACCEPTED */ 27979 static __exception int js_parse_logical_and_or(JSParseState *s, int op, 27980 int parse_flags) 27981 { 27982 int label1; 27983 27984 if (op == TOK_LAND) { 27985 if (js_parse_expr_binary(s, 8, parse_flags)) 27986 return -1; 27987 } else { 27988 if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) 27989 return -1; 27990 } 27991 if (s->token.val == op) { 27992 label1 = new_label(s); 27993 27994 for(;;) { 27995 if (next_token(s)) 27996 return -1; 27997 emit_op(s, OP_dup); 27998 emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1); 27999 emit_op(s, OP_drop); 28000 28001 if (op == TOK_LAND) { 28002 if (js_parse_expr_binary(s, 8, parse_flags)) 28003 return -1; 28004 } else { 28005 if (js_parse_logical_and_or(s, TOK_LAND, 28006 parse_flags)) 28007 return -1; 28008 } 28009 if (s->token.val != op) { 28010 if (s->token.val == TOK_DOUBLE_QUESTION_MARK) 28011 return js_parse_error(s, "cannot mix ?? with && or ||"); 28012 break; 28013 } 28014 } 28015 28016 emit_label(s, label1); 28017 } 28018 return 0; 28019 } 28020 28021 static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) 28022 { 28023 int label1; 28024 28025 if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) 28026 return -1; 28027 if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { 28028 label1 = new_label(s); 28029 for(;;) { 28030 if (next_token(s)) 28031 return -1; 28032 28033 emit_op(s, OP_dup); 28034 emit_op(s, OP_is_undefined_or_null); 28035 emit_goto(s, OP_if_false, label1); 28036 emit_op(s, OP_drop); 28037 28038 if (js_parse_expr_binary(s, 8, parse_flags)) 28039 return -1; 28040 if (s->token.val != TOK_DOUBLE_QUESTION_MARK) 28041 break; 28042 } 28043 emit_label(s, label1); 28044 } 28045 return 0; 28046 } 28047 28048 /* allowed parse_flags: PF_IN_ACCEPTED */ 28049 static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) 28050 { 28051 int label1, label2; 28052 28053 if (js_parse_coalesce_expr(s, parse_flags)) 28054 return -1; 28055 if (s->token.val == '?') { 28056 if (next_token(s)) 28057 return -1; 28058 label1 = emit_goto(s, OP_if_false, -1); 28059 28060 if (js_parse_assign_expr(s)) 28061 return -1; 28062 if (js_parse_expect(s, ':')) 28063 return -1; 28064 28065 label2 = emit_goto(s, OP_goto, -1); 28066 28067 emit_label(s, label1); 28068 28069 if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) 28070 return -1; 28071 28072 emit_label(s, label2); 28073 } 28074 return 0; 28075 } 28076 28077 /* allowed parse_flags: PF_IN_ACCEPTED */ 28078 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) 28079 { 28080 int opcode, op, scope, skip_bits; 28081 JSAtom name0 = JS_ATOM_NULL; 28082 JSAtom name; 28083 28084 if (s->token.val == TOK_YIELD) { 28085 BOOL is_star = FALSE, is_async; 28086 28087 if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) 28088 return js_parse_error(s, "unexpected 'yield' keyword"); 28089 if (!s->cur_func->in_function_body) 28090 return js_parse_error(s, "yield in default expression"); 28091 if (next_token(s)) 28092 return -1; 28093 /* XXX: is there a better method to detect 'yield' without 28094 parameters ? */ 28095 if (s->token.val != ';' && s->token.val != ')' && 28096 s->token.val != ']' && s->token.val != '}' && 28097 s->token.val != ',' && s->token.val != ':' && !s->got_lf) { 28098 if (s->token.val == '*') { 28099 is_star = TRUE; 28100 if (next_token(s)) 28101 return -1; 28102 } 28103 if (js_parse_assign_expr2(s, parse_flags)) 28104 return -1; 28105 } else { 28106 emit_op(s, OP_undefined); 28107 } 28108 is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); 28109 28110 if (is_star) { 28111 int label_loop, label_return, label_next; 28112 int label_return1, label_yield, label_throw, label_throw1; 28113 int label_throw2; 28114 28115 label_loop = new_label(s); 28116 label_yield = new_label(s); 28117 28118 emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); 28119 28120 /* remove the catch offset (XXX: could avoid pushing back 28121 undefined) */ 28122 emit_op(s, OP_drop); 28123 emit_op(s, OP_undefined); 28124 28125 emit_op(s, OP_undefined); /* initial value */ 28126 28127 emit_label(s, label_loop); 28128 emit_op(s, OP_iterator_next); 28129 if (is_async) 28130 emit_op(s, OP_await); 28131 emit_op(s, OP_iterator_check_object); 28132 emit_op(s, OP_get_field2); 28133 emit_atom(s, JS_ATOM_done); 28134 label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ 28135 emit_label(s, label_yield); 28136 if (is_async) { 28137 /* OP_async_yield_star takes the value as parameter */ 28138 emit_op(s, OP_get_field); 28139 emit_atom(s, JS_ATOM_value); 28140 emit_op(s, OP_async_yield_star); 28141 } else { 28142 /* OP_yield_star takes (value, done) as parameter */ 28143 emit_op(s, OP_yield_star); 28144 } 28145 emit_op(s, OP_dup); 28146 label_return = emit_goto(s, OP_if_true, -1); 28147 emit_op(s, OP_drop); 28148 emit_goto(s, OP_goto, label_loop); 28149 28150 emit_label(s, label_return); 28151 emit_op(s, OP_push_i32); 28152 emit_u32(s, 2); 28153 emit_op(s, OP_strict_eq); 28154 label_throw = emit_goto(s, OP_if_true, -1); 28155 28156 /* return handling */ 28157 if (is_async) 28158 emit_op(s, OP_await); 28159 emit_op(s, OP_iterator_call); 28160 emit_u8(s, 0); 28161 label_return1 = emit_goto(s, OP_if_true, -1); 28162 if (is_async) 28163 emit_op(s, OP_await); 28164 emit_op(s, OP_iterator_check_object); 28165 emit_op(s, OP_get_field2); 28166 emit_atom(s, JS_ATOM_done); 28167 emit_goto(s, OP_if_false, label_yield); 28168 28169 emit_op(s, OP_get_field); 28170 emit_atom(s, JS_ATOM_value); 28171 28172 emit_label(s, label_return1); 28173 emit_op(s, OP_nip); 28174 emit_op(s, OP_nip); 28175 emit_op(s, OP_nip); 28176 emit_return(s, TRUE); 28177 28178 /* throw handling */ 28179 emit_label(s, label_throw); 28180 emit_op(s, OP_iterator_call); 28181 emit_u8(s, 1); 28182 label_throw1 = emit_goto(s, OP_if_true, -1); 28183 if (is_async) 28184 emit_op(s, OP_await); 28185 emit_op(s, OP_iterator_check_object); 28186 emit_op(s, OP_get_field2); 28187 emit_atom(s, JS_ATOM_done); 28188 emit_goto(s, OP_if_false, label_yield); 28189 emit_goto(s, OP_goto, label_next); 28190 /* close the iterator and throw a type error exception */ 28191 emit_label(s, label_throw1); 28192 emit_op(s, OP_iterator_call); 28193 emit_u8(s, 2); 28194 label_throw2 = emit_goto(s, OP_if_true, -1); 28195 if (is_async) 28196 emit_op(s, OP_await); 28197 emit_label(s, label_throw2); 28198 28199 emit_op(s, OP_throw_error); 28200 emit_atom(s, JS_ATOM_NULL); 28201 emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); 28202 28203 emit_label(s, label_next); 28204 emit_op(s, OP_get_field); 28205 emit_atom(s, JS_ATOM_value); 28206 emit_op(s, OP_nip); /* keep the value associated with 28207 done = true */ 28208 emit_op(s, OP_nip); 28209 emit_op(s, OP_nip); 28210 } else { 28211 int label_next; 28212 28213 if (is_async) 28214 emit_op(s, OP_await); 28215 emit_op(s, OP_yield); 28216 label_next = emit_goto(s, OP_if_false, -1); 28217 emit_return(s, TRUE); 28218 emit_label(s, label_next); 28219 } 28220 return 0; 28221 } else if (s->token.val == '(' && 28222 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { 28223 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 28224 JS_FUNC_NORMAL, JS_ATOM_NULL, 28225 s->token.ptr); 28226 } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { 28227 const uint8_t *source_ptr; 28228 int tok; 28229 JSParsePos pos; 28230 28231 /* fast test */ 28232 tok = peek_token(s, TRUE); 28233 if (tok == TOK_FUNCTION || tok == '\n') 28234 goto next; 28235 28236 source_ptr = s->token.ptr; 28237 js_parse_get_pos(s, &pos); 28238 if (next_token(s)) 28239 return -1; 28240 if ((s->token.val == '(' && 28241 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || 28242 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && 28243 peek_token(s, TRUE) == TOK_ARROW)) { 28244 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 28245 JS_FUNC_ASYNC, JS_ATOM_NULL, 28246 source_ptr); 28247 } else { 28248 /* undo the token parsing */ 28249 if (js_parse_seek_token(s, &pos)) 28250 return -1; 28251 } 28252 } else if (s->token.val == TOK_IDENT && 28253 peek_token(s, TRUE) == TOK_ARROW) { 28254 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 28255 JS_FUNC_NORMAL, JS_ATOM_NULL, 28256 s->token.ptr); 28257 } else if ((s->token.val == '{' || s->token.val == '[') && 28258 js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { 28259 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0) 28260 return -1; 28261 return 0; 28262 } 28263 next: 28264 if (s->token.val == TOK_IDENT) { 28265 /* name0 is used to check for OP_set_name pattern, not duplicated */ 28266 name0 = s->token.u.ident.atom; 28267 } 28268 if (js_parse_cond_expr(s, parse_flags)) 28269 return -1; 28270 28271 op = s->token.val; 28272 if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { 28273 int label; 28274 const uint8_t *op_token_ptr; 28275 op_token_ptr = s->token.ptr; 28276 if (next_token(s)) 28277 return -1; 28278 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) 28279 return -1; 28280 28281 if (js_parse_assign_expr2(s, parse_flags)) { 28282 JS_FreeAtom(s->ctx, name); 28283 return -1; 28284 } 28285 28286 if (op == '=') { 28287 if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) { 28288 set_object_name(s, name); 28289 } 28290 } else { 28291 static const uint8_t assign_opcodes[] = { 28292 OP_mul, OP_div, OP_mod, OP_add, OP_sub, 28293 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, 28294 OP_pow, 28295 }; 28296 op = assign_opcodes[op - TOK_MUL_ASSIGN]; 28297 emit_source_pos(s, op_token_ptr); 28298 emit_op(s, op); 28299 } 28300 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); 28301 } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { 28302 int label, label1, depth_lvalue, label2; 28303 28304 if (next_token(s)) 28305 return -1; 28306 if (get_lvalue(s, &opcode, &scope, &name, &label, 28307 &depth_lvalue, TRUE, op) < 0) 28308 return -1; 28309 28310 emit_op(s, OP_dup); 28311 if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) 28312 emit_op(s, OP_is_undefined_or_null); 28313 label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, 28314 -1); 28315 emit_op(s, OP_drop); 28316 28317 if (js_parse_assign_expr2(s, parse_flags)) { 28318 JS_FreeAtom(s->ctx, name); 28319 return -1; 28320 } 28321 28322 if ((opcode == OP_get_ref_value || opcode == OP_scope_get_var) && name == name0) { 28323 set_object_name(s, name); 28324 } 28325 28326 switch(depth_lvalue) { 28327 case 0: 28328 emit_op(s, OP_dup); 28329 break; 28330 case 1: 28331 emit_op(s, OP_insert2); 28332 break; 28333 case 2: 28334 emit_op(s, OP_insert3); 28335 break; 28336 case 3: 28337 emit_op(s, OP_insert4); 28338 break; 28339 default: 28340 abort(); 28341 } 28342 28343 /* XXX: we disable the OP_put_ref_value optimization by not 28344 using put_lvalue() otherwise depth_lvalue is not correct */ 28345 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, 28346 FALSE); 28347 label2 = emit_goto(s, OP_goto, -1); 28348 28349 emit_label(s, label1); 28350 28351 /* remove the lvalue stack entries */ 28352 while (depth_lvalue != 0) { 28353 emit_op(s, OP_nip); 28354 depth_lvalue--; 28355 } 28356 28357 emit_label(s, label2); 28358 } 28359 return 0; 28360 } 28361 28362 static __exception int js_parse_assign_expr(JSParseState *s) 28363 { 28364 return js_parse_assign_expr2(s, PF_IN_ACCEPTED); 28365 } 28366 28367 /* allowed parse_flags: PF_IN_ACCEPTED */ 28368 static __exception int js_parse_expr2(JSParseState *s, int parse_flags) 28369 { 28370 BOOL comma = FALSE; 28371 for(;;) { 28372 if (js_parse_assign_expr2(s, parse_flags)) 28373 return -1; 28374 if (comma) { 28375 /* prevent get_lvalue from using the last expression 28376 as an lvalue. This also prevents the conversion of 28377 of get_var to get_ref for method lookup in function 28378 call inside `with` statement. 28379 */ 28380 s->cur_func->last_opcode_pos = -1; 28381 } 28382 if (s->token.val != ',') 28383 break; 28384 comma = TRUE; 28385 if (next_token(s)) 28386 return -1; 28387 emit_op(s, OP_drop); 28388 } 28389 return 0; 28390 } 28391 28392 static __exception int js_parse_expr(JSParseState *s) 28393 { 28394 return js_parse_expr2(s, PF_IN_ACCEPTED); 28395 } 28396 28397 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, 28398 JSAtom label_name, 28399 int label_break, int label_cont, 28400 int drop_count) 28401 { 28402 be->prev = fd->top_break; 28403 fd->top_break = be; 28404 be->label_name = label_name; 28405 be->label_break = label_break; 28406 be->label_cont = label_cont; 28407 be->drop_count = drop_count; 28408 be->label_finally = -1; 28409 be->scope_level = fd->scope_level; 28410 be->has_iterator = FALSE; 28411 be->is_regular_stmt = FALSE; 28412 } 28413 28414 static void pop_break_entry(JSFunctionDef *fd) 28415 { 28416 BlockEnv *be; 28417 be = fd->top_break; 28418 fd->top_break = be->prev; 28419 } 28420 28421 static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) 28422 { 28423 BlockEnv *top; 28424 int i, scope_level; 28425 28426 scope_level = s->cur_func->scope_level; 28427 top = s->cur_func->top_break; 28428 while (top != NULL) { 28429 close_scopes(s, scope_level, top->scope_level); 28430 scope_level = top->scope_level; 28431 if (is_cont && 28432 top->label_cont != -1 && 28433 (name == JS_ATOM_NULL || top->label_name == name)) { 28434 /* continue stays inside the same block */ 28435 emit_goto(s, OP_goto, top->label_cont); 28436 return 0; 28437 } 28438 if (!is_cont && 28439 top->label_break != -1 && 28440 ((name == JS_ATOM_NULL && !top->is_regular_stmt) || 28441 top->label_name == name)) { 28442 emit_goto(s, OP_goto, top->label_break); 28443 return 0; 28444 } 28445 i = 0; 28446 if (top->has_iterator) { 28447 emit_op(s, OP_iterator_close); 28448 i += 3; 28449 } 28450 for(; i < top->drop_count; i++) 28451 emit_op(s, OP_drop); 28452 if (top->label_finally != -1) { 28453 /* must push dummy value to keep same stack depth */ 28454 emit_op(s, OP_undefined); 28455 emit_goto(s, OP_gosub, top->label_finally); 28456 emit_op(s, OP_drop); 28457 } 28458 top = top->prev; 28459 } 28460 if (name == JS_ATOM_NULL) { 28461 if (is_cont) 28462 return js_parse_error(s, "continue must be inside loop"); 28463 else 28464 return js_parse_error(s, "break must be inside loop or switch"); 28465 } else { 28466 return js_parse_error(s, "break/continue label not found"); 28467 } 28468 } 28469 28470 /* execute the finally blocks before return */ 28471 static void emit_return(JSParseState *s, BOOL hasval) 28472 { 28473 BlockEnv *top; 28474 28475 if (s->cur_func->func_kind != JS_FUNC_NORMAL) { 28476 if (!hasval) { 28477 /* no value: direct return in case of async generator */ 28478 emit_op(s, OP_undefined); 28479 hasval = TRUE; 28480 } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { 28481 /* the await must be done before handling the "finally" in 28482 case it raises an exception */ 28483 emit_op(s, OP_await); 28484 } 28485 } 28486 28487 top = s->cur_func->top_break; 28488 while (top != NULL) { 28489 if (top->has_iterator || top->label_finally != -1) { 28490 if (!hasval) { 28491 emit_op(s, OP_undefined); 28492 hasval = TRUE; 28493 } 28494 /* Remove the stack elements up to and including the catch 28495 offset. When 'yield' is used in an expression we have 28496 no easy way to count them, so we use this specific 28497 instruction instead. */ 28498 emit_op(s, OP_nip_catch); 28499 /* stack: iter_obj next ret_val */ 28500 if (top->has_iterator) { 28501 if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { 28502 int label_next, label_next2; 28503 emit_op(s, OP_nip); /* next */ 28504 emit_op(s, OP_swap); 28505 emit_op(s, OP_get_field2); 28506 emit_atom(s, JS_ATOM_return); 28507 /* stack: iter_obj return_func */ 28508 emit_op(s, OP_dup); 28509 emit_op(s, OP_is_undefined_or_null); 28510 label_next = emit_goto(s, OP_if_true, -1); 28511 emit_op(s, OP_call_method); 28512 emit_u16(s, 0); 28513 emit_op(s, OP_iterator_check_object); 28514 emit_op(s, OP_await); 28515 label_next2 = emit_goto(s, OP_goto, -1); 28516 emit_label(s, label_next); 28517 emit_op(s, OP_drop); 28518 emit_label(s, label_next2); 28519 emit_op(s, OP_drop); 28520 } else { 28521 emit_op(s, OP_rot3r); 28522 emit_op(s, OP_undefined); /* dummy catch offset */ 28523 emit_op(s, OP_iterator_close); 28524 } 28525 } else { 28526 /* execute the "finally" block */ 28527 emit_goto(s, OP_gosub, top->label_finally); 28528 } 28529 } 28530 top = top->prev; 28531 } 28532 if (s->cur_func->is_derived_class_constructor) { 28533 int label_return; 28534 28535 /* 'this' can be uninitialized, so it may be accessed only if 28536 the derived class constructor does not return an object */ 28537 if (hasval) { 28538 emit_op(s, OP_check_ctor_return); 28539 label_return = emit_goto(s, OP_if_false, -1); 28540 emit_op(s, OP_drop); 28541 } else { 28542 label_return = -1; 28543 } 28544 28545 /* The error should be raised in the caller context, so we use 28546 a specific opcode */ 28547 emit_op(s, OP_scope_get_var_checkthis); 28548 emit_atom(s, JS_ATOM_this); 28549 emit_u16(s, 0); 28550 28551 emit_label(s, label_return); 28552 emit_op(s, OP_return); 28553 } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { 28554 emit_op(s, OP_return_async); 28555 } else { 28556 emit_op(s, hasval ? OP_return : OP_return_undef); 28557 } 28558 } 28559 28560 #define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ 28561 /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ 28562 #define DECL_MASK_FUNC_WITH_LABEL (1 << 1) 28563 #define DECL_MASK_OTHER (1 << 2) /* all other declarations */ 28564 #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) 28565 28566 static __exception int js_parse_statement_or_decl(JSParseState *s, 28567 int decl_mask); 28568 28569 static __exception int js_parse_statement(JSParseState *s) 28570 { 28571 return js_parse_statement_or_decl(s, 0); 28572 } 28573 28574 static __exception int js_parse_block(JSParseState *s) 28575 { 28576 if (js_parse_expect(s, '{')) 28577 return -1; 28578 if (s->token.val != '}') { 28579 push_scope(s); 28580 for(;;) { 28581 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 28582 return -1; 28583 if (s->token.val == '}') 28584 break; 28585 } 28586 pop_scope(s); 28587 } 28588 if (next_token(s)) 28589 return -1; 28590 return 0; 28591 } 28592 28593 /* allowed parse_flags: PF_IN_ACCEPTED */ 28594 static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, 28595 BOOL export_flag) 28596 { 28597 JSContext *ctx = s->ctx; 28598 JSFunctionDef *fd = s->cur_func; 28599 JSAtom name = JS_ATOM_NULL; 28600 28601 for (;;) { 28602 if (s->token.val == TOK_IDENT) { 28603 if (s->token.u.ident.is_reserved) { 28604 return js_parse_error_reserved_identifier(s); 28605 } 28606 name = JS_DupAtom(ctx, s->token.u.ident.atom); 28607 if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { 28608 js_parse_error(s, "'let' is not a valid lexical identifier"); 28609 goto var_error; 28610 } 28611 if (next_token(s)) 28612 goto var_error; 28613 if (js_define_var(s, name, tok)) 28614 goto var_error; 28615 if (export_flag) { 28616 if (!add_export_entry(s, s->cur_func->module, name, name, 28617 JS_EXPORT_TYPE_LOCAL)) 28618 goto var_error; 28619 } 28620 28621 if (s->token.val == '=') { 28622 const uint8_t *source_ptr = s->token.ptr; 28623 if (next_token(s)) 28624 goto var_error; 28625 if (need_var_reference(s, tok)) { 28626 /* Must make a reference for proper `with` semantics */ 28627 int opcode, scope, label; 28628 JSAtom name1; 28629 28630 emit_op(s, OP_scope_get_var); 28631 emit_atom(s, name); 28632 emit_u16(s, fd->scope_level); 28633 if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) 28634 goto var_error; 28635 if (js_parse_assign_expr2(s, parse_flags)) { 28636 JS_FreeAtom(ctx, name1); 28637 goto var_error; 28638 } 28639 set_object_name(s, name); 28640 emit_source_pos(s, source_ptr); 28641 put_lvalue(s, opcode, scope, name1, label, 28642 PUT_LVALUE_NOKEEP, FALSE); 28643 } else { 28644 if (js_parse_assign_expr2(s, parse_flags)) 28645 goto var_error; 28646 set_object_name(s, name); 28647 emit_source_pos(s, source_ptr); 28648 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? 28649 OP_scope_put_var_init : OP_scope_put_var); 28650 emit_atom(s, name); 28651 emit_u16(s, fd->scope_level); 28652 } 28653 } else { 28654 if (tok == TOK_CONST) { 28655 js_parse_error(s, "missing initializer for const variable"); 28656 goto var_error; 28657 } 28658 if (tok == TOK_LET) { 28659 /* initialize lexical variable upon entering its scope */ 28660 emit_op(s, OP_undefined); 28661 emit_op(s, OP_scope_put_var_init); 28662 emit_atom(s, name); 28663 emit_u16(s, fd->scope_level); 28664 } 28665 } 28666 JS_FreeAtom(ctx, name); 28667 } else { 28668 int skip_bits; 28669 if ((s->token.val == '[' || s->token.val == '{') 28670 && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { 28671 emit_op(s, OP_undefined); 28672 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0) 28673 return -1; 28674 } else { 28675 return js_parse_error(s, "variable name expected"); 28676 } 28677 } 28678 if (s->token.val != ',') 28679 break; 28680 if (next_token(s)) 28681 return -1; 28682 } 28683 return 0; 28684 28685 var_error: 28686 JS_FreeAtom(ctx, name); 28687 return -1; 28688 } 28689 28690 /* test if the current token is a label. Use simplistic look-ahead scanner */ 28691 static BOOL is_label(JSParseState *s) 28692 { 28693 return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && 28694 peek_token(s, FALSE) == ':'); 28695 } 28696 28697 /* test if the current token is a let keyword. Use simplistic look-ahead scanner */ 28698 static int is_let(JSParseState *s, int decl_mask) 28699 { 28700 int res = FALSE; 28701 const uint8_t *last_token_ptr; 28702 28703 if (token_is_pseudo_keyword(s, JS_ATOM_let)) { 28704 JSParsePos pos; 28705 js_parse_get_pos(s, &pos); 28706 for (;;) { 28707 last_token_ptr = s->token.ptr; 28708 if (next_token(s)) { 28709 res = -1; 28710 break; 28711 } 28712 if (s->token.val == '[') { 28713 /* let [ is a syntax restriction: 28714 it never introduces an ExpressionStatement */ 28715 res = TRUE; 28716 break; 28717 } 28718 if (s->token.val == '{' || 28719 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) || 28720 s->token.val == TOK_LET || 28721 s->token.val == TOK_YIELD || 28722 s->token.val == TOK_AWAIT) { 28723 /* Check for possible ASI if not scanning for Declaration */ 28724 /* XXX: should also check that `{` introduces a BindingPattern, 28725 but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ 28726 if (!has_lf_in_range(last_token_ptr, s->token.ptr) || 28727 (decl_mask & DECL_MASK_OTHER)) { 28728 res = TRUE; 28729 break; 28730 } 28731 break; 28732 } 28733 break; 28734 } 28735 if (js_parse_seek_token(s, &pos)) { 28736 res = -1; 28737 } 28738 } 28739 return res; 28740 } 28741 28742 /* XXX: handle IteratorClose when exiting the loop before the 28743 enumeration is done */ 28744 static __exception int js_parse_for_in_of(JSParseState *s, int label_name, 28745 BOOL is_async) 28746 { 28747 JSContext *ctx = s->ctx; 28748 JSFunctionDef *fd = s->cur_func; 28749 JSAtom var_name; 28750 BOOL has_initializer, is_for_of, has_destructuring; 28751 int tok, tok1, opcode, scope, block_scope_level; 28752 int label_next, label_expr, label_cont, label_body, label_break; 28753 int pos_next, pos_expr; 28754 BlockEnv break_entry; 28755 28756 has_initializer = FALSE; 28757 has_destructuring = FALSE; 28758 is_for_of = FALSE; 28759 block_scope_level = fd->scope_level; 28760 label_cont = new_label(s); 28761 label_body = new_label(s); 28762 label_break = new_label(s); 28763 label_next = new_label(s); 28764 28765 /* create scope for the lexical variables declared in the enumeration 28766 expressions. XXX: Not completely correct because of weird capturing 28767 semantics in `for (i of o) a.push(function(){return i})` */ 28768 push_scope(s); 28769 28770 /* local for_in scope starts here so individual elements 28771 can be closed in statement. */ 28772 push_break_entry(s->cur_func, &break_entry, 28773 label_name, label_break, label_cont, 1); 28774 break_entry.scope_level = block_scope_level; 28775 28776 label_expr = emit_goto(s, OP_goto, -1); 28777 28778 pos_next = s->cur_func->byte_code.size; 28779 emit_label(s, label_next); 28780 28781 tok = s->token.val; 28782 switch (is_let(s, DECL_MASK_OTHER)) { 28783 case TRUE: 28784 tok = TOK_LET; 28785 break; 28786 case FALSE: 28787 break; 28788 default: 28789 return -1; 28790 } 28791 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { 28792 if (next_token(s)) 28793 return -1; 28794 28795 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { 28796 if (s->token.val == '[' || s->token.val == '{') { 28797 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0) 28798 return -1; 28799 has_destructuring = TRUE; 28800 } else { 28801 return js_parse_error(s, "variable name expected"); 28802 } 28803 var_name = JS_ATOM_NULL; 28804 } else { 28805 var_name = JS_DupAtom(ctx, s->token.u.ident.atom); 28806 if (next_token(s)) { 28807 JS_FreeAtom(s->ctx, var_name); 28808 return -1; 28809 } 28810 if (js_define_var(s, var_name, tok)) { 28811 JS_FreeAtom(s->ctx, var_name); 28812 return -1; 28813 } 28814 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? 28815 OP_scope_put_var_init : OP_scope_put_var); 28816 emit_atom(s, var_name); 28817 emit_u16(s, fd->scope_level); 28818 } 28819 } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && 28820 peek_token(s, FALSE) == TOK_OF) { 28821 return js_parse_error(s, "'for of' expression cannot start with 'async'"); 28822 } else { 28823 int skip_bits; 28824 if ((s->token.val == '[' || s->token.val == '{') 28825 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { 28826 if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0) 28827 return -1; 28828 } else { 28829 int lvalue_label; 28830 if (js_parse_left_hand_side_expr(s)) 28831 return -1; 28832 if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, 28833 NULL, FALSE, TOK_FOR)) 28834 return -1; 28835 put_lvalue(s, opcode, scope, var_name, lvalue_label, 28836 PUT_LVALUE_NOKEEP_BOTTOM, FALSE); 28837 } 28838 var_name = JS_ATOM_NULL; 28839 } 28840 emit_goto(s, OP_goto, label_body); 28841 28842 pos_expr = s->cur_func->byte_code.size; 28843 emit_label(s, label_expr); 28844 if (s->token.val == '=') { 28845 /* XXX: potential scoping issue if inside `with` statement */ 28846 has_initializer = TRUE; 28847 /* parse and evaluate initializer prior to evaluating the 28848 object (only used with "for in" with a non lexical variable 28849 in non strict mode */ 28850 if (next_token(s) || js_parse_assign_expr2(s, 0)) { 28851 JS_FreeAtom(ctx, var_name); 28852 return -1; 28853 } 28854 if (var_name != JS_ATOM_NULL) { 28855 emit_op(s, OP_scope_put_var); 28856 emit_atom(s, var_name); 28857 emit_u16(s, fd->scope_level); 28858 } 28859 } 28860 JS_FreeAtom(ctx, var_name); 28861 28862 if (token_is_pseudo_keyword(s, JS_ATOM_of)) { 28863 is_for_of = TRUE; 28864 if (has_initializer) 28865 goto initializer_error; 28866 } else if (s->token.val == TOK_IN) { 28867 if (is_async) 28868 return js_parse_error(s, "'for await' loop should be used with 'of'"); 28869 if (has_initializer && 28870 (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) || 28871 has_destructuring)) { 28872 initializer_error: 28873 return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer", 28874 is_for_of ? "of" : "in"); 28875 } 28876 } else { 28877 return js_parse_error(s, "expected 'of' or 'in' in for control expression"); 28878 } 28879 if (next_token(s)) 28880 return -1; 28881 if (is_for_of) { 28882 if (js_parse_assign_expr(s)) 28883 return -1; 28884 } else { 28885 if (js_parse_expr(s)) 28886 return -1; 28887 } 28888 /* close the scope after having evaluated the expression so that 28889 the TDZ values are in the closures */ 28890 close_scopes(s, s->cur_func->scope_level, block_scope_level); 28891 if (is_for_of) { 28892 /* set has_iterator after the iterable expression is parsed so 28893 that a yield in the expression does not try to close a 28894 not-yet-created iterator */ 28895 break_entry.has_iterator = TRUE; 28896 break_entry.drop_count += 2; 28897 if (is_async) 28898 emit_op(s, OP_for_await_of_start); 28899 else 28900 emit_op(s, OP_for_of_start); 28901 /* on stack: enum_rec */ 28902 } else { 28903 emit_op(s, OP_for_in_start); 28904 /* on stack: enum_obj */ 28905 } 28906 emit_goto(s, OP_goto, label_cont); 28907 28908 if (js_parse_expect(s, ')')) 28909 return -1; 28910 28911 if (OPTIMIZE) { 28912 /* move the `next` code here */ 28913 DynBuf *bc = &s->cur_func->byte_code; 28914 int chunk_size = pos_expr - pos_next; 28915 int offset = bc->size - pos_next; 28916 int i; 28917 if (dbuf_claim(bc, chunk_size)) 28918 return -1; 28919 dbuf_put(bc, bc->buf + pos_next, chunk_size); 28920 memset(bc->buf + pos_next, OP_nop, chunk_size); 28921 /* `next` part ends with a goto */ 28922 s->cur_func->last_opcode_pos = bc->size - 5; 28923 /* relocate labels */ 28924 for (i = label_cont; i < s->cur_func->label_count; i++) { 28925 LabelSlot *ls = &s->cur_func->label_slots[i]; 28926 if (ls->pos >= pos_next && ls->pos < pos_expr) 28927 ls->pos += offset; 28928 } 28929 } 28930 28931 emit_label(s, label_body); 28932 if (js_parse_statement(s)) 28933 return -1; 28934 28935 close_scopes(s, s->cur_func->scope_level, block_scope_level); 28936 28937 emit_label(s, label_cont); 28938 if (is_for_of) { 28939 if (is_async) { 28940 /* stack: iter_obj next catch_offset */ 28941 /* call the next method */ 28942 emit_op(s, OP_for_await_of_next); 28943 /* get the result of the promise */ 28944 emit_op(s, OP_await); 28945 /* unwrap the value and done values */ 28946 emit_op(s, OP_iterator_get_value_done); 28947 } else { 28948 emit_op(s, OP_for_of_next); 28949 emit_u8(s, 0); 28950 } 28951 } else { 28952 emit_op(s, OP_for_in_next); 28953 } 28954 /* on stack: enum_rec / enum_obj value bool */ 28955 emit_goto(s, OP_if_false, label_next); 28956 /* drop the undefined value from for_xx_next */ 28957 emit_op(s, OP_drop); 28958 28959 emit_label(s, label_break); 28960 if (is_for_of) { 28961 /* close and drop enum_rec */ 28962 emit_op(s, OP_iterator_close); 28963 } else { 28964 emit_op(s, OP_drop); 28965 } 28966 pop_break_entry(s->cur_func); 28967 pop_scope(s); 28968 return 0; 28969 } 28970 28971 static void set_eval_ret_undefined(JSParseState *s) 28972 { 28973 if (s->cur_func->eval_ret_idx >= 0) { 28974 emit_op(s, OP_undefined); 28975 emit_op(s, OP_put_loc); 28976 emit_u16(s, s->cur_func->eval_ret_idx); 28977 } 28978 } 28979 28980 static __exception int js_parse_statement_or_decl(JSParseState *s, 28981 int decl_mask) 28982 { 28983 JSContext *ctx = s->ctx; 28984 JSAtom label_name; 28985 int tok; 28986 28987 /* specific label handling */ 28988 /* XXX: support multiple labels on loop statements */ 28989 label_name = JS_ATOM_NULL; 28990 if (is_label(s)) { 28991 BlockEnv *be; 28992 28993 label_name = JS_DupAtom(ctx, s->token.u.ident.atom); 28994 28995 for (be = s->cur_func->top_break; be; be = be->prev) { 28996 if (be->label_name == label_name) { 28997 js_parse_error(s, "duplicate label name"); 28998 goto fail; 28999 } 29000 } 29001 29002 if (next_token(s)) 29003 goto fail; 29004 if (js_parse_expect(s, ':')) 29005 goto fail; 29006 if (s->token.val != TOK_FOR 29007 && s->token.val != TOK_DO 29008 && s->token.val != TOK_WHILE) { 29009 /* labelled regular statement */ 29010 int label_break, mask; 29011 BlockEnv break_entry; 29012 29013 label_break = new_label(s); 29014 push_break_entry(s->cur_func, &break_entry, 29015 label_name, label_break, -1, 0); 29016 break_entry.is_regular_stmt = TRUE; 29017 if (!(s->cur_func->js_mode & JS_MODE_STRICT) && 29018 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { 29019 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; 29020 } else { 29021 mask = 0; 29022 } 29023 if (js_parse_statement_or_decl(s, mask)) 29024 goto fail; 29025 emit_label(s, label_break); 29026 pop_break_entry(s->cur_func); 29027 goto done; 29028 } 29029 } 29030 29031 switch(tok = s->token.val) { 29032 case '{': 29033 if (js_parse_block(s)) 29034 goto fail; 29035 break; 29036 case TOK_RETURN: 29037 { 29038 const uint8_t *op_token_ptr; 29039 if (s->cur_func->is_eval) { 29040 js_parse_error(s, "return not in a function"); 29041 goto fail; 29042 } 29043 if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { 29044 js_parse_error(s, "return in a static initializer block"); 29045 goto fail; 29046 } 29047 op_token_ptr = s->token.ptr; 29048 if (next_token(s)) 29049 goto fail; 29050 if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { 29051 if (js_parse_expr(s)) 29052 goto fail; 29053 emit_source_pos(s, op_token_ptr); 29054 emit_return(s, TRUE); 29055 } else { 29056 emit_source_pos(s, op_token_ptr); 29057 emit_return(s, FALSE); 29058 } 29059 if (js_parse_expect_semi(s)) 29060 goto fail; 29061 } 29062 break; 29063 case TOK_THROW: 29064 { 29065 const uint8_t *op_token_ptr; 29066 op_token_ptr = s->token.ptr; 29067 if (next_token(s)) 29068 goto fail; 29069 if (s->got_lf) { 29070 js_parse_error(s, "line terminator not allowed after throw"); 29071 goto fail; 29072 } 29073 if (js_parse_expr(s)) 29074 goto fail; 29075 emit_source_pos(s, op_token_ptr); 29076 emit_op(s, OP_throw); 29077 if (js_parse_expect_semi(s)) 29078 goto fail; 29079 } 29080 break; 29081 case TOK_LET: 29082 case TOK_CONST: 29083 haslet: 29084 if (!(decl_mask & DECL_MASK_OTHER)) { 29085 js_parse_error(s, "lexical declarations can't appear in single-statement context"); 29086 goto fail; 29087 } 29088 /* fall thru */ 29089 case TOK_VAR: 29090 if (next_token(s)) 29091 goto fail; 29092 if (js_parse_var(s, TRUE, tok, FALSE)) 29093 goto fail; 29094 if (js_parse_expect_semi(s)) 29095 goto fail; 29096 break; 29097 case TOK_IF: 29098 { 29099 int label1, label2, mask; 29100 if (next_token(s)) 29101 goto fail; 29102 /* create a new scope for `let f;if(1) function f(){}` */ 29103 push_scope(s); 29104 set_eval_ret_undefined(s); 29105 if (js_parse_expr_paren(s)) 29106 goto fail; 29107 label1 = emit_goto(s, OP_if_false, -1); 29108 if (s->cur_func->js_mode & JS_MODE_STRICT) 29109 mask = 0; 29110 else 29111 mask = DECL_MASK_FUNC; /* Annex B.3.4 */ 29112 29113 if (js_parse_statement_or_decl(s, mask)) 29114 goto fail; 29115 29116 if (s->token.val == TOK_ELSE) { 29117 label2 = emit_goto(s, OP_goto, -1); 29118 if (next_token(s)) 29119 goto fail; 29120 29121 emit_label(s, label1); 29122 if (js_parse_statement_or_decl(s, mask)) 29123 goto fail; 29124 29125 label1 = label2; 29126 } 29127 emit_label(s, label1); 29128 pop_scope(s); 29129 } 29130 break; 29131 case TOK_WHILE: 29132 { 29133 int label_cont, label_break; 29134 BlockEnv break_entry; 29135 29136 label_cont = new_label(s); 29137 label_break = new_label(s); 29138 29139 push_break_entry(s->cur_func, &break_entry, 29140 label_name, label_break, label_cont, 0); 29141 29142 if (next_token(s)) 29143 goto fail; 29144 29145 set_eval_ret_undefined(s); 29146 29147 emit_label(s, label_cont); 29148 if (js_parse_expr_paren(s)) 29149 goto fail; 29150 emit_goto(s, OP_if_false, label_break); 29151 29152 if (js_parse_statement(s)) 29153 goto fail; 29154 emit_goto(s, OP_goto, label_cont); 29155 29156 emit_label(s, label_break); 29157 29158 pop_break_entry(s->cur_func); 29159 } 29160 break; 29161 case TOK_DO: 29162 { 29163 int label_cont, label_break, label1; 29164 BlockEnv break_entry; 29165 29166 label_cont = new_label(s); 29167 label_break = new_label(s); 29168 label1 = new_label(s); 29169 29170 push_break_entry(s->cur_func, &break_entry, 29171 label_name, label_break, label_cont, 0); 29172 29173 if (next_token(s)) 29174 goto fail; 29175 29176 emit_label(s, label1); 29177 29178 set_eval_ret_undefined(s); 29179 29180 if (js_parse_statement(s)) 29181 goto fail; 29182 29183 emit_label(s, label_cont); 29184 if (js_parse_expect(s, TOK_WHILE)) 29185 goto fail; 29186 if (js_parse_expr_paren(s)) 29187 goto fail; 29188 /* Insert semicolon if missing */ 29189 if (s->token.val == ';') { 29190 if (next_token(s)) 29191 goto fail; 29192 } 29193 emit_goto(s, OP_if_true, label1); 29194 29195 emit_label(s, label_break); 29196 29197 pop_break_entry(s->cur_func); 29198 } 29199 break; 29200 case TOK_FOR: 29201 { 29202 int label_cont, label_break, label_body, label_test; 29203 int pos_cont, pos_body, block_scope_level; 29204 BlockEnv break_entry; 29205 int tok, bits; 29206 BOOL is_async; 29207 29208 if (next_token(s)) 29209 goto fail; 29210 29211 set_eval_ret_undefined(s); 29212 bits = 0; 29213 is_async = FALSE; 29214 if (s->token.val == '(') { 29215 js_parse_skip_parens_token(s, &bits, FALSE); 29216 } else if (s->token.val == TOK_AWAIT) { 29217 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) { 29218 js_parse_error(s, "for await is only valid in asynchronous functions"); 29219 goto fail; 29220 } 29221 is_async = TRUE; 29222 if (next_token(s)) 29223 goto fail; 29224 s->cur_func->has_await = TRUE; 29225 } 29226 if (js_parse_expect(s, '(')) 29227 goto fail; 29228 29229 if (!(bits & SKIP_HAS_SEMI)) { 29230 /* parse for/in or for/of */ 29231 if (js_parse_for_in_of(s, label_name, is_async)) 29232 goto fail; 29233 break; 29234 } 29235 block_scope_level = s->cur_func->scope_level; 29236 29237 /* create scope for the lexical variables declared in the initial, 29238 test and increment expressions */ 29239 push_scope(s); 29240 /* initial expression */ 29241 tok = s->token.val; 29242 if (tok != ';') { 29243 switch (is_let(s, DECL_MASK_OTHER)) { 29244 case TRUE: 29245 tok = TOK_LET; 29246 break; 29247 case FALSE: 29248 break; 29249 default: 29250 goto fail; 29251 } 29252 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { 29253 if (next_token(s)) 29254 goto fail; 29255 if (js_parse_var(s, FALSE, tok, FALSE)) 29256 goto fail; 29257 } else { 29258 if (js_parse_expr2(s, FALSE)) 29259 goto fail; 29260 emit_op(s, OP_drop); 29261 } 29262 29263 /* close the closures before the first iteration */ 29264 close_scopes(s, s->cur_func->scope_level, block_scope_level); 29265 } 29266 if (js_parse_expect(s, ';')) 29267 goto fail; 29268 29269 label_test = new_label(s); 29270 label_cont = new_label(s); 29271 label_body = new_label(s); 29272 label_break = new_label(s); 29273 29274 push_break_entry(s->cur_func, &break_entry, 29275 label_name, label_break, label_cont, 0); 29276 29277 /* test expression */ 29278 if (s->token.val == ';') { 29279 /* no test expression */ 29280 label_test = label_body; 29281 } else { 29282 emit_label(s, label_test); 29283 if (js_parse_expr(s)) 29284 goto fail; 29285 emit_goto(s, OP_if_false, label_break); 29286 } 29287 if (js_parse_expect(s, ';')) 29288 goto fail; 29289 29290 if (s->token.val == ')') { 29291 /* no end expression */ 29292 break_entry.label_cont = label_cont = label_test; 29293 pos_cont = 0; /* avoid warning */ 29294 } else { 29295 /* skip the end expression */ 29296 emit_goto(s, OP_goto, label_body); 29297 29298 pos_cont = s->cur_func->byte_code.size; 29299 emit_label(s, label_cont); 29300 if (js_parse_expr(s)) 29301 goto fail; 29302 emit_op(s, OP_drop); 29303 if (label_test != label_body) 29304 emit_goto(s, OP_goto, label_test); 29305 } 29306 if (js_parse_expect(s, ')')) 29307 goto fail; 29308 29309 pos_body = s->cur_func->byte_code.size; 29310 emit_label(s, label_body); 29311 if (js_parse_statement(s)) 29312 goto fail; 29313 29314 /* close the closures before the next iteration */ 29315 /* XXX: check continue case */ 29316 close_scopes(s, s->cur_func->scope_level, block_scope_level); 29317 29318 if (OPTIMIZE && label_test != label_body && label_cont != label_test) { 29319 /* move the increment code here */ 29320 DynBuf *bc = &s->cur_func->byte_code; 29321 int chunk_size = pos_body - pos_cont; 29322 int offset = bc->size - pos_cont; 29323 int i; 29324 if (dbuf_claim(bc, chunk_size)) 29325 goto fail; 29326 dbuf_put(bc, bc->buf + pos_cont, chunk_size); 29327 memset(bc->buf + pos_cont, OP_nop, chunk_size); 29328 /* increment part ends with a goto */ 29329 s->cur_func->last_opcode_pos = bc->size - 5; 29330 /* relocate labels */ 29331 for (i = label_cont; i < s->cur_func->label_count; i++) { 29332 LabelSlot *ls = &s->cur_func->label_slots[i]; 29333 if (ls->pos >= pos_cont && ls->pos < pos_body) 29334 ls->pos += offset; 29335 } 29336 } else { 29337 emit_goto(s, OP_goto, label_cont); 29338 } 29339 29340 emit_label(s, label_break); 29341 29342 pop_break_entry(s->cur_func); 29343 pop_scope(s); 29344 } 29345 break; 29346 case TOK_BREAK: 29347 case TOK_CONTINUE: 29348 { 29349 int is_cont = s->token.val - TOK_BREAK; 29350 int label; 29351 29352 if (next_token(s)) 29353 goto fail; 29354 if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) 29355 label = s->token.u.ident.atom; 29356 else 29357 label = JS_ATOM_NULL; 29358 if (emit_break(s, label, is_cont)) 29359 goto fail; 29360 if (label != JS_ATOM_NULL) { 29361 if (next_token(s)) 29362 goto fail; 29363 } 29364 if (js_parse_expect_semi(s)) 29365 goto fail; 29366 } 29367 break; 29368 case TOK_SWITCH: 29369 { 29370 int label_case, label_break, label1; 29371 int default_label_pos; 29372 BlockEnv break_entry; 29373 29374 if (next_token(s)) 29375 goto fail; 29376 29377 set_eval_ret_undefined(s); 29378 if (js_parse_expr_paren(s)) 29379 goto fail; 29380 29381 push_scope(s); 29382 label_break = new_label(s); 29383 push_break_entry(s->cur_func, &break_entry, 29384 label_name, label_break, -1, 1); 29385 29386 if (js_parse_expect(s, '{')) 29387 goto fail; 29388 29389 default_label_pos = -1; 29390 label_case = -1; 29391 while (s->token.val != '}') { 29392 if (s->token.val == TOK_CASE) { 29393 label1 = -1; 29394 if (label_case >= 0) { 29395 /* skip the case if needed */ 29396 label1 = emit_goto(s, OP_goto, -1); 29397 } 29398 emit_label(s, label_case); 29399 label_case = -1; 29400 for (;;) { 29401 /* parse a sequence of case clauses */ 29402 if (next_token(s)) 29403 goto fail; 29404 emit_op(s, OP_dup); 29405 if (js_parse_expr(s)) 29406 goto fail; 29407 if (js_parse_expect(s, ':')) 29408 goto fail; 29409 emit_op(s, OP_strict_eq); 29410 if (s->token.val == TOK_CASE) { 29411 label1 = emit_goto(s, OP_if_true, label1); 29412 } else { 29413 label_case = emit_goto(s, OP_if_false, -1); 29414 emit_label(s, label1); 29415 break; 29416 } 29417 } 29418 } else if (s->token.val == TOK_DEFAULT) { 29419 if (next_token(s)) 29420 goto fail; 29421 if (js_parse_expect(s, ':')) 29422 goto fail; 29423 if (default_label_pos >= 0) { 29424 js_parse_error(s, "duplicate default"); 29425 goto fail; 29426 } 29427 if (label_case < 0) { 29428 /* falling thru direct from switch expression */ 29429 label_case = emit_goto(s, OP_goto, -1); 29430 } 29431 /* Emit a dummy label opcode. Label will be patched after 29432 the end of the switch body. Do not use emit_label(s, 0) 29433 because it would clobber label 0 address, preventing 29434 proper optimizer operation. 29435 */ 29436 emit_op(s, OP_label); 29437 emit_u32(s, 0); 29438 default_label_pos = s->cur_func->byte_code.size - 4; 29439 } else { 29440 if (label_case < 0) { 29441 /* falling thru direct from switch expression */ 29442 js_parse_error(s, "invalid switch statement"); 29443 goto fail; 29444 } 29445 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 29446 goto fail; 29447 } 29448 } 29449 if (js_parse_expect(s, '}')) 29450 goto fail; 29451 if (default_label_pos >= 0) { 29452 /* Ugly patch for the `default` label, shameful and risky */ 29453 put_u32(s->cur_func->byte_code.buf + default_label_pos, 29454 label_case); 29455 s->cur_func->label_slots[label_case].pos = default_label_pos + 4; 29456 } else { 29457 emit_label(s, label_case); 29458 } 29459 emit_label(s, label_break); 29460 emit_op(s, OP_drop); /* drop the switch expression */ 29461 29462 pop_break_entry(s->cur_func); 29463 pop_scope(s); 29464 } 29465 break; 29466 case TOK_TRY: 29467 { 29468 int label_catch, label_catch2, label_finally, label_end; 29469 JSAtom name; 29470 BlockEnv block_env; 29471 29472 set_eval_ret_undefined(s); 29473 if (next_token(s)) 29474 goto fail; 29475 label_catch = new_label(s); 29476 label_catch2 = new_label(s); 29477 label_finally = new_label(s); 29478 label_end = new_label(s); 29479 29480 emit_goto(s, OP_catch, label_catch); 29481 29482 push_break_entry(s->cur_func, &block_env, 29483 JS_ATOM_NULL, -1, -1, 1); 29484 block_env.label_finally = label_finally; 29485 29486 if (js_parse_block(s)) 29487 goto fail; 29488 29489 pop_break_entry(s->cur_func); 29490 29491 if (js_is_live_code(s)) { 29492 /* drop the catch offset */ 29493 emit_op(s, OP_drop); 29494 /* must push dummy value to keep same stack size */ 29495 emit_op(s, OP_undefined); 29496 emit_goto(s, OP_gosub, label_finally); 29497 emit_op(s, OP_drop); 29498 29499 emit_goto(s, OP_goto, label_end); 29500 } 29501 29502 if (s->token.val == TOK_CATCH) { 29503 if (next_token(s)) 29504 goto fail; 29505 29506 push_scope(s); /* catch variable */ 29507 emit_label(s, label_catch); 29508 29509 if (s->token.val == '{') { 29510 /* support optional-catch-binding feature */ 29511 emit_op(s, OP_drop); /* pop the exception object */ 29512 } else { 29513 if (js_parse_expect(s, '(')) 29514 goto fail; 29515 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { 29516 if (s->token.val == '[' || s->token.val == '{') { 29517 /* XXX: TOK_LET is not completely correct */ 29518 if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE, FALSE) < 0) 29519 goto fail; 29520 } else { 29521 js_parse_error(s, "identifier expected"); 29522 goto fail; 29523 } 29524 } else { 29525 name = JS_DupAtom(ctx, s->token.u.ident.atom); 29526 if (next_token(s) 29527 || js_define_var(s, name, TOK_CATCH) < 0) { 29528 JS_FreeAtom(ctx, name); 29529 goto fail; 29530 } 29531 /* store the exception value in the catch variable */ 29532 emit_op(s, OP_scope_put_var); 29533 emit_u32(s, name); 29534 emit_u16(s, s->cur_func->scope_level); 29535 } 29536 if (js_parse_expect(s, ')')) 29537 goto fail; 29538 } 29539 /* XXX: should keep the address to nop it out if there is no finally block */ 29540 emit_goto(s, OP_catch, label_catch2); 29541 29542 push_scope(s); /* catch block */ 29543 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, 29544 -1, -1, 1); 29545 block_env.label_finally = label_finally; 29546 29547 if (js_parse_block(s)) 29548 goto fail; 29549 29550 pop_break_entry(s->cur_func); 29551 pop_scope(s); /* catch block */ 29552 pop_scope(s); /* catch variable */ 29553 29554 if (js_is_live_code(s)) { 29555 /* drop the catch2 offset */ 29556 emit_op(s, OP_drop); 29557 /* XXX: should keep the address to nop it out if there is no finally block */ 29558 /* must push dummy value to keep same stack size */ 29559 emit_op(s, OP_undefined); 29560 emit_goto(s, OP_gosub, label_finally); 29561 emit_op(s, OP_drop); 29562 emit_goto(s, OP_goto, label_end); 29563 } 29564 /* catch exceptions thrown in the catch block to execute the 29565 * finally clause and rethrow the exception */ 29566 emit_label(s, label_catch2); 29567 /* catch value is at TOS, no need to push undefined */ 29568 emit_goto(s, OP_gosub, label_finally); 29569 emit_op(s, OP_throw); 29570 29571 } else if (s->token.val == TOK_FINALLY) { 29572 /* finally without catch : execute the finally clause 29573 * and rethrow the exception */ 29574 emit_label(s, label_catch); 29575 /* catch value is at TOS, no need to push undefined */ 29576 emit_goto(s, OP_gosub, label_finally); 29577 emit_op(s, OP_throw); 29578 } else { 29579 js_parse_error(s, "expecting catch or finally"); 29580 goto fail; 29581 } 29582 emit_label(s, label_finally); 29583 if (s->token.val == TOK_FINALLY) { 29584 int saved_eval_ret_idx = 0; /* avoid warning */ 29585 29586 if (next_token(s)) 29587 goto fail; 29588 /* on the stack: ret_value gosub_ret_value */ 29589 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, 29590 -1, -1, 2); 29591 29592 if (s->cur_func->eval_ret_idx >= 0) { 29593 /* 'finally' updates eval_ret only if not a normal 29594 termination */ 29595 saved_eval_ret_idx = 29596 add_var(s->ctx, s->cur_func, JS_ATOM__ret_); 29597 if (saved_eval_ret_idx < 0) 29598 goto fail; 29599 emit_op(s, OP_get_loc); 29600 emit_u16(s, s->cur_func->eval_ret_idx); 29601 emit_op(s, OP_put_loc); 29602 emit_u16(s, saved_eval_ret_idx); 29603 set_eval_ret_undefined(s); 29604 } 29605 29606 if (js_parse_block(s)) 29607 goto fail; 29608 29609 if (s->cur_func->eval_ret_idx >= 0) { 29610 emit_op(s, OP_get_loc); 29611 emit_u16(s, saved_eval_ret_idx); 29612 emit_op(s, OP_put_loc); 29613 emit_u16(s, s->cur_func->eval_ret_idx); 29614 } 29615 pop_break_entry(s->cur_func); 29616 } 29617 emit_op(s, OP_ret); 29618 emit_label(s, label_end); 29619 } 29620 break; 29621 case ';': 29622 /* empty statement */ 29623 if (next_token(s)) 29624 goto fail; 29625 break; 29626 case TOK_WITH: 29627 if (s->cur_func->js_mode & JS_MODE_STRICT) { 29628 js_parse_error(s, "invalid keyword: with"); 29629 goto fail; 29630 } else { 29631 int with_idx; 29632 29633 if (next_token(s)) 29634 goto fail; 29635 29636 if (js_parse_expr_paren(s)) 29637 goto fail; 29638 29639 push_scope(s); 29640 with_idx = define_var(s, s->cur_func, JS_ATOM__with_, 29641 JS_VAR_DEF_WITH); 29642 if (with_idx < 0) 29643 goto fail; 29644 emit_op(s, OP_to_object); 29645 emit_op(s, OP_put_loc); 29646 emit_u16(s, with_idx); 29647 29648 set_eval_ret_undefined(s); 29649 if (js_parse_statement(s)) 29650 goto fail; 29651 29652 /* Popping scope drops lexical context for the with object variable */ 29653 pop_scope(s); 29654 } 29655 break; 29656 case TOK_FUNCTION: 29657 /* ES6 Annex B.3.2 and B.3.3 semantics */ 29658 if (!(decl_mask & DECL_MASK_FUNC)) 29659 goto func_decl_error; 29660 if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*') 29661 goto func_decl_error; 29662 goto parse_func_var; 29663 case TOK_IDENT: 29664 if (s->token.u.ident.is_reserved) { 29665 js_parse_error_reserved_identifier(s); 29666 goto fail; 29667 } 29668 /* Determine if `let` introduces a Declaration or an ExpressionStatement */ 29669 switch (is_let(s, decl_mask)) { 29670 case TRUE: 29671 tok = TOK_LET; 29672 goto haslet; 29673 case FALSE: 29674 break; 29675 default: 29676 goto fail; 29677 } 29678 if (token_is_pseudo_keyword(s, JS_ATOM_async) && 29679 peek_token(s, TRUE) == TOK_FUNCTION) { 29680 if (!(decl_mask & DECL_MASK_OTHER)) { 29681 func_decl_error: 29682 js_parse_error(s, "function declarations can't appear in single-statement context"); 29683 goto fail; 29684 } 29685 parse_func_var: 29686 if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, 29687 JS_FUNC_NORMAL, JS_ATOM_NULL, 29688 s->token.ptr)) 29689 goto fail; 29690 break; 29691 } 29692 goto hasexpr; 29693 29694 case TOK_CLASS: 29695 if (!(decl_mask & DECL_MASK_OTHER)) { 29696 js_parse_error(s, "class declarations can't appear in single-statement context"); 29697 goto fail; 29698 } 29699 if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE)) 29700 return -1; 29701 break; 29702 29703 case TOK_DEBUGGER: 29704 /* currently no debugger, so just skip the keyword */ 29705 if (next_token(s)) 29706 goto fail; 29707 if (js_parse_expect_semi(s)) 29708 goto fail; 29709 break; 29710 29711 case TOK_ENUM: 29712 case TOK_EXPORT: 29713 case TOK_EXTENDS: 29714 js_unsupported_keyword(s, s->token.u.ident.atom); 29715 goto fail; 29716 29717 default: 29718 hasexpr: 29719 emit_source_pos(s, s->token.ptr); 29720 if (js_parse_expr(s)) 29721 goto fail; 29722 if (s->cur_func->eval_ret_idx >= 0) { 29723 /* store the expression value so that it can be returned 29724 by eval() */ 29725 emit_op(s, OP_put_loc); 29726 emit_u16(s, s->cur_func->eval_ret_idx); 29727 } else { 29728 emit_op(s, OP_drop); /* drop the result */ 29729 } 29730 if (js_parse_expect_semi(s)) 29731 goto fail; 29732 break; 29733 } 29734 done: 29735 JS_FreeAtom(ctx, label_name); 29736 return 0; 29737 fail: 29738 JS_FreeAtom(ctx, label_name); 29739 return -1; 29740 } 29741 29742 /* 'name' is freed. The module is referenced by 'ctx->loaded_modules' */ 29743 static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) 29744 { 29745 JSModuleDef *m; 29746 m = js_mallocz(ctx, sizeof(*m)); 29747 if (!m) { 29748 JS_FreeAtom(ctx, name); 29749 return NULL; 29750 } 29751 js_rc(m)->ref_count = 1; 29752 add_gc_object(ctx->rt, &m->header, JS_GC_OBJ_TYPE_MODULE); 29753 m->module_name = name; 29754 m->module_ns = JS_UNDEFINED; 29755 m->func_obj = JS_UNDEFINED; 29756 m->eval_exception = JS_UNDEFINED; 29757 m->meta_obj = JS_UNDEFINED; 29758 m->promise = JS_UNDEFINED; 29759 m->resolving_funcs[0] = JS_UNDEFINED; 29760 m->resolving_funcs[1] = JS_UNDEFINED; 29761 m->private_value = JS_UNDEFINED; 29762 list_add_tail(&m->link, &ctx->loaded_modules); 29763 return m; 29764 } 29765 29766 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, 29767 JS_MarkFunc *mark_func) 29768 { 29769 int i; 29770 29771 for(i = 0; i < m->req_module_entries_count; i++) { 29772 JSReqModuleEntry *rme = &m->req_module_entries[i]; 29773 JS_MarkValue(rt, rme->attributes, mark_func); 29774 } 29775 29776 for(i = 0; i < m->export_entries_count; i++) { 29777 JSExportEntry *me = &m->export_entries[i]; 29778 if (me->export_type == JS_EXPORT_TYPE_LOCAL && 29779 me->u.local.var_ref) { 29780 mark_func(rt, &me->u.local.var_ref->header); 29781 } 29782 } 29783 29784 JS_MarkValue(rt, m->module_ns, mark_func); 29785 JS_MarkValue(rt, m->func_obj, mark_func); 29786 JS_MarkValue(rt, m->eval_exception, mark_func); 29787 JS_MarkValue(rt, m->meta_obj, mark_func); 29788 JS_MarkValue(rt, m->promise, mark_func); 29789 JS_MarkValue(rt, m->resolving_funcs[0], mark_func); 29790 JS_MarkValue(rt, m->resolving_funcs[1], mark_func); 29791 JS_MarkValue(rt, m->private_value, mark_func); 29792 } 29793 29794 static void js_free_module_def(JSRuntime *rt, JSModuleDef *m) 29795 { 29796 int i; 29797 29798 JS_FreeAtomRT(rt, m->module_name); 29799 29800 for(i = 0; i < m->req_module_entries_count; i++) { 29801 JSReqModuleEntry *rme = &m->req_module_entries[i]; 29802 JS_FreeAtomRT(rt, rme->module_name); 29803 JS_FreeValueRT(rt, rme->attributes); 29804 } 29805 js_free_rt(rt, m->req_module_entries); 29806 29807 for(i = 0; i < m->export_entries_count; i++) { 29808 JSExportEntry *me = &m->export_entries[i]; 29809 if (me->export_type == JS_EXPORT_TYPE_LOCAL) 29810 free_var_ref(rt, me->u.local.var_ref); 29811 JS_FreeAtomRT(rt, me->export_name); 29812 JS_FreeAtomRT(rt, me->local_name); 29813 } 29814 js_free_rt(rt, m->export_entries); 29815 29816 js_free_rt(rt, m->star_export_entries); 29817 29818 for(i = 0; i < m->import_entries_count; i++) { 29819 JSImportEntry *mi = &m->import_entries[i]; 29820 JS_FreeAtomRT(rt, mi->import_name); 29821 } 29822 js_free_rt(rt, m->import_entries); 29823 js_free_rt(rt, m->async_parent_modules); 29824 29825 JS_FreeValueRT(rt, m->module_ns); 29826 JS_FreeValueRT(rt, m->func_obj); 29827 JS_FreeValueRT(rt, m->eval_exception); 29828 JS_FreeValueRT(rt, m->meta_obj); 29829 JS_FreeValueRT(rt, m->promise); 29830 JS_FreeValueRT(rt, m->resolving_funcs[0]); 29831 JS_FreeValueRT(rt, m->resolving_funcs[1]); 29832 JS_FreeValueRT(rt, m->private_value); 29833 /* during the GC the finalizers are called in an arbitrary 29834 order so the module may no longer be referenced by the JSContext list */ 29835 if (m->link.next) { 29836 list_del(&m->link); 29837 } 29838 remove_gc_object(&m->header); 29839 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(m)->ref_count != 0) { 29840 list_add_tail(&m->header.link, &rt->gc_zero_ref_count_list); 29841 } else { 29842 js_free_rt(rt, m); 29843 } 29844 } 29845 29846 static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, 29847 JSAtom module_name) 29848 { 29849 JSReqModuleEntry *rme; 29850 29851 if (js_resize_array(ctx, (void **)&m->req_module_entries, 29852 sizeof(JSReqModuleEntry), 29853 &m->req_module_entries_size, 29854 m->req_module_entries_count + 1)) 29855 return -1; 29856 rme = &m->req_module_entries[m->req_module_entries_count++]; 29857 rme->module_name = JS_DupAtom(ctx, module_name); 29858 rme->module = NULL; 29859 rme->attributes = JS_UNDEFINED; 29860 return m->req_module_entries_count - 1; 29861 } 29862 29863 static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, 29864 JSAtom export_name) 29865 { 29866 JSExportEntry *me; 29867 int i; 29868 for(i = 0; i < m->export_entries_count; i++) { 29869 me = &m->export_entries[i]; 29870 if (me->export_name == export_name) 29871 return me; 29872 } 29873 return NULL; 29874 } 29875 29876 static JSExportEntry *add_export_entry2(JSContext *ctx, 29877 JSParseState *s, JSModuleDef *m, 29878 JSAtom local_name, JSAtom export_name, 29879 JSExportTypeEnum export_type) 29880 { 29881 JSExportEntry *me; 29882 29883 if (find_export_entry(ctx, m, export_name)) { 29884 char buf1[ATOM_GET_STR_BUF_SIZE]; 29885 if (s) { 29886 js_parse_error(s, "duplicate exported name '%s'", 29887 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); 29888 } else { 29889 JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); 29890 } 29891 return NULL; 29892 } 29893 29894 if (js_resize_array(ctx, (void **)&m->export_entries, 29895 sizeof(JSExportEntry), 29896 &m->export_entries_size, 29897 m->export_entries_count + 1)) 29898 return NULL; 29899 me = &m->export_entries[m->export_entries_count++]; 29900 memset(me, 0, sizeof(*me)); 29901 me->local_name = JS_DupAtom(ctx, local_name); 29902 me->export_name = JS_DupAtom(ctx, export_name); 29903 me->export_type = export_type; 29904 return me; 29905 } 29906 29907 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, 29908 JSAtom local_name, JSAtom export_name, 29909 JSExportTypeEnum export_type) 29910 { 29911 return add_export_entry2(s->ctx, s, m, local_name, export_name, 29912 export_type); 29913 } 29914 29915 static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, 29916 int req_module_idx) 29917 { 29918 JSStarExportEntry *se; 29919 29920 if (js_resize_array(ctx, (void **)&m->star_export_entries, 29921 sizeof(JSStarExportEntry), 29922 &m->star_export_entries_size, 29923 m->star_export_entries_count + 1)) 29924 return -1; 29925 se = &m->star_export_entries[m->star_export_entries_count++]; 29926 se->req_module_idx = req_module_idx; 29927 return 0; 29928 } 29929 29930 /* create a C module */ 29931 JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, 29932 JSModuleInitFunc *func) 29933 { 29934 JSModuleDef *m; 29935 JSAtom name; 29936 name = JS_NewAtom(ctx, name_str); 29937 if (name == JS_ATOM_NULL) 29938 return NULL; 29939 m = js_new_module_def(ctx, name); 29940 if (!m) 29941 return NULL; 29942 m->init_func = func; 29943 return m; 29944 } 29945 29946 int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) 29947 { 29948 JSExportEntry *me; 29949 JSAtom name; 29950 name = JS_NewAtom(ctx, export_name); 29951 if (name == JS_ATOM_NULL) 29952 return -1; 29953 me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, 29954 JS_EXPORT_TYPE_LOCAL); 29955 JS_FreeAtom(ctx, name); 29956 if (!me) 29957 return -1; 29958 else 29959 return 0; 29960 } 29961 29962 int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, 29963 JSValue val) 29964 { 29965 JSExportEntry *me; 29966 JSAtom name; 29967 name = JS_NewAtom(ctx, export_name); 29968 if (name == JS_ATOM_NULL) 29969 goto fail; 29970 me = find_export_entry(ctx, m, name); 29971 JS_FreeAtom(ctx, name); 29972 if (!me) 29973 goto fail; 29974 set_value(ctx, me->u.local.var_ref->pvalue, val); 29975 return 0; 29976 fail: 29977 JS_FreeValue(ctx, val); 29978 return -1; 29979 } 29980 29981 int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val) 29982 { 29983 set_value(ctx, &m->private_value, val); 29984 return 0; 29985 } 29986 29987 JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m) 29988 { 29989 return JS_DupValue(ctx, m->private_value); 29990 } 29991 29992 void JS_SetModuleLoaderFunc(JSRuntime *rt, 29993 JSModuleNormalizeFunc *module_normalize, 29994 JSModuleLoaderFunc *module_loader, void *opaque) 29995 { 29996 rt->module_normalize_func = module_normalize; 29997 rt->module_loader_has_attr = FALSE; 29998 rt->u.module_loader_func = module_loader; 29999 rt->module_check_attrs = NULL; 30000 rt->module_loader_opaque = opaque; 30001 } 30002 30003 void JS_SetModuleLoaderFunc2(JSRuntime *rt, 30004 JSModuleNormalizeFunc *module_normalize, 30005 JSModuleLoaderFunc2 *module_loader, 30006 JSModuleCheckSupportedImportAttributes *module_check_attrs, 30007 void *opaque) 30008 { 30009 rt->module_normalize_func = module_normalize; 30010 rt->module_loader_has_attr = TRUE; 30011 rt->u.module_loader_func2 = module_loader; 30012 rt->module_check_attrs = module_check_attrs; 30013 rt->module_loader_opaque = opaque; 30014 } 30015 30016 /* default module filename normalizer */ 30017 static char *js_default_module_normalize_name(JSContext *ctx, 30018 const char *base_name, 30019 const char *name) 30020 { 30021 char *filename, *p; 30022 const char *r; 30023 int cap; 30024 int len; 30025 30026 if (name[0] != '.') { 30027 /* if no initial dot, the module name is not modified */ 30028 return js_strdup(ctx, name); 30029 } 30030 30031 p = strrchr(base_name, '/'); 30032 if (p) 30033 len = p - base_name; 30034 else 30035 len = 0; 30036 30037 cap = len + strlen(name) + 1 + 1; 30038 filename = js_malloc(ctx, cap); 30039 if (!filename) 30040 return NULL; 30041 memcpy(filename, base_name, len); 30042 filename[len] = '\0'; 30043 30044 /* we only normalize the leading '..' or '.' */ 30045 r = name; 30046 for(;;) { 30047 if (r[0] == '.' && r[1] == '/') { 30048 r += 2; 30049 } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { 30050 /* remove the last path element of filename, except if "." 30051 or ".." */ 30052 if (filename[0] == '\0') 30053 break; 30054 p = strrchr(filename, '/'); 30055 if (!p) 30056 p = filename; 30057 else 30058 p++; 30059 if (!strcmp(p, ".") || !strcmp(p, "..")) 30060 break; 30061 if (p > filename) 30062 p--; 30063 *p = '\0'; 30064 r += 3; 30065 } else { 30066 break; 30067 } 30068 } 30069 if (filename[0] != '\0') 30070 pstrcat(filename, cap, "/"); 30071 pstrcat(filename, cap, r); 30072 // printf("normalize: %s %s -> %s\n", base_name, name, filename); 30073 return filename; 30074 } 30075 30076 static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) 30077 { 30078 struct list_head *el; 30079 JSModuleDef *m; 30080 30081 /* first look at the loaded modules */ 30082 list_for_each(el, &ctx->loaded_modules) { 30083 m = list_entry(el, JSModuleDef, link); 30084 if (m->module_name == name) 30085 return m; 30086 } 30087 return NULL; 30088 } 30089 30090 /* return NULL in case of exception (e.g. module could not be loaded) */ 30091 static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, 30092 const char *base_cname, 30093 const char *cname1, 30094 JSValueConst attributes) 30095 { 30096 JSRuntime *rt = ctx->rt; 30097 JSModuleDef *m; 30098 char *cname; 30099 JSAtom module_name; 30100 30101 if (!rt->module_normalize_func) { 30102 cname = js_default_module_normalize_name(ctx, base_cname, cname1); 30103 } else { 30104 cname = rt->module_normalize_func(ctx, base_cname, cname1, 30105 rt->module_loader_opaque); 30106 } 30107 if (!cname) 30108 return NULL; 30109 30110 module_name = JS_NewAtom(ctx, cname); 30111 if (module_name == JS_ATOM_NULL) { 30112 js_free(ctx, cname); 30113 return NULL; 30114 } 30115 30116 /* first look at the loaded modules */ 30117 m = js_find_loaded_module(ctx, module_name); 30118 if (m) { 30119 js_free(ctx, cname); 30120 JS_FreeAtom(ctx, module_name); 30121 return m; 30122 } 30123 30124 JS_FreeAtom(ctx, module_name); 30125 30126 /* load the module */ 30127 if (!rt->u.module_loader_func) { 30128 /* XXX: use a syntax error ? */ 30129 JS_ThrowReferenceError(ctx, "could not load module '%s'", 30130 cname); 30131 js_free(ctx, cname); 30132 return NULL; 30133 } 30134 if (rt->module_loader_has_attr) { 30135 m = rt->u.module_loader_func2(ctx, cname, rt->module_loader_opaque, attributes); 30136 } else { 30137 m = rt->u.module_loader_func(ctx, cname, rt->module_loader_opaque); 30138 } 30139 js_free(ctx, cname); 30140 return m; 30141 } 30142 30143 static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, 30144 JSAtom base_module_name, 30145 JSAtom module_name1, 30146 JSValueConst attributes) 30147 { 30148 const char *base_cname, *cname; 30149 JSModuleDef *m; 30150 30151 base_cname = JS_AtomToCString(ctx, base_module_name); 30152 if (!base_cname) 30153 return NULL; 30154 cname = JS_AtomToCString(ctx, module_name1); 30155 if (!cname) { 30156 JS_FreeCString(ctx, base_cname); 30157 return NULL; 30158 } 30159 m = js_host_resolve_imported_module(ctx, base_cname, cname, attributes); 30160 JS_FreeCString(ctx, base_cname); 30161 JS_FreeCString(ctx, cname); 30162 return m; 30163 } 30164 30165 typedef struct JSResolveEntry { 30166 JSModuleDef *module; 30167 JSAtom name; 30168 } JSResolveEntry; 30169 30170 typedef struct JSResolveState { 30171 JSResolveEntry *array; 30172 int size; 30173 int count; 30174 } JSResolveState; 30175 30176 static int find_resolve_entry(JSResolveState *s, 30177 JSModuleDef *m, JSAtom name) 30178 { 30179 int i; 30180 for(i = 0; i < s->count; i++) { 30181 JSResolveEntry *re = &s->array[i]; 30182 if (re->module == m && re->name == name) 30183 return i; 30184 } 30185 return -1; 30186 } 30187 30188 static int add_resolve_entry(JSContext *ctx, JSResolveState *s, 30189 JSModuleDef *m, JSAtom name) 30190 { 30191 JSResolveEntry *re; 30192 30193 if (js_resize_array(ctx, (void **)&s->array, 30194 sizeof(JSResolveEntry), 30195 &s->size, s->count + 1)) 30196 return -1; 30197 re = &s->array[s->count++]; 30198 re->module = m; 30199 re->name = JS_DupAtom(ctx, name); 30200 return 0; 30201 } 30202 30203 typedef enum JSResolveResultEnum { 30204 JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ 30205 JS_RESOLVE_RES_FOUND = 0, 30206 JS_RESOLVE_RES_NOT_FOUND, 30207 JS_RESOLVE_RES_CIRCULAR, 30208 JS_RESOLVE_RES_AMBIGUOUS, 30209 } JSResolveResultEnum; 30210 30211 static JSResolveResultEnum js_resolve_export1(JSContext *ctx, 30212 JSModuleDef **pmodule, 30213 JSExportEntry **pme, 30214 JSModuleDef *m, 30215 JSAtom export_name, 30216 JSResolveState *s) 30217 { 30218 JSExportEntry *me; 30219 30220 *pmodule = NULL; 30221 *pme = NULL; 30222 if (find_resolve_entry(s, m, export_name) >= 0) 30223 return JS_RESOLVE_RES_CIRCULAR; 30224 if (add_resolve_entry(ctx, s, m, export_name) < 0) 30225 return JS_RESOLVE_RES_EXCEPTION; 30226 me = find_export_entry(ctx, m, export_name); 30227 if (me) { 30228 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 30229 /* local export */ 30230 *pmodule = m; 30231 *pme = me; 30232 return JS_RESOLVE_RES_FOUND; 30233 } else { 30234 /* indirect export */ 30235 JSModuleDef *m1; 30236 m1 = m->req_module_entries[me->u.req_module_idx].module; 30237 if (me->local_name == JS_ATOM__star_) { 30238 /* export ns from */ 30239 *pmodule = m; 30240 *pme = me; 30241 return JS_RESOLVE_RES_FOUND; 30242 } else { 30243 return js_resolve_export1(ctx, pmodule, pme, m1, 30244 me->local_name, s); 30245 } 30246 } 30247 } else { 30248 if (export_name != JS_ATOM_default) { 30249 /* not found in direct or indirect exports: try star exports */ 30250 int i; 30251 30252 for(i = 0; i < m->star_export_entries_count; i++) { 30253 JSStarExportEntry *se = &m->star_export_entries[i]; 30254 JSModuleDef *m1, *res_m; 30255 JSExportEntry *res_me; 30256 JSResolveResultEnum ret; 30257 30258 m1 = m->req_module_entries[se->req_module_idx].module; 30259 ret = js_resolve_export1(ctx, &res_m, &res_me, m1, 30260 export_name, s); 30261 if (ret == JS_RESOLVE_RES_AMBIGUOUS || 30262 ret == JS_RESOLVE_RES_EXCEPTION) { 30263 return ret; 30264 } else if (ret == JS_RESOLVE_RES_FOUND) { 30265 if (*pme != NULL) { 30266 if (*pmodule != res_m || 30267 res_me->local_name != (*pme)->local_name) { 30268 *pmodule = NULL; 30269 *pme = NULL; 30270 return JS_RESOLVE_RES_AMBIGUOUS; 30271 } 30272 } else { 30273 *pmodule = res_m; 30274 *pme = res_me; 30275 } 30276 } 30277 } 30278 if (*pme != NULL) 30279 return JS_RESOLVE_RES_FOUND; 30280 } 30281 return JS_RESOLVE_RES_NOT_FOUND; 30282 } 30283 } 30284 30285 /* If the return value is JS_RESOLVE_RES_FOUND, return the module 30286 (*pmodule) and the corresponding local export entry 30287 (*pme). Otherwise return (NULL, NULL) */ 30288 static JSResolveResultEnum js_resolve_export(JSContext *ctx, 30289 JSModuleDef **pmodule, 30290 JSExportEntry **pme, 30291 JSModuleDef *m, 30292 JSAtom export_name) 30293 { 30294 JSResolveState ss, *s = &ss; 30295 int i; 30296 JSResolveResultEnum ret; 30297 30298 s->array = NULL; 30299 s->size = 0; 30300 s->count = 0; 30301 30302 ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); 30303 30304 for(i = 0; i < s->count; i++) 30305 JS_FreeAtom(ctx, s->array[i].name); 30306 js_free(ctx, s->array); 30307 30308 return ret; 30309 } 30310 30311 static void js_resolve_export_throw_error(JSContext *ctx, 30312 JSResolveResultEnum res, 30313 JSModuleDef *m, JSAtom export_name) 30314 { 30315 char buf1[ATOM_GET_STR_BUF_SIZE]; 30316 char buf2[ATOM_GET_STR_BUF_SIZE]; 30317 switch(res) { 30318 case JS_RESOLVE_RES_EXCEPTION: 30319 break; 30320 default: 30321 case JS_RESOLVE_RES_NOT_FOUND: 30322 JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", 30323 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 30324 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 30325 break; 30326 case JS_RESOLVE_RES_CIRCULAR: 30327 JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", 30328 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 30329 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 30330 break; 30331 case JS_RESOLVE_RES_AMBIGUOUS: 30332 JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", 30333 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 30334 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 30335 break; 30336 } 30337 } 30338 30339 30340 typedef enum { 30341 EXPORTED_NAME_AMBIGUOUS, 30342 EXPORTED_NAME_NORMAL, 30343 EXPORTED_NAME_DELAYED, 30344 } ExportedNameEntryEnum; 30345 30346 typedef struct ExportedNameEntry { 30347 JSAtom export_name; 30348 ExportedNameEntryEnum export_type; 30349 union { 30350 JSExportEntry *me; /* using when the list is built */ 30351 JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ 30352 } u; 30353 } ExportedNameEntry; 30354 30355 typedef struct GetExportNamesState { 30356 JSModuleDef **modules; 30357 int modules_size; 30358 int modules_count; 30359 30360 ExportedNameEntry *exported_names; 30361 int exported_names_size; 30362 int exported_names_count; 30363 } GetExportNamesState; 30364 30365 static int find_exported_name(GetExportNamesState *s, JSAtom name) 30366 { 30367 int i; 30368 for(i = 0; i < s->exported_names_count; i++) { 30369 if (s->exported_names[i].export_name == name) 30370 return i; 30371 } 30372 return -1; 30373 } 30374 30375 static __exception int get_exported_names(JSContext *ctx, 30376 GetExportNamesState *s, 30377 JSModuleDef *m, BOOL from_star) 30378 { 30379 ExportedNameEntry *en; 30380 int i, j; 30381 30382 /* check circular reference */ 30383 for(i = 0; i < s->modules_count; i++) { 30384 if (s->modules[i] == m) 30385 return 0; 30386 } 30387 if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), 30388 &s->modules_size, s->modules_count + 1)) 30389 return -1; 30390 s->modules[s->modules_count++] = m; 30391 30392 for(i = 0; i < m->export_entries_count; i++) { 30393 JSExportEntry *me = &m->export_entries[i]; 30394 if (from_star && me->export_name == JS_ATOM_default) 30395 continue; 30396 j = find_exported_name(s, me->export_name); 30397 if (j < 0) { 30398 if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), 30399 &s->exported_names_size, 30400 s->exported_names_count + 1)) 30401 return -1; 30402 en = &s->exported_names[s->exported_names_count++]; 30403 en->export_name = me->export_name; 30404 /* avoid a second lookup for simple module exports */ 30405 if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) 30406 en->u.me = NULL; 30407 else 30408 en->u.me = me; 30409 } else { 30410 en = &s->exported_names[j]; 30411 en->u.me = NULL; 30412 } 30413 } 30414 for(i = 0; i < m->star_export_entries_count; i++) { 30415 JSStarExportEntry *se = &m->star_export_entries[i]; 30416 JSModuleDef *m1; 30417 m1 = m->req_module_entries[se->req_module_idx].module; 30418 if (get_exported_names(ctx, s, m1, TRUE)) 30419 return -1; 30420 } 30421 return 0; 30422 } 30423 30424 /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ 30425 static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) 30426 { 30427 return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); 30428 } 30429 30430 static const JSClassExoticMethods js_module_ns_exotic_methods = { 30431 .has_property = js_module_ns_has, 30432 }; 30433 30434 static int exported_names_cmp(const void *p1, const void *p2, void *opaque) 30435 { 30436 JSContext *ctx = opaque; 30437 const ExportedNameEntry *me1 = p1; 30438 const ExportedNameEntry *me2 = p2; 30439 JSValue str1, str2; 30440 int ret; 30441 30442 /* XXX: should avoid allocation memory in atom comparison */ 30443 str1 = JS_AtomToString(ctx, me1->export_name); 30444 str2 = JS_AtomToString(ctx, me2->export_name); 30445 if (JS_IsException(str1) || JS_IsException(str2)) { 30446 /* XXX: raise an error ? */ 30447 ret = 0; 30448 } else { 30449 ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), 30450 JS_VALUE_GET_STRING(str2)); 30451 } 30452 JS_FreeValue(ctx, str1); 30453 JS_FreeValue(ctx, str2); 30454 return ret; 30455 } 30456 30457 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, 30458 void *opaque) 30459 { 30460 JSModuleDef *m = opaque; 30461 JSResolveResultEnum res; 30462 JSExportEntry *res_me; 30463 JSModuleDef *res_m; 30464 JSVarRef *var_ref; 30465 30466 res = js_resolve_export(ctx, &res_m, &res_me, m, atom); 30467 if (res != JS_RESOLVE_RES_FOUND) { 30468 /* fail safe: normally no error should happen here except for memory */ 30469 js_resolve_export_throw_error(ctx, res, m, atom); 30470 return JS_EXCEPTION; 30471 } 30472 if (res_me->local_name == JS_ATOM__star_) { 30473 return JS_GetModuleNamespace(ctx, res_m->req_module_entries[res_me->u.req_module_idx].module); 30474 } else { 30475 if (res_me->u.local.var_ref) { 30476 var_ref = res_me->u.local.var_ref; 30477 } else { 30478 JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 30479 var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; 30480 } 30481 /* WARNING: a varref is returned as a string ! */ 30482 return JS_MKPTR(JS_TAG_STRING, var_ref); 30483 } 30484 } 30485 30486 static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) 30487 { 30488 JSValue obj; 30489 JSObject *p; 30490 GetExportNamesState s_s, *s = &s_s; 30491 int i, ret; 30492 JSProperty *pr; 30493 30494 obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); 30495 if (JS_IsException(obj)) 30496 return obj; 30497 p = JS_VALUE_GET_OBJ(obj); 30498 30499 memset(s, 0, sizeof(*s)); 30500 ret = get_exported_names(ctx, s, m, FALSE); 30501 js_free(ctx, s->modules); 30502 if (ret) 30503 goto fail; 30504 30505 /* Resolve the exported names. The ambiguous exports are removed */ 30506 for(i = 0; i < s->exported_names_count; i++) { 30507 ExportedNameEntry *en = &s->exported_names[i]; 30508 JSResolveResultEnum res; 30509 JSExportEntry *res_me; 30510 JSModuleDef *res_m; 30511 30512 if (en->u.me) { 30513 res_me = en->u.me; /* fast case: no resolution needed */ 30514 res_m = m; 30515 res = JS_RESOLVE_RES_FOUND; 30516 } else { 30517 res = js_resolve_export(ctx, &res_m, &res_me, m, 30518 en->export_name); 30519 } 30520 if (res != JS_RESOLVE_RES_FOUND) { 30521 if (res != JS_RESOLVE_RES_AMBIGUOUS) { 30522 js_resolve_export_throw_error(ctx, res, m, en->export_name); 30523 goto fail; 30524 } 30525 en->export_type = EXPORTED_NAME_AMBIGUOUS; 30526 } else { 30527 if (res_me->local_name == JS_ATOM__star_) { 30528 en->export_type = EXPORTED_NAME_DELAYED; 30529 } else { 30530 if (res_me->u.local.var_ref) { 30531 en->u.var_ref = res_me->u.local.var_ref; 30532 } else { 30533 JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 30534 en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; 30535 } 30536 if (en->u.var_ref == NULL) 30537 en->export_type = EXPORTED_NAME_DELAYED; 30538 else 30539 en->export_type = EXPORTED_NAME_NORMAL; 30540 } 30541 } 30542 } 30543 30544 /* sort the exported names */ 30545 rqsort(s->exported_names, s->exported_names_count, 30546 sizeof(s->exported_names[0]), exported_names_cmp, ctx); 30547 30548 for(i = 0; i < s->exported_names_count; i++) { 30549 ExportedNameEntry *en = &s->exported_names[i]; 30550 switch(en->export_type) { 30551 case EXPORTED_NAME_NORMAL: 30552 { 30553 JSVarRef *var_ref = en->u.var_ref; 30554 pr = add_property(ctx, p, en->export_name, 30555 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | 30556 JS_PROP_VARREF); 30557 if (!pr) 30558 goto fail; 30559 js_rc(var_ref)->ref_count++; 30560 pr->u.var_ref = var_ref; 30561 } 30562 break; 30563 case EXPORTED_NAME_DELAYED: 30564 /* the exported namespace or reference may depend on 30565 circular references, so we resolve it lazily */ 30566 if (JS_DefineAutoInitProperty(ctx, obj, 30567 en->export_name, 30568 JS_AUTOINIT_ID_MODULE_NS, 30569 m, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) 30570 goto fail; 30571 break; 30572 default: 30573 break; 30574 } 30575 } 30576 30577 js_free(ctx, s->exported_names); 30578 30579 JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, 30580 JS_AtomToString(ctx, JS_ATOM_Module), 30581 0); 30582 30583 p->extensible = FALSE; 30584 return obj; 30585 fail: 30586 js_free(ctx, s->exported_names); 30587 JS_FreeValue(ctx, obj); 30588 return JS_EXCEPTION; 30589 } 30590 30591 JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) 30592 { 30593 if (JS_IsUndefined(m->module_ns)) { 30594 JSValue val; 30595 val = js_build_module_ns(ctx, m); 30596 if (JS_IsException(val)) 30597 return JS_EXCEPTION; 30598 m->module_ns = val; 30599 } 30600 return JS_DupValue(ctx, m->module_ns); 30601 } 30602 30603 /* Load all the required modules for module 'm' */ 30604 static int js_resolve_module(JSContext *ctx, JSModuleDef *m) 30605 { 30606 int i; 30607 JSModuleDef *m1; 30608 30609 if (m->resolved) 30610 return 0; 30611 #ifdef DUMP_MODULE_RESOLVE 30612 { 30613 char buf1[ATOM_GET_STR_BUF_SIZE]; 30614 printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 30615 } 30616 #endif 30617 m->resolved = TRUE; 30618 /* resolve each requested module */ 30619 for(i = 0; i < m->req_module_entries_count; i++) { 30620 JSReqModuleEntry *rme = &m->req_module_entries[i]; 30621 m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, 30622 rme->module_name, 30623 rme->attributes); 30624 if (!m1) 30625 return -1; 30626 rme->module = m1; 30627 /* already done in js_host_resolve_imported_module() except if 30628 the module was loaded with JS_EvalBinary() */ 30629 if (js_resolve_module(ctx, m1) < 0) 30630 return -1; 30631 } 30632 return 0; 30633 } 30634 30635 /* Create the <eval> function associated with the module */ 30636 static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) 30637 { 30638 JSFunctionBytecode *b; 30639 JSValue func_obj, bfunc; 30640 30641 bfunc = m->func_obj; 30642 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 30643 JS_CLASS_BYTECODE_FUNCTION); 30644 30645 if (JS_IsException(func_obj)) 30646 return -1; 30647 m->func_obj = func_obj; 30648 b = JS_VALUE_GET_PTR(bfunc); 30649 func_obj = js_closure2(ctx, func_obj, b, NULL, NULL, TRUE, m); 30650 if (JS_IsException(func_obj)) { 30651 m->func_obj = JS_UNDEFINED; /* XXX: keep it ? */ 30652 JS_FreeValue(ctx, func_obj); 30653 return -1; 30654 } 30655 return 0; 30656 } 30657 30658 /* must be done before js_link_module() because of cyclic references */ 30659 static int js_create_module_function(JSContext *ctx, JSModuleDef *m) 30660 { 30661 BOOL is_c_module; 30662 int i; 30663 JSVarRef *var_ref; 30664 30665 if (m->func_created) 30666 return 0; 30667 30668 is_c_module = (m->init_func != NULL); 30669 30670 if (is_c_module) { 30671 /* initialize the exported variables */ 30672 for(i = 0; i < m->export_entries_count; i++) { 30673 JSExportEntry *me = &m->export_entries[i]; 30674 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 30675 var_ref = js_create_var_ref(ctx, FALSE); 30676 if (!var_ref) 30677 return -1; 30678 me->u.local.var_ref = var_ref; 30679 } 30680 } 30681 } else { 30682 if (js_create_module_bytecode_function(ctx, m)) 30683 return -1; 30684 } 30685 m->func_created = TRUE; 30686 30687 /* do it on the dependencies */ 30688 30689 for(i = 0; i < m->req_module_entries_count; i++) { 30690 JSReqModuleEntry *rme = &m->req_module_entries[i]; 30691 if (js_create_module_function(ctx, rme->module) < 0) 30692 return -1; 30693 } 30694 30695 return 0; 30696 } 30697 30698 30699 /* Prepare a module to be executed by resolving all the imported 30700 variables. */ 30701 static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, 30702 JSModuleDef **pstack_top, int index) 30703 { 30704 int i; 30705 JSImportEntry *mi; 30706 JSModuleDef *m1; 30707 JSVarRef **var_refs, *var_ref; 30708 JSObject *p; 30709 BOOL is_c_module; 30710 JSValue ret_val; 30711 30712 if (js_check_stack_overflow(ctx->rt, 0)) { 30713 JS_ThrowStackOverflow(ctx); 30714 return -1; 30715 } 30716 30717 #ifdef DUMP_MODULE_RESOLVE 30718 { 30719 char buf1[ATOM_GET_STR_BUF_SIZE]; 30720 printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 30721 } 30722 #endif 30723 30724 if (m->status == JS_MODULE_STATUS_LINKING || 30725 m->status == JS_MODULE_STATUS_LINKED || 30726 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 30727 m->status == JS_MODULE_STATUS_EVALUATED) 30728 return index; 30729 30730 assert(m->status == JS_MODULE_STATUS_UNLINKED); 30731 m->status = JS_MODULE_STATUS_LINKING; 30732 m->dfs_index = index; 30733 m->dfs_ancestor_index = index; 30734 index++; 30735 /* push 'm' on stack */ 30736 m->stack_prev = *pstack_top; 30737 *pstack_top = m; 30738 30739 for(i = 0; i < m->req_module_entries_count; i++) { 30740 JSReqModuleEntry *rme = &m->req_module_entries[i]; 30741 m1 = rme->module; 30742 index = js_inner_module_linking(ctx, m1, pstack_top, index); 30743 if (index < 0) 30744 goto fail; 30745 assert(m1->status == JS_MODULE_STATUS_LINKING || 30746 m1->status == JS_MODULE_STATUS_LINKED || 30747 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 30748 m1->status == JS_MODULE_STATUS_EVALUATED); 30749 if (m1->status == JS_MODULE_STATUS_LINKING) { 30750 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, 30751 m1->dfs_ancestor_index); 30752 } 30753 } 30754 30755 #ifdef DUMP_MODULE_RESOLVE 30756 { 30757 char buf1[ATOM_GET_STR_BUF_SIZE]; 30758 printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 30759 } 30760 #endif 30761 /* check the indirect exports */ 30762 for(i = 0; i < m->export_entries_count; i++) { 30763 JSExportEntry *me = &m->export_entries[i]; 30764 if (me->export_type == JS_EXPORT_TYPE_INDIRECT && 30765 me->local_name != JS_ATOM__star_) { 30766 JSResolveResultEnum ret; 30767 JSExportEntry *res_me; 30768 JSModuleDef *res_m, *m1; 30769 m1 = m->req_module_entries[me->u.req_module_idx].module; 30770 ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); 30771 if (ret != JS_RESOLVE_RES_FOUND) { 30772 js_resolve_export_throw_error(ctx, ret, m, me->export_name); 30773 goto fail; 30774 } 30775 } 30776 } 30777 30778 #ifdef DUMP_MODULE_RESOLVE 30779 { 30780 printf("exported bindings:\n"); 30781 for(i = 0; i < m->export_entries_count; i++) { 30782 JSExportEntry *me = &m->export_entries[i]; 30783 printf(" name="); print_atom(ctx, me->export_name); 30784 printf(" local="); print_atom(ctx, me->local_name); 30785 printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); 30786 } 30787 } 30788 #endif 30789 30790 is_c_module = (m->init_func != NULL); 30791 30792 if (!is_c_module) { 30793 p = JS_VALUE_GET_OBJ(m->func_obj); 30794 var_refs = p->u.func.var_refs; 30795 30796 for(i = 0; i < m->import_entries_count; i++) { 30797 mi = &m->import_entries[i]; 30798 #ifdef DUMP_MODULE_RESOLVE 30799 printf("import var_idx=%d name=", mi->var_idx); 30800 print_atom(ctx, mi->import_name); 30801 printf(": "); 30802 #endif 30803 m1 = m->req_module_entries[mi->req_module_idx].module; 30804 if (mi->is_star) { 30805 JSValue val; 30806 /* name space import */ 30807 val = JS_GetModuleNamespace(ctx, m1); 30808 if (JS_IsException(val)) 30809 goto fail; 30810 set_value(ctx, &var_refs[mi->var_idx]->value, val); 30811 #ifdef DUMP_MODULE_RESOLVE 30812 printf("namespace\n"); 30813 #endif 30814 } else { 30815 JSResolveResultEnum ret; 30816 JSExportEntry *res_me; 30817 JSModuleDef *res_m; 30818 JSObject *p1; 30819 30820 ret = js_resolve_export(ctx, &res_m, 30821 &res_me, m1, mi->import_name); 30822 if (ret != JS_RESOLVE_RES_FOUND) { 30823 js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); 30824 goto fail; 30825 } 30826 if (res_me->local_name == JS_ATOM__star_) { 30827 JSValue val; 30828 JSModuleDef *m2; 30829 /* name space import from */ 30830 m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; 30831 val = JS_GetModuleNamespace(ctx, m2); 30832 if (JS_IsException(val)) 30833 goto fail; 30834 var_ref = js_create_var_ref(ctx, TRUE); 30835 if (!var_ref) { 30836 JS_FreeValue(ctx, val); 30837 goto fail; 30838 } 30839 set_value(ctx, &var_ref->value, val); 30840 var_refs[mi->var_idx] = var_ref; 30841 #ifdef DUMP_MODULE_RESOLVE 30842 printf("namespace from\n"); 30843 #endif 30844 } else { 30845 var_ref = res_me->u.local.var_ref; 30846 if (!var_ref) { 30847 p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 30848 var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; 30849 } 30850 js_rc(var_ref)->ref_count++; 30851 var_refs[mi->var_idx] = var_ref; 30852 #ifdef DUMP_MODULE_RESOLVE 30853 printf("local export (var_ref=%p)\n", var_ref); 30854 #endif 30855 } 30856 } 30857 } 30858 30859 /* keep the exported variables in the module export entries (they 30860 are used when the eval function is deleted and cannot be 30861 initialized before in case imports are exported) */ 30862 for(i = 0; i < m->export_entries_count; i++) { 30863 JSExportEntry *me = &m->export_entries[i]; 30864 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 30865 var_ref = var_refs[me->u.local.var_idx]; 30866 js_rc(var_ref)->ref_count++; 30867 me->u.local.var_ref = var_ref; 30868 } 30869 } 30870 30871 /* initialize the global variables */ 30872 ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); 30873 if (JS_IsException(ret_val)) 30874 goto fail; 30875 JS_FreeValue(ctx, ret_val); 30876 } 30877 30878 assert(m->dfs_ancestor_index <= m->dfs_index); 30879 if (m->dfs_index == m->dfs_ancestor_index) { 30880 for(;;) { 30881 /* pop m1 from stack */ 30882 m1 = *pstack_top; 30883 *pstack_top = m1->stack_prev; 30884 m1->status = JS_MODULE_STATUS_LINKED; 30885 if (m1 == m) 30886 break; 30887 } 30888 } 30889 30890 #ifdef DUMP_MODULE_RESOLVE 30891 printf("js_inner_module_linking done\n"); 30892 #endif 30893 return index; 30894 fail: 30895 return -1; 30896 } 30897 30898 /* Prepare a module to be executed by resolving all the imported 30899 variables. */ 30900 static int js_link_module(JSContext *ctx, JSModuleDef *m) 30901 { 30902 JSModuleDef *stack_top, *m1; 30903 30904 #ifdef DUMP_MODULE_RESOLVE 30905 { 30906 char buf1[ATOM_GET_STR_BUF_SIZE]; 30907 printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 30908 } 30909 #endif 30910 assert(m->status == JS_MODULE_STATUS_UNLINKED || 30911 m->status == JS_MODULE_STATUS_LINKED || 30912 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 30913 m->status == JS_MODULE_STATUS_EVALUATED); 30914 stack_top = NULL; 30915 if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) { 30916 while (stack_top != NULL) { 30917 m1 = stack_top; 30918 assert(m1->status == JS_MODULE_STATUS_LINKING); 30919 m1->status = JS_MODULE_STATUS_UNLINKED; 30920 stack_top = m1->stack_prev; 30921 } 30922 return -1; 30923 } 30924 assert(stack_top == NULL); 30925 assert(m->status == JS_MODULE_STATUS_LINKED || 30926 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 30927 m->status == JS_MODULE_STATUS_EVALUATED); 30928 return 0; 30929 } 30930 30931 /* return JS_ATOM_NULL if the name cannot be found. Only works with 30932 not striped bytecode functions. */ 30933 JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) 30934 { 30935 JSStackFrame *sf; 30936 JSFunctionBytecode *b; 30937 JSObject *p; 30938 /* XXX: currently we just use the filename of the englobing 30939 function from the debug info. May need to add a ScriptOrModule 30940 info in JSFunctionBytecode. */ 30941 sf = ctx->rt->current_stack_frame; 30942 if (!sf) 30943 return JS_ATOM_NULL; 30944 while (n_stack_levels-- > 0) { 30945 sf = sf->prev_frame; 30946 if (!sf) 30947 return JS_ATOM_NULL; 30948 } 30949 for(;;) { 30950 if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) 30951 return JS_ATOM_NULL; 30952 p = JS_VALUE_GET_OBJ(sf->cur_func); 30953 if (!js_class_has_bytecode(p->class_id)) 30954 return JS_ATOM_NULL; 30955 b = p->u.func.function_bytecode; 30956 if (!b->is_direct_or_indirect_eval) { 30957 if (!b->has_debug) 30958 return JS_ATOM_NULL; 30959 return JS_DupAtom(ctx, b->debug.filename); 30960 } else { 30961 sf = sf->prev_frame; 30962 if (!sf) 30963 return JS_ATOM_NULL; 30964 } 30965 } 30966 } 30967 30968 JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) 30969 { 30970 return JS_DupAtom(ctx, m->module_name); 30971 } 30972 30973 JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) 30974 { 30975 JSValue obj; 30976 /* allocate meta_obj only if requested to save memory */ 30977 obj = m->meta_obj; 30978 if (JS_IsUndefined(obj)) { 30979 obj = JS_NewObjectProto(ctx, JS_NULL); 30980 if (JS_IsException(obj)) 30981 return JS_EXCEPTION; 30982 m->meta_obj = obj; 30983 } 30984 return JS_DupValue(ctx, obj); 30985 } 30986 30987 static JSValue js_import_meta(JSContext *ctx) 30988 { 30989 JSAtom filename; 30990 JSModuleDef *m; 30991 30992 filename = JS_GetScriptOrModuleName(ctx, 0); 30993 if (filename == JS_ATOM_NULL) 30994 goto fail; 30995 30996 /* XXX: inefficient, need to add a module or script pointer in 30997 JSFunctionBytecode */ 30998 m = js_find_loaded_module(ctx, filename); 30999 JS_FreeAtom(ctx, filename); 31000 if (!m) { 31001 fail: 31002 JS_ThrowTypeError(ctx, "import.meta not supported in this context"); 31003 return JS_EXCEPTION; 31004 } 31005 return JS_GetImportMeta(ctx, m); 31006 } 31007 31008 static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) 31009 { 31010 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); 31011 } 31012 31013 static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, 31014 int argc, JSValueConst *argv, int magic, JSValue *func_data) 31015 { 31016 JSValueConst *resolving_funcs = (JSValueConst *)func_data; 31017 JSValueConst error; 31018 JSValue ret; 31019 31020 /* XXX: check if the test is necessary */ 31021 if (argc >= 1) 31022 error = argv[0]; 31023 else 31024 error = JS_UNDEFINED; 31025 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 31026 1, &error); 31027 JS_FreeValue(ctx, ret); 31028 return JS_UNDEFINED; 31029 } 31030 31031 static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, 31032 int argc, JSValueConst *argv, int magic, JSValue *func_data) 31033 { 31034 JSValueConst *resolving_funcs = (JSValueConst *)func_data; 31035 JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); 31036 JSValue ret, ns; 31037 31038 /* return the module namespace */ 31039 ns = JS_GetModuleNamespace(ctx, m); 31040 if (JS_IsException(ns)) { 31041 JSValue err = JS_GetException(ctx); 31042 js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); 31043 return JS_UNDEFINED; 31044 } 31045 ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 31046 1, (JSValueConst *)&ns); 31047 JS_FreeValue(ctx, ret); 31048 JS_FreeValue(ctx, ns); 31049 return JS_UNDEFINED; 31050 } 31051 31052 static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, 31053 const char *filename, 31054 JSValueConst *resolving_funcs, 31055 JSValueConst attributes) 31056 { 31057 JSValue evaluate_promise; 31058 JSModuleDef *m; 31059 JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; 31060 JSValueConst func_data[3]; 31061 31062 m = js_host_resolve_imported_module(ctx, basename, filename, attributes); 31063 if (!m) 31064 goto fail; 31065 31066 if (js_resolve_module(ctx, m) < 0) { 31067 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); 31068 goto fail; 31069 } 31070 31071 /* Evaluate the module code */ 31072 func_obj = JS_NewModuleValue(ctx, m); 31073 evaluate_promise = JS_EvalFunction(ctx, func_obj); 31074 if (JS_IsException(evaluate_promise)) { 31075 fail: 31076 err = JS_GetException(ctx); 31077 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 31078 1, (JSValueConst *)&err); 31079 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ 31080 JS_FreeValue(ctx, err); 31081 return; 31082 } 31083 31084 func_obj = JS_NewModuleValue(ctx, m); 31085 func_data[0] = resolving_funcs[0]; 31086 func_data[1] = resolving_funcs[1]; 31087 func_data[2] = func_obj; 31088 evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); 31089 evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); 31090 JS_FreeValue(ctx, func_obj); 31091 ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); 31092 JS_FreeValue(ctx, ret); 31093 JS_FreeValue(ctx, evaluate_resolving_funcs[0]); 31094 JS_FreeValue(ctx, evaluate_resolving_funcs[1]); 31095 JS_FreeValue(ctx, evaluate_promise); 31096 } 31097 31098 /* Return a promise or an exception in case of memory error. Used by 31099 os.Worker() */ 31100 JSValue JS_LoadModule(JSContext *ctx, const char *basename, 31101 const char *filename) 31102 { 31103 JSValue promise, resolving_funcs[2]; 31104 31105 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 31106 if (JS_IsException(promise)) 31107 return JS_EXCEPTION; 31108 JS_LoadModuleInternal(ctx, basename, filename, 31109 (JSValueConst *)resolving_funcs, JS_UNDEFINED); 31110 JS_FreeValue(ctx, resolving_funcs[0]); 31111 JS_FreeValue(ctx, resolving_funcs[1]); 31112 return promise; 31113 } 31114 31115 static JSValue js_dynamic_import_job(JSContext *ctx, 31116 int argc, JSValueConst *argv) 31117 { 31118 JSValueConst *resolving_funcs = argv; 31119 JSValueConst basename_val = argv[2]; 31120 JSValueConst specifier = argv[3]; 31121 JSValueConst attributes = argv[4]; 31122 const char *basename = NULL, *filename; 31123 JSValue ret, err; 31124 31125 if (!JS_IsString(basename_val)) { 31126 JS_ThrowTypeError(ctx, "no function filename for import()"); 31127 goto exception; 31128 } 31129 basename = JS_ToCString(ctx, basename_val); 31130 if (!basename) 31131 goto exception; 31132 31133 filename = JS_ToCString(ctx, specifier); 31134 if (!filename) 31135 goto exception; 31136 31137 JS_LoadModuleInternal(ctx, basename, filename, 31138 resolving_funcs, attributes); 31139 JS_FreeCString(ctx, filename); 31140 JS_FreeCString(ctx, basename); 31141 return JS_UNDEFINED; 31142 exception: 31143 err = JS_GetException(ctx); 31144 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 31145 1, (JSValueConst *)&err); 31146 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ 31147 JS_FreeValue(ctx, err); 31148 JS_FreeCString(ctx, basename); 31149 return JS_UNDEFINED; 31150 } 31151 31152 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier, JSValueConst options) 31153 { 31154 JSAtom basename; 31155 JSValue promise, resolving_funcs[2], basename_val, err, ret; 31156 JSValue specifier_str = JS_UNDEFINED, attributes = JS_UNDEFINED, attributes_obj = JS_UNDEFINED; 31157 JSValueConst args[5]; 31158 31159 basename = JS_GetScriptOrModuleName(ctx, 0); 31160 if (basename == JS_ATOM_NULL) 31161 basename_val = JS_NULL; 31162 else 31163 basename_val = JS_AtomToValue(ctx, basename); 31164 JS_FreeAtom(ctx, basename); 31165 if (JS_IsException(basename_val)) 31166 return basename_val; 31167 31168 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 31169 if (JS_IsException(promise)) { 31170 JS_FreeValue(ctx, basename_val); 31171 return promise; 31172 } 31173 31174 /* the string conversion must occur here */ 31175 specifier_str = JS_ToString(ctx, specifier); 31176 if (JS_IsException(specifier_str)) 31177 goto exception; 31178 31179 if (!JS_IsUndefined(options)) { 31180 if (!JS_IsObject(options)) { 31181 JS_ThrowTypeError(ctx, "options must be an object"); 31182 goto exception; 31183 } 31184 attributes_obj = JS_GetProperty(ctx, options, JS_ATOM_with); 31185 if (JS_IsException(attributes_obj)) 31186 goto exception; 31187 if (!JS_IsUndefined(attributes_obj)) { 31188 JSPropertyEnum *atoms; 31189 uint32_t atoms_len, i; 31190 JSValue val; 31191 31192 if (!JS_IsObject(attributes_obj)) { 31193 JS_ThrowTypeError(ctx, "options.with must be an object"); 31194 goto exception; 31195 } 31196 attributes = JS_NewObjectProto(ctx, JS_NULL); 31197 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &atoms_len, JS_VALUE_GET_OBJ(attributes_obj), 31198 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { 31199 goto exception; 31200 } 31201 for(i = 0; i < atoms_len; i++) { 31202 val = JS_GetProperty(ctx, attributes_obj, atoms[i].atom); 31203 if (JS_IsException(val)) 31204 goto exception1; 31205 if (!JS_IsString(val)) { 31206 JS_FreeValue(ctx, val); 31207 JS_ThrowTypeError(ctx, "module attribute values must be strings"); 31208 goto exception1; 31209 } 31210 if (JS_DefinePropertyValue(ctx, attributes, atoms[i].atom, val, 31211 JS_PROP_C_W_E) < 0) { 31212 exception1: 31213 JS_FreePropertyEnum(ctx, atoms, atoms_len); 31214 goto exception; 31215 } 31216 } 31217 JS_FreePropertyEnum(ctx, atoms, atoms_len); 31218 if (ctx->rt->module_check_attrs && 31219 ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, attributes) < 0) { 31220 goto exception; 31221 } 31222 JS_FreeValue(ctx, attributes_obj); 31223 } 31224 } 31225 31226 args[0] = resolving_funcs[0]; 31227 args[1] = resolving_funcs[1]; 31228 args[2] = basename_val; 31229 args[3] = specifier_str; 31230 args[4] = attributes; 31231 31232 /* cannot run JS_LoadModuleInternal synchronously because it would 31233 cause an unexpected recursion in js_evaluate_module() */ 31234 JS_EnqueueJob(ctx, js_dynamic_import_job, 5, args); 31235 done: 31236 JS_FreeValue(ctx, basename_val); 31237 JS_FreeValue(ctx, resolving_funcs[0]); 31238 JS_FreeValue(ctx, resolving_funcs[1]); 31239 JS_FreeValue(ctx, specifier_str); 31240 JS_FreeValue(ctx, attributes); 31241 return promise; 31242 exception: 31243 JS_FreeValue(ctx, attributes_obj); 31244 err = JS_GetException(ctx); 31245 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 31246 1, (JSValueConst *)&err); 31247 JS_FreeValue(ctx, ret); 31248 JS_FreeValue(ctx, err); 31249 goto done; 31250 } 31251 31252 static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) 31253 { 31254 m->status = JS_MODULE_STATUS_EVALUATED; 31255 if (!JS_IsUndefined(m->promise)) { 31256 JSValue value, ret_val; 31257 assert(m->cycle_root == m); 31258 value = JS_UNDEFINED; 31259 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, 31260 1, (JSValueConst *)&value); 31261 JS_FreeValue(ctx, ret_val); 31262 } 31263 } 31264 31265 typedef struct { 31266 JSModuleDef **tab; 31267 int count; 31268 int size; 31269 } ExecModuleList; 31270 31271 /* XXX: slow. Could use a linked list instead of ExecModuleList */ 31272 static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m) 31273 { 31274 int i; 31275 for(i = 0; i < exec_list->count; i++) { 31276 if (exec_list->tab[i] == m) 31277 return TRUE; 31278 } 31279 return FALSE; 31280 } 31281 31282 static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module, 31283 ExecModuleList *exec_list) 31284 { 31285 int i; 31286 31287 if (js_check_stack_overflow(ctx->rt, 0)) { 31288 JS_ThrowStackOverflow(ctx); 31289 return -1; 31290 } 31291 for(i = 0; i < module->async_parent_modules_count; i++) { 31292 JSModuleDef *m = module->async_parent_modules[i]; 31293 if (!find_in_exec_module_list(exec_list, m) && 31294 !m->cycle_root->eval_has_exception) { 31295 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 31296 assert(!m->eval_has_exception); 31297 assert(m->async_evaluation); 31298 assert(m->pending_async_dependencies > 0); 31299 m->pending_async_dependencies--; 31300 if (m->pending_async_dependencies == 0) { 31301 if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) { 31302 return -1; 31303 } 31304 exec_list->tab[exec_list->count++] = m; 31305 if (!m->has_tla) { 31306 if (gather_available_ancestors(ctx, m, exec_list)) 31307 return -1; 31308 } 31309 } 31310 } 31311 } 31312 return 0; 31313 } 31314 31315 static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque) 31316 { 31317 JSModuleDef *m1 = *(JSModuleDef **)p1; 31318 JSModuleDef *m2 = *(JSModuleDef **)p2; 31319 return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) - 31320 (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp); 31321 } 31322 31323 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m); 31324 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, 31325 JSValue *pvalue); 31326 #ifdef DUMP_MODULE_EXEC 31327 static void js_dump_module(JSContext *ctx, const char *str, JSModuleDef *m) 31328 { 31329 char buf1[ATOM_GET_STR_BUF_SIZE]; 31330 static const char *module_status_str[] = { "unlinked", "linking", "linked", "evaluating", "evaluating_async", "evaluated" }; 31331 printf("%s: %s status=%s\n", str, JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name), module_status_str[m->status]); 31332 } 31333 #endif 31334 31335 static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, 31336 int argc, JSValueConst *argv, int magic, JSValue *func_data) 31337 { 31338 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); 31339 JSValueConst error = argv[0]; 31340 int i; 31341 31342 #ifdef DUMP_MODULE_EXEC 31343 js_dump_module(ctx, __func__, module); 31344 #endif 31345 if (js_check_stack_overflow(ctx->rt, 0)) 31346 return JS_ThrowStackOverflow(ctx); 31347 31348 if (module->status == JS_MODULE_STATUS_EVALUATED) { 31349 assert(module->eval_has_exception); 31350 return JS_UNDEFINED; 31351 } 31352 31353 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 31354 assert(!module->eval_has_exception); 31355 assert(module->async_evaluation); 31356 31357 module->eval_has_exception = TRUE; 31358 module->eval_exception = JS_DupValue(ctx, error); 31359 module->status = JS_MODULE_STATUS_EVALUATED; 31360 module->async_evaluation = FALSE; 31361 31362 if (!JS_IsUndefined(module->promise)) { 31363 JSValue ret_val; 31364 assert(module->cycle_root == module); 31365 ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED, 31366 1, &error); 31367 JS_FreeValue(ctx, ret_val); 31368 } 31369 31370 for(i = 0; i < module->async_parent_modules_count; i++) { 31371 JSModuleDef *m = module->async_parent_modules[i]; 31372 JSValue m_obj = JS_NewModuleValue(ctx, m); 31373 js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, 31374 &m_obj); 31375 JS_FreeValue(ctx, m_obj); 31376 } 31377 return JS_UNDEFINED; 31378 } 31379 31380 static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, 31381 int argc, JSValueConst *argv, int magic, JSValue *func_data) 31382 { 31383 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); 31384 ExecModuleList exec_list_s, *exec_list = &exec_list_s; 31385 int i; 31386 31387 #ifdef DUMP_MODULE_EXEC 31388 js_dump_module(ctx, __func__, module); 31389 #endif 31390 if (module->status == JS_MODULE_STATUS_EVALUATED) { 31391 assert(module->eval_has_exception); 31392 return JS_UNDEFINED; 31393 } 31394 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 31395 assert(!module->eval_has_exception); 31396 assert(module->async_evaluation); 31397 module->async_evaluation = FALSE; 31398 js_set_module_evaluated(ctx, module); 31399 31400 exec_list->tab = NULL; 31401 exec_list->count = 0; 31402 exec_list->size = 0; 31403 31404 if (gather_available_ancestors(ctx, module, exec_list) < 0) { 31405 js_free(ctx, exec_list->tab); 31406 return JS_EXCEPTION; 31407 } 31408 31409 /* sort by increasing async_evaluation timestamp */ 31410 rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]), 31411 exec_module_list_cmp, NULL); 31412 31413 for(i = 0; i < exec_list->count; i++) { 31414 JSModuleDef *m = exec_list->tab[i]; 31415 #ifdef DUMP_MODULE_EXEC 31416 printf(" %d/%d", i, exec_list->count); js_dump_module(ctx, "", m); 31417 #endif 31418 if (m->status == JS_MODULE_STATUS_EVALUATED) { 31419 assert(m->eval_has_exception); 31420 } else if (m->has_tla) { 31421 js_execute_async_module(ctx, m); 31422 } else { 31423 JSValue error; 31424 if (js_execute_sync_module(ctx, m, &error) < 0) { 31425 JSValue m_obj = JS_NewModuleValue(ctx, m); 31426 js_async_module_execution_rejected(ctx, JS_UNDEFINED, 31427 1, (JSValueConst *)&error, 0, 31428 &m_obj); 31429 JS_FreeValue(ctx, m_obj); 31430 JS_FreeValue(ctx, error); 31431 } else { 31432 m->async_evaluation = FALSE; 31433 js_set_module_evaluated(ctx, m); 31434 } 31435 } 31436 } 31437 js_free(ctx, exec_list->tab); 31438 return JS_UNDEFINED; 31439 } 31440 31441 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) 31442 { 31443 JSValue promise, m_obj; 31444 JSValue resolve_funcs[2], ret_val; 31445 #ifdef DUMP_MODULE_EXEC 31446 js_dump_module(ctx, __func__, m); 31447 #endif 31448 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); 31449 if (JS_IsException(promise)) 31450 return -1; 31451 m_obj = JS_NewModuleValue(ctx, m); 31452 resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); 31453 resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); 31454 ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); 31455 JS_FreeValue(ctx, ret_val); 31456 JS_FreeValue(ctx, m_obj); 31457 JS_FreeValue(ctx, resolve_funcs[0]); 31458 JS_FreeValue(ctx, resolve_funcs[1]); 31459 JS_FreeValue(ctx, promise); 31460 return 0; 31461 } 31462 31463 /* return < 0 in case of exception. *pvalue contains the exception. */ 31464 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, 31465 JSValue *pvalue) 31466 { 31467 #ifdef DUMP_MODULE_EXEC 31468 js_dump_module(ctx, __func__, m); 31469 #endif 31470 if (m->init_func) { 31471 /* C module init : no asynchronous execution */ 31472 if (m->init_func(ctx, m) < 0) 31473 goto fail; 31474 } else { 31475 JSValue promise; 31476 JSPromiseStateEnum state; 31477 31478 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); 31479 if (JS_IsException(promise)) 31480 goto fail; 31481 state = JS_PromiseState(ctx, promise); 31482 if (state == JS_PROMISE_FULFILLED) { 31483 JS_FreeValue(ctx, promise); 31484 } else if (state == JS_PROMISE_REJECTED) { 31485 *pvalue = JS_PromiseResult(ctx, promise); 31486 JS_FreeValue(ctx, promise); 31487 return -1; 31488 } else { 31489 JS_FreeValue(ctx, promise); 31490 JS_ThrowTypeError(ctx, "promise is pending"); 31491 fail: 31492 *pvalue = JS_GetException(ctx); 31493 return -1; 31494 } 31495 } 31496 *pvalue = JS_UNDEFINED; 31497 return 0; 31498 } 31499 31500 /* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1, 31501 exception) */ 31502 static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, 31503 int index, JSModuleDef **pstack_top, 31504 JSValue *pvalue) 31505 { 31506 JSModuleDef *m1; 31507 int i; 31508 31509 #ifdef DUMP_MODULE_EXEC 31510 js_dump_module(ctx, __func__, m); 31511 #endif 31512 31513 if (js_check_stack_overflow(ctx->rt, 0)) { 31514 JS_ThrowStackOverflow(ctx); 31515 *pvalue = JS_GetException(ctx); 31516 return -1; 31517 } 31518 31519 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31520 m->status == JS_MODULE_STATUS_EVALUATED) { 31521 if (m->eval_has_exception) { 31522 *pvalue = JS_DupValue(ctx, m->eval_exception); 31523 return -1; 31524 } else { 31525 *pvalue = JS_UNDEFINED; 31526 return index; 31527 } 31528 } 31529 if (m->status == JS_MODULE_STATUS_EVALUATING) { 31530 *pvalue = JS_UNDEFINED; 31531 return index; 31532 } 31533 assert(m->status == JS_MODULE_STATUS_LINKED); 31534 31535 m->status = JS_MODULE_STATUS_EVALUATING; 31536 m->dfs_index = index; 31537 m->dfs_ancestor_index = index; 31538 m->pending_async_dependencies = 0; 31539 index++; 31540 /* push 'm' on stack */ 31541 m->stack_prev = *pstack_top; 31542 *pstack_top = m; 31543 31544 for(i = 0; i < m->req_module_entries_count; i++) { 31545 JSReqModuleEntry *rme = &m->req_module_entries[i]; 31546 m1 = rme->module; 31547 index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue); 31548 if (index < 0) 31549 return -1; 31550 assert(m1->status == JS_MODULE_STATUS_EVALUATING || 31551 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31552 m1->status == JS_MODULE_STATUS_EVALUATED); 31553 if (m1->status == JS_MODULE_STATUS_EVALUATING) { 31554 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, 31555 m1->dfs_ancestor_index); 31556 } else { 31557 m1 = m1->cycle_root; 31558 assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31559 m1->status == JS_MODULE_STATUS_EVALUATED); 31560 if (m1->eval_has_exception) { 31561 *pvalue = JS_DupValue(ctx, m1->eval_exception); 31562 return -1; 31563 } 31564 } 31565 if (m1->async_evaluation) { 31566 m->pending_async_dependencies++; 31567 if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) { 31568 *pvalue = JS_GetException(ctx); 31569 return -1; 31570 } 31571 m1->async_parent_modules[m1->async_parent_modules_count++] = m; 31572 } 31573 } 31574 31575 if (m->pending_async_dependencies > 0) { 31576 assert(!m->async_evaluation); 31577 m->async_evaluation = TRUE; 31578 m->async_evaluation_timestamp = 31579 ctx->rt->module_async_evaluation_next_timestamp++; 31580 } else if (m->has_tla) { 31581 assert(!m->async_evaluation); 31582 m->async_evaluation = TRUE; 31583 m->async_evaluation_timestamp = 31584 ctx->rt->module_async_evaluation_next_timestamp++; 31585 js_execute_async_module(ctx, m); 31586 } else { 31587 if (js_execute_sync_module(ctx, m, pvalue) < 0) 31588 return -1; 31589 } 31590 31591 assert(m->dfs_ancestor_index <= m->dfs_index); 31592 if (m->dfs_index == m->dfs_ancestor_index) { 31593 for(;;) { 31594 /* pop m1 from stack */ 31595 m1 = *pstack_top; 31596 *pstack_top = m1->stack_prev; 31597 if (!m1->async_evaluation) { 31598 m1->status = JS_MODULE_STATUS_EVALUATED; 31599 } else { 31600 m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC; 31601 } 31602 /* spec bug: cycle_root must be assigned before the test */ 31603 m1->cycle_root = m; 31604 if (m1 == m) 31605 break; 31606 } 31607 } 31608 *pvalue = JS_UNDEFINED; 31609 return index; 31610 } 31611 31612 /* Run the <eval> function of the module and of all its requested 31613 modules. Return a promise or an exception. */ 31614 static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) 31615 { 31616 JSModuleDef *m1, *stack_top; 31617 JSValue ret_val, result; 31618 31619 #ifdef DUMP_MODULE_EXEC 31620 js_dump_module(ctx, __func__, m); 31621 #endif 31622 assert(m->status == JS_MODULE_STATUS_LINKED || 31623 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31624 m->status == JS_MODULE_STATUS_EVALUATED); 31625 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31626 m->status == JS_MODULE_STATUS_EVALUATED) { 31627 m = m->cycle_root; 31628 } 31629 /* a promise may be created only on the cycle_root of a cycle */ 31630 if (!JS_IsUndefined(m->promise)) 31631 return JS_DupValue(ctx, m->promise); 31632 m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); 31633 if (JS_IsException(m->promise)) 31634 return JS_EXCEPTION; 31635 31636 stack_top = NULL; 31637 if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) { 31638 while (stack_top != NULL) { 31639 m1 = stack_top; 31640 assert(m1->status == JS_MODULE_STATUS_EVALUATING); 31641 m1->status = JS_MODULE_STATUS_EVALUATED; 31642 m1->eval_has_exception = TRUE; 31643 m1->eval_exception = JS_DupValue(ctx, result); 31644 m1->cycle_root = m; /* spec bug: should be present */ 31645 stack_top = m1->stack_prev; 31646 } 31647 JS_FreeValue(ctx, result); 31648 assert(m->status == JS_MODULE_STATUS_EVALUATED); 31649 assert(m->eval_has_exception); 31650 ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, 31651 1, (JSValueConst *)&m->eval_exception); 31652 JS_FreeValue(ctx, ret_val); 31653 } else { 31654 #ifdef DUMP_MODULE_EXEC 31655 js_dump_module(ctx, " done", m); 31656 #endif 31657 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 31658 m->status == JS_MODULE_STATUS_EVALUATED); 31659 assert(!m->eval_has_exception); 31660 if (!m->async_evaluation) { 31661 JSValue value; 31662 assert(m->status == JS_MODULE_STATUS_EVALUATED); 31663 value = JS_UNDEFINED; 31664 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, 31665 1, (JSValueConst *)&value); 31666 JS_FreeValue(ctx, ret_val); 31667 } 31668 assert(stack_top == NULL); 31669 } 31670 return JS_DupValue(ctx, m->promise); 31671 } 31672 31673 static __exception int js_parse_with_clause(JSParseState *s, JSReqModuleEntry *rme) 31674 { 31675 JSContext *ctx = s->ctx; 31676 JSAtom key; 31677 int ret; 31678 const uint8_t *key_token_ptr; 31679 31680 if (next_token(s)) 31681 return -1; 31682 if (js_parse_expect(s, '{')) 31683 return -1; 31684 while (s->token.val != '}') { 31685 key_token_ptr = s->token.ptr; 31686 if (s->token.val == TOK_STRING) { 31687 key = JS_ValueToAtom(ctx, s->token.u.str.str); 31688 if (key == JS_ATOM_NULL) 31689 return -1; 31690 } else { 31691 if (!token_is_ident(s->token.val)) { 31692 js_parse_error(s, "identifier expected"); 31693 return -1; 31694 } 31695 key = JS_DupAtom(ctx, s->token.u.ident.atom); 31696 } 31697 if (next_token(s)) 31698 return -1; 31699 if (js_parse_expect(s, ':')) { 31700 JS_FreeAtom(ctx, key); 31701 return -1; 31702 } 31703 if (s->token.val != TOK_STRING) { 31704 js_parse_error_pos(s, key_token_ptr, "string expected"); 31705 return -1; 31706 } 31707 if (JS_IsUndefined(rme->attributes)) { 31708 JSValue attributes = JS_NewObjectProto(ctx, JS_NULL); 31709 if (JS_IsException(attributes)) { 31710 JS_FreeAtom(ctx, key); 31711 return -1; 31712 } 31713 rme->attributes = attributes; 31714 } 31715 ret = JS_HasProperty(ctx, rme->attributes, key); 31716 if (ret != 0) { 31717 JS_FreeAtom(ctx, key); 31718 if (ret < 0) 31719 return -1; 31720 else 31721 return js_parse_error(s, "duplicate with key"); 31722 } 31723 ret = JS_DefinePropertyValue(ctx, rme->attributes, key, 31724 JS_DupValue(ctx, s->token.u.str.str), JS_PROP_C_W_E); 31725 JS_FreeAtom(ctx, key); 31726 if (ret < 0) 31727 return -1; 31728 if (next_token(s)) 31729 return -1; 31730 if (s->token.val != ',') 31731 break; 31732 if (next_token(s)) 31733 return -1; 31734 } 31735 if (!JS_IsUndefined(rme->attributes) && 31736 ctx->rt->module_check_attrs && 31737 ctx->rt->module_check_attrs(ctx, ctx->rt->module_loader_opaque, rme->attributes) < 0) { 31738 return -1; 31739 } 31740 return js_parse_expect(s, '}'); 31741 } 31742 31743 /* return the module index in m->req_module_entries[] or < 0 if error */ 31744 static __exception int js_parse_from_clause(JSParseState *s, JSModuleDef *m) 31745 { 31746 JSAtom module_name; 31747 int idx; 31748 31749 if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { 31750 js_parse_error(s, "from clause expected"); 31751 return -1; 31752 } 31753 if (next_token(s)) 31754 return -1; 31755 if (s->token.val != TOK_STRING) { 31756 js_parse_error(s, "string expected"); 31757 return -1; 31758 } 31759 module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 31760 if (module_name == JS_ATOM_NULL) 31761 return -1; 31762 if (next_token(s)) { 31763 JS_FreeAtom(s->ctx, module_name); 31764 return -1; 31765 } 31766 31767 idx = add_req_module_entry(s->ctx, m, module_name); 31768 JS_FreeAtom(s->ctx, module_name); 31769 if (idx < 0) 31770 return -1; 31771 if (s->token.val == TOK_WITH) { 31772 if (js_parse_with_clause(s, &m->req_module_entries[idx])) 31773 return -1; 31774 } 31775 return idx; 31776 } 31777 31778 static __exception int js_parse_export(JSParseState *s) 31779 { 31780 JSContext *ctx = s->ctx; 31781 JSModuleDef *m = s->cur_func->module; 31782 JSAtom local_name, export_name; 31783 int first_export, idx, i, tok; 31784 JSExportEntry *me; 31785 31786 if (next_token(s)) 31787 return -1; 31788 31789 tok = s->token.val; 31790 if (tok == TOK_CLASS) { 31791 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED); 31792 } else if (tok == TOK_FUNCTION || 31793 (token_is_pseudo_keyword(s, JS_ATOM_async) && 31794 peek_token(s, TRUE) == TOK_FUNCTION)) { 31795 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, 31796 JS_FUNC_NORMAL, JS_ATOM_NULL, 31797 s->token.ptr, 31798 JS_PARSE_EXPORT_NAMED, NULL); 31799 } 31800 31801 if (next_token(s)) 31802 return -1; 31803 31804 switch(tok) { 31805 case '{': 31806 first_export = m->export_entries_count; 31807 while (s->token.val != '}') { 31808 if (!token_is_ident(s->token.val)) { 31809 js_parse_error(s, "identifier expected"); 31810 return -1; 31811 } 31812 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 31813 export_name = JS_ATOM_NULL; 31814 if (next_token(s)) 31815 goto fail; 31816 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 31817 if (next_token(s)) 31818 goto fail; 31819 if (s->token.val == TOK_STRING) { 31820 if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) { 31821 js_parse_error(s, "contains unpaired surrogate"); 31822 goto fail; 31823 } 31824 export_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 31825 if (export_name == JS_ATOM_NULL) 31826 goto fail; 31827 } else { 31828 if (!token_is_ident(s->token.val)) { 31829 js_parse_error(s, "identifier expected"); 31830 goto fail; 31831 } 31832 export_name = JS_DupAtom(ctx, s->token.u.ident.atom); 31833 } 31834 if (next_token(s)) { 31835 fail: 31836 JS_FreeAtom(ctx, local_name); 31837 fail1: 31838 JS_FreeAtom(ctx, export_name); 31839 return -1; 31840 } 31841 } else { 31842 export_name = JS_DupAtom(ctx, local_name); 31843 } 31844 me = add_export_entry(s, m, local_name, export_name, 31845 JS_EXPORT_TYPE_LOCAL); 31846 JS_FreeAtom(ctx, local_name); 31847 JS_FreeAtom(ctx, export_name); 31848 if (!me) 31849 return -1; 31850 if (s->token.val != ',') 31851 break; 31852 if (next_token(s)) 31853 return -1; 31854 } 31855 if (js_parse_expect(s, '}')) 31856 return -1; 31857 if (token_is_pseudo_keyword(s, JS_ATOM_from)) { 31858 idx = js_parse_from_clause(s, m); 31859 if (idx < 0) 31860 return -1; 31861 for(i = first_export; i < m->export_entries_count; i++) { 31862 me = &m->export_entries[i]; 31863 me->export_type = JS_EXPORT_TYPE_INDIRECT; 31864 me->u.req_module_idx = idx; 31865 } 31866 } 31867 break; 31868 case '*': 31869 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 31870 /* export ns from */ 31871 if (next_token(s)) 31872 return -1; 31873 if (!token_is_ident(s->token.val)) { 31874 js_parse_error(s, "identifier expected"); 31875 return -1; 31876 } 31877 export_name = JS_DupAtom(ctx, s->token.u.ident.atom); 31878 if (next_token(s)) 31879 goto fail1; 31880 idx = js_parse_from_clause(s, m); 31881 if (idx < 0) 31882 goto fail1; 31883 me = add_export_entry(s, m, JS_ATOM__star_, export_name, 31884 JS_EXPORT_TYPE_INDIRECT); 31885 JS_FreeAtom(ctx, export_name); 31886 if (!me) 31887 return -1; 31888 me->u.req_module_idx = idx; 31889 } else { 31890 idx = js_parse_from_clause(s, m); 31891 if (idx < 0) 31892 return -1; 31893 if (add_star_export_entry(ctx, m, idx) < 0) 31894 return -1; 31895 } 31896 break; 31897 case TOK_DEFAULT: 31898 if (s->token.val == TOK_CLASS) { 31899 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT); 31900 } else if (s->token.val == TOK_FUNCTION || 31901 (token_is_pseudo_keyword(s, JS_ATOM_async) && 31902 peek_token(s, TRUE) == TOK_FUNCTION)) { 31903 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, 31904 JS_FUNC_NORMAL, JS_ATOM_NULL, 31905 s->token.ptr, 31906 JS_PARSE_EXPORT_DEFAULT, NULL); 31907 } else { 31908 if (js_parse_assign_expr(s)) 31909 return -1; 31910 } 31911 /* set the name of anonymous functions */ 31912 set_object_name(s, JS_ATOM_default); 31913 31914 /* store the value in the _default_ global variable and export 31915 it */ 31916 local_name = JS_ATOM__default_; 31917 if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0) 31918 return -1; 31919 emit_op(s, OP_scope_put_var_init); 31920 emit_atom(s, local_name); 31921 emit_u16(s, 0); 31922 31923 if (!add_export_entry(s, m, local_name, JS_ATOM_default, 31924 JS_EXPORT_TYPE_LOCAL)) 31925 return -1; 31926 break; 31927 case TOK_VAR: 31928 case TOK_LET: 31929 case TOK_CONST: 31930 return js_parse_var(s, TRUE, tok, TRUE); 31931 default: 31932 return js_parse_error(s, "invalid export syntax"); 31933 } 31934 return js_parse_expect_semi(s); 31935 } 31936 31937 static int add_closure_var(JSContext *ctx, JSFunctionDef *s, 31938 JSClosureTypeEnum closure_type, 31939 int var_idx, JSAtom var_name, 31940 BOOL is_const, BOOL is_lexical, 31941 JSVarKindEnum var_kind); 31942 31943 static int add_import(JSParseState *s, JSModuleDef *m, 31944 JSAtom local_name, JSAtom import_name, BOOL is_star) 31945 { 31946 JSContext *ctx = s->ctx; 31947 int i, var_idx; 31948 JSImportEntry *mi; 31949 31950 if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) 31951 return js_parse_error(s, "invalid import binding"); 31952 31953 if (local_name != JS_ATOM_default) { 31954 for (i = 0; i < s->cur_func->closure_var_count; i++) { 31955 if (s->cur_func->closure_var[i].var_name == local_name) 31956 return js_parse_error(s, "duplicate import binding"); 31957 } 31958 } 31959 31960 var_idx = add_closure_var(ctx, s->cur_func, 31961 is_star ? JS_CLOSURE_MODULE_DECL : JS_CLOSURE_MODULE_IMPORT, 31962 m->import_entries_count, 31963 local_name, TRUE, TRUE, JS_VAR_NORMAL); 31964 if (var_idx < 0) 31965 return -1; 31966 if (js_resize_array(ctx, (void **)&m->import_entries, 31967 sizeof(JSImportEntry), 31968 &m->import_entries_size, 31969 m->import_entries_count + 1)) 31970 return -1; 31971 mi = &m->import_entries[m->import_entries_count++]; 31972 mi->import_name = JS_DupAtom(ctx, import_name); 31973 mi->var_idx = var_idx; 31974 mi->is_star = is_star; 31975 return 0; 31976 } 31977 31978 static __exception int js_parse_import(JSParseState *s) 31979 { 31980 JSContext *ctx = s->ctx; 31981 JSModuleDef *m = s->cur_func->module; 31982 JSAtom local_name, import_name, module_name; 31983 int first_import, i, idx; 31984 31985 if (next_token(s)) 31986 return -1; 31987 31988 first_import = m->import_entries_count; 31989 if (s->token.val == TOK_STRING) { 31990 module_name = JS_ValueToAtom(ctx, s->token.u.str.str); 31991 if (module_name == JS_ATOM_NULL) 31992 return -1; 31993 if (next_token(s)) { 31994 JS_FreeAtom(ctx, module_name); 31995 return -1; 31996 } 31997 idx = add_req_module_entry(ctx, m, module_name); 31998 JS_FreeAtom(ctx, module_name); 31999 if (idx < 0) 32000 return -1; 32001 if (s->token.val == TOK_WITH) { 32002 if (js_parse_with_clause(s, &m->req_module_entries[idx])) 32003 return -1; 32004 } 32005 } else { 32006 if (s->token.val == TOK_IDENT) { 32007 if (s->token.u.ident.is_reserved) { 32008 return js_parse_error_reserved_identifier(s); 32009 } 32010 /* "default" import */ 32011 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 32012 import_name = JS_ATOM_default; 32013 if (next_token(s)) 32014 goto fail; 32015 if (add_import(s, m, local_name, import_name, FALSE)) 32016 goto fail; 32017 JS_FreeAtom(ctx, local_name); 32018 32019 if (s->token.val != ',') 32020 goto end_import_clause; 32021 if (next_token(s)) 32022 return -1; 32023 } 32024 32025 if (s->token.val == '*') { 32026 /* name space import */ 32027 if (next_token(s)) 32028 return -1; 32029 if (!token_is_pseudo_keyword(s, JS_ATOM_as)) 32030 return js_parse_error(s, "expecting 'as'"); 32031 if (next_token(s)) 32032 return -1; 32033 if (!token_is_ident(s->token.val)) { 32034 js_parse_error(s, "identifier expected"); 32035 return -1; 32036 } 32037 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 32038 import_name = JS_ATOM__star_; 32039 if (next_token(s)) 32040 goto fail; 32041 if (add_import(s, m, local_name, import_name, TRUE)) 32042 goto fail; 32043 JS_FreeAtom(ctx, local_name); 32044 } else if (s->token.val == '{') { 32045 if (next_token(s)) 32046 return -1; 32047 32048 while (s->token.val != '}') { 32049 BOOL is_string; 32050 if (s->token.val == TOK_STRING) { 32051 is_string = TRUE; 32052 if (js_string_find_invalid_codepoint(JS_VALUE_GET_STRING(s->token.u.str.str)) >= 0) { 32053 js_parse_error(s, "contains unpaired surrogate"); 32054 return -1; 32055 } 32056 import_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 32057 if (import_name == JS_ATOM_NULL) 32058 return -1; 32059 } else { 32060 is_string = FALSE; 32061 if (!token_is_ident(s->token.val)) { 32062 js_parse_error(s, "identifier expected"); 32063 return -1; 32064 } 32065 import_name = JS_DupAtom(ctx, s->token.u.ident.atom); 32066 } 32067 local_name = JS_ATOM_NULL; 32068 if (next_token(s)) 32069 goto fail; 32070 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 32071 if (next_token(s)) 32072 goto fail; 32073 if (!token_is_ident(s->token.val)) { 32074 js_parse_error(s, "identifier expected"); 32075 goto fail; 32076 } 32077 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 32078 if (next_token(s)) 32079 goto fail; 32080 } else { 32081 if (is_string) { 32082 js_parse_error(s, "expecting 'as'"); 32083 fail: 32084 JS_FreeAtom(ctx, local_name); 32085 JS_FreeAtom(ctx, import_name); 32086 return -1; 32087 } 32088 local_name = JS_DupAtom(ctx, import_name); 32089 } 32090 if (add_import(s, m, local_name, import_name, FALSE)) 32091 goto fail; 32092 JS_FreeAtom(ctx, local_name); 32093 JS_FreeAtom(ctx, import_name); 32094 if (s->token.val != ',') 32095 break; 32096 if (next_token(s)) 32097 return -1; 32098 } 32099 if (js_parse_expect(s, '}')) 32100 return -1; 32101 } 32102 end_import_clause: 32103 idx = js_parse_from_clause(s, m); 32104 if (idx < 0) 32105 return -1; 32106 } 32107 for(i = first_import; i < m->import_entries_count; i++) 32108 m->import_entries[i].req_module_idx = idx; 32109 32110 return js_parse_expect_semi(s); 32111 } 32112 32113 static __exception int js_parse_source_element(JSParseState *s) 32114 { 32115 JSFunctionDef *fd = s->cur_func; 32116 int tok; 32117 32118 if (s->token.val == TOK_FUNCTION || 32119 (token_is_pseudo_keyword(s, JS_ATOM_async) && 32120 peek_token(s, TRUE) == TOK_FUNCTION)) { 32121 if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, 32122 JS_FUNC_NORMAL, JS_ATOM_NULL, 32123 s->token.ptr)) 32124 return -1; 32125 } else if (s->token.val == TOK_EXPORT && fd->module) { 32126 if (js_parse_export(s)) 32127 return -1; 32128 } else if (s->token.val == TOK_IMPORT && fd->module && 32129 ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) { 32130 /* the peek_token is needed to avoid confusion with ImportCall 32131 (dynamic import) or import.meta */ 32132 if (js_parse_import(s)) 32133 return -1; 32134 } else { 32135 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 32136 return -1; 32137 } 32138 return 0; 32139 } 32140 32141 static JSFunctionDef *js_new_function_def(JSContext *ctx, 32142 JSFunctionDef *parent, 32143 BOOL is_eval, 32144 BOOL is_func_expr, 32145 const char *filename, 32146 const uint8_t *source_ptr, 32147 GetLineColCache *get_line_col_cache) 32148 { 32149 JSFunctionDef *fd; 32150 32151 fd = js_mallocz(ctx, sizeof(*fd)); 32152 if (!fd) 32153 return NULL; 32154 32155 fd->ctx = ctx; 32156 init_list_head(&fd->child_list); 32157 32158 /* insert in parent list */ 32159 fd->parent = parent; 32160 fd->parent_cpool_idx = -1; 32161 if (parent) { 32162 list_add_tail(&fd->link, &parent->child_list); 32163 fd->js_mode = parent->js_mode; 32164 fd->parent_scope_level = parent->scope_level; 32165 } 32166 fd->strip_debug = ((ctx->rt->strip_flags & JS_STRIP_DEBUG) != 0); 32167 fd->strip_source = ((ctx->rt->strip_flags & (JS_STRIP_DEBUG | JS_STRIP_SOURCE)) != 0); 32168 32169 fd->is_eval = is_eval; 32170 fd->is_func_expr = is_func_expr; 32171 js_dbuf_bytecode_init(ctx, &fd->byte_code); 32172 fd->last_opcode_pos = -1; 32173 fd->func_name = JS_ATOM_NULL; 32174 fd->var_object_idx = -1; 32175 fd->arg_var_object_idx = -1; 32176 fd->arguments_var_idx = -1; 32177 fd->arguments_arg_idx = -1; 32178 fd->func_var_idx = -1; 32179 fd->eval_ret_idx = -1; 32180 fd->this_var_idx = -1; 32181 fd->new_target_var_idx = -1; 32182 fd->this_active_func_var_idx = -1; 32183 fd->home_object_var_idx = -1; 32184 32185 /* XXX: should distinguish arg, var and var object and body scopes */ 32186 fd->scopes = fd->def_scope_array; 32187 fd->scope_size = countof(fd->def_scope_array); 32188 fd->scope_count = 1; 32189 fd->scopes[0].first = -1; 32190 fd->scopes[0].parent = -1; 32191 fd->scope_level = 0; /* 0: var/arg scope */ 32192 fd->scope_first = -1; 32193 fd->body_scope = -1; 32194 32195 fd->filename = JS_NewAtom(ctx, filename); 32196 fd->source_pos = source_ptr - get_line_col_cache->buf_start; 32197 fd->get_line_col_cache = get_line_col_cache; 32198 32199 js_dbuf_init(ctx, &fd->pc2line); 32200 //fd->pc2line_last_line_num = line_num; 32201 //fd->pc2line_last_pc = 0; 32202 fd->last_opcode_source_ptr = source_ptr; 32203 return fd; 32204 } 32205 32206 static void free_bytecode_atoms(JSRuntime *rt, 32207 const uint8_t *bc_buf, int bc_len, 32208 BOOL use_short_opcodes) 32209 { 32210 int pos, len, op; 32211 JSAtom atom; 32212 const JSOpCode *oi; 32213 32214 pos = 0; 32215 while (pos < bc_len) { 32216 op = bc_buf[pos]; 32217 if (use_short_opcodes) 32218 oi = &short_opcode_info(op); 32219 else 32220 oi = &opcode_info[op]; 32221 32222 len = oi->size; 32223 switch(oi->fmt) { 32224 case OP_FMT_atom: 32225 case OP_FMT_atom_u8: 32226 case OP_FMT_atom_u16: 32227 case OP_FMT_atom_label_u8: 32228 case OP_FMT_atom_label_u16: 32229 if ((pos + 1 + 4) > bc_len) 32230 break; /* may happen if there is not enough memory when emiting bytecode */ 32231 atom = get_u32(bc_buf + pos + 1); 32232 JS_FreeAtomRT(rt, atom); 32233 break; 32234 default: 32235 break; 32236 } 32237 pos += len; 32238 } 32239 } 32240 32241 static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) 32242 { 32243 int i; 32244 struct list_head *el, *el1; 32245 32246 /* free the child functions */ 32247 list_for_each_safe(el, el1, &fd->child_list) { 32248 JSFunctionDef *fd1; 32249 fd1 = list_entry(el, JSFunctionDef, link); 32250 js_free_function_def(ctx, fd1); 32251 } 32252 32253 free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size, 32254 fd->use_short_opcodes); 32255 dbuf_free(&fd->byte_code); 32256 js_free(ctx, fd->jump_slots); 32257 js_free(ctx, fd->label_slots); 32258 js_free(ctx, fd->line_number_slots); 32259 32260 for(i = 0; i < fd->cpool_count; i++) { 32261 JS_FreeValue(ctx, fd->cpool[i]); 32262 } 32263 js_free(ctx, fd->cpool); 32264 32265 JS_FreeAtom(ctx, fd->func_name); 32266 32267 for(i = 0; i < fd->var_count; i++) { 32268 JS_FreeAtom(ctx, fd->vars[i].var_name); 32269 } 32270 js_free(ctx, fd->vars); 32271 for(i = 0; i < fd->arg_count; i++) { 32272 JS_FreeAtom(ctx, fd->args[i].var_name); 32273 } 32274 js_free(ctx, fd->args); 32275 32276 for(i = 0; i < fd->global_var_count; i++) { 32277 JS_FreeAtom(ctx, fd->global_vars[i].var_name); 32278 } 32279 js_free(ctx, fd->global_vars); 32280 32281 for(i = 0; i < fd->closure_var_count; i++) { 32282 JSClosureVar *cv = &fd->closure_var[i]; 32283 JS_FreeAtom(ctx, cv->var_name); 32284 } 32285 js_free(ctx, fd->closure_var); 32286 32287 if (fd->scopes != fd->def_scope_array) 32288 js_free(ctx, fd->scopes); 32289 32290 JS_FreeAtom(ctx, fd->filename); 32291 dbuf_free(&fd->pc2line); 32292 32293 js_free(ctx, fd->source); 32294 32295 if (fd->parent) { 32296 /* remove in parent list */ 32297 list_del(&fd->link); 32298 } 32299 js_free(ctx, fd); 32300 } 32301 32302 #ifdef DUMP_BYTECODE 32303 static const char *skip_lines(const char *p, int n) { 32304 while (n-- > 0 && *p) { 32305 while (*p && *p++ != '\n') 32306 continue; 32307 } 32308 return p; 32309 } 32310 32311 static void print_lines(const char *source, int line, int line1) { 32312 const char *s = source; 32313 const char *p = skip_lines(s, line); 32314 if (*p) { 32315 while (line++ < line1) { 32316 p = skip_lines(s = p, 1); 32317 printf(";; %.*s", (int)(p - s), s); 32318 if (!*p) { 32319 if (p[-1] != '\n') 32320 printf("\n"); 32321 break; 32322 } 32323 } 32324 } 32325 } 32326 32327 static void dump_byte_code(JSContext *ctx, int pass, 32328 const uint8_t *tab, int len, 32329 const JSBytecodeVarDef *vardefs, 32330 const JSVarDef *args, int arg_count, 32331 const JSVarDef *vars, int var_count, 32332 const JSClosureVar *closure_var, int closure_var_count, 32333 const JSValue *cpool, uint32_t cpool_count, 32334 const char *source, 32335 const LabelSlot *label_slots, JSFunctionBytecode *b) 32336 { 32337 const JSOpCode *oi; 32338 int pos, pos_next, op, size, idx, addr, line, line1, in_source, line_num; 32339 uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits)); 32340 BOOL use_short_opcodes = (b != NULL), dump_pc; 32341 32342 if (b) { 32343 int col_num; 32344 line_num = find_line_num(ctx, b, -1, &col_num); 32345 } 32346 32347 /* scan for jump targets */ 32348 for (pos = 0; pos < len; pos = pos_next) { 32349 op = tab[pos]; 32350 if (use_short_opcodes) 32351 oi = &short_opcode_info(op); 32352 else 32353 oi = &opcode_info[op]; 32354 pos_next = pos + oi->size; 32355 if (op < OP_COUNT) { 32356 switch (oi->fmt) { 32357 #if SHORT_OPCODES 32358 case OP_FMT_label8: 32359 pos++; 32360 addr = (int8_t)tab[pos]; 32361 goto has_addr; 32362 case OP_FMT_label16: 32363 pos++; 32364 addr = (int16_t)get_u16(tab + pos); 32365 goto has_addr; 32366 #endif 32367 case OP_FMT_atom_label_u8: 32368 case OP_FMT_atom_label_u16: 32369 pos += 4; 32370 /* fall thru */ 32371 case OP_FMT_label: 32372 case OP_FMT_label_u16: 32373 pos++; 32374 addr = get_u32(tab + pos); 32375 goto has_addr; 32376 has_addr: 32377 if (pass == 1) 32378 addr = label_slots[addr].pos; 32379 if (pass == 2) 32380 addr = label_slots[addr].pos2; 32381 if (pass == 3) 32382 addr += pos; 32383 if (addr >= 0 && addr < len) 32384 bits[addr] |= 1; 32385 break; 32386 } 32387 } 32388 } 32389 in_source = 0; 32390 if (source) { 32391 /* Always print first line: needed if single line */ 32392 print_lines(source, 0, 1); 32393 in_source = 1; 32394 } 32395 line1 = line = 1; 32396 pos = 0; 32397 while (pos < len) { 32398 op = tab[pos]; 32399 if (source && b) { 32400 int col_num; 32401 if (b) { 32402 line1 = find_line_num(ctx, b, pos, &col_num) - line_num + 1; 32403 } else if (op == OP_line_num) { 32404 /* XXX: no longer works */ 32405 line1 = get_u32(tab + pos + 1) - line_num + 1; 32406 } 32407 if (line1 > line) { 32408 if (!in_source) 32409 printf("\n"); 32410 in_source = 1; 32411 print_lines(source, line, line1); 32412 line = line1; 32413 //bits[pos] |= 2; 32414 } 32415 } 32416 if (in_source) 32417 printf("\n"); 32418 in_source = 0; 32419 if (op >= OP_COUNT) { 32420 printf("invalid opcode (0x%02x)\n", op); 32421 pos++; 32422 continue; 32423 } 32424 if (use_short_opcodes) 32425 oi = &short_opcode_info(op); 32426 else 32427 oi = &opcode_info[op]; 32428 size = oi->size; 32429 if (pos + size > len) { 32430 printf("truncated opcode (0x%02x)\n", op); 32431 break; 32432 } 32433 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16) 32434 { 32435 int i, x, x0; 32436 x = x0 = printf("%5d ", pos); 32437 for (i = 0; i < size; i++) { 32438 if (i == 6) { 32439 printf("\n%*s", x = x0, ""); 32440 } 32441 x += printf(" %02X", tab[pos + i]); 32442 } 32443 printf("%*s", x0 + 20 - x, ""); 32444 } 32445 #endif 32446 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32) 32447 dump_pc = TRUE; 32448 #else 32449 dump_pc = bits[pos]; 32450 #endif 32451 if (dump_pc) { 32452 printf("%5d: ", pos); 32453 } else { 32454 printf(" "); 32455 } 32456 printf("%s", oi->name); 32457 pos++; 32458 switch(oi->fmt) { 32459 case OP_FMT_none_int: 32460 printf(" %d", op - OP_push_0); 32461 break; 32462 case OP_FMT_npopx: 32463 printf(" %d", op - OP_call0); 32464 break; 32465 case OP_FMT_u8: 32466 printf(" %u", get_u8(tab + pos)); 32467 break; 32468 case OP_FMT_i8: 32469 printf(" %d", get_i8(tab + pos)); 32470 break; 32471 case OP_FMT_u16: 32472 case OP_FMT_npop: 32473 printf(" %u", get_u16(tab + pos)); 32474 break; 32475 case OP_FMT_npop_u16: 32476 printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2)); 32477 break; 32478 case OP_FMT_i16: 32479 printf(" %d", get_i16(tab + pos)); 32480 break; 32481 case OP_FMT_i32: 32482 printf(" %d", get_i32(tab + pos)); 32483 break; 32484 case OP_FMT_u32: 32485 printf(" %u", get_u32(tab + pos)); 32486 break; 32487 #if SHORT_OPCODES 32488 case OP_FMT_label8: 32489 addr = get_i8(tab + pos); 32490 goto has_addr1; 32491 case OP_FMT_label16: 32492 addr = get_i16(tab + pos); 32493 goto has_addr1; 32494 #endif 32495 case OP_FMT_label: 32496 addr = get_u32(tab + pos); 32497 goto has_addr1; 32498 has_addr1: 32499 if (pass == 1) 32500 printf(" %u:%u", addr, label_slots[addr].pos); 32501 if (pass == 2) 32502 printf(" %u:%u", addr, label_slots[addr].pos2); 32503 if (pass == 3) 32504 printf(" %u", addr + pos); 32505 break; 32506 case OP_FMT_label_u16: 32507 addr = get_u32(tab + pos); 32508 if (pass == 1) 32509 printf(" %u:%u", addr, label_slots[addr].pos); 32510 if (pass == 2) 32511 printf(" %u:%u", addr, label_slots[addr].pos2); 32512 if (pass == 3) 32513 printf(" %u", addr + pos); 32514 printf(",%u", get_u16(tab + pos + 4)); 32515 break; 32516 #if SHORT_OPCODES 32517 case OP_FMT_const8: 32518 idx = get_u8(tab + pos); 32519 goto has_pool_idx; 32520 #endif 32521 case OP_FMT_const: 32522 idx = get_u32(tab + pos); 32523 goto has_pool_idx; 32524 has_pool_idx: 32525 printf(" %u: ", idx); 32526 if (idx < cpool_count) { 32527 JS_PrintValue(ctx, js_dump_value_write, stdout, cpool[idx], NULL); 32528 } 32529 break; 32530 case OP_FMT_atom: 32531 printf(" "); 32532 print_atom(ctx, get_u32(tab + pos)); 32533 break; 32534 case OP_FMT_atom_u8: 32535 printf(" "); 32536 print_atom(ctx, get_u32(tab + pos)); 32537 printf(",%d", get_u8(tab + pos + 4)); 32538 break; 32539 case OP_FMT_atom_u16: 32540 printf(" "); 32541 print_atom(ctx, get_u32(tab + pos)); 32542 printf(",%d", get_u16(tab + pos + 4)); 32543 break; 32544 case OP_FMT_atom_label_u8: 32545 case OP_FMT_atom_label_u16: 32546 printf(" "); 32547 print_atom(ctx, get_u32(tab + pos)); 32548 addr = get_u32(tab + pos + 4); 32549 if (pass == 1) 32550 printf(",%u:%u", addr, label_slots[addr].pos); 32551 if (pass == 2) 32552 printf(",%u:%u", addr, label_slots[addr].pos2); 32553 if (pass == 3) 32554 printf(",%u", addr + pos + 4); 32555 if (oi->fmt == OP_FMT_atom_label_u8) 32556 printf(",%u", get_u8(tab + pos + 8)); 32557 else 32558 printf(",%u", get_u16(tab + pos + 8)); 32559 break; 32560 case OP_FMT_none_loc: 32561 idx = (op - OP_get_loc0) % 4; 32562 goto has_loc; 32563 case OP_FMT_loc8: 32564 idx = get_u8(tab + pos); 32565 goto has_loc; 32566 case OP_FMT_loc: 32567 idx = get_u16(tab + pos); 32568 has_loc: 32569 printf(" %d: ", idx); 32570 if (idx < var_count) { 32571 print_atom(ctx, vars ? vars[idx].var_name : vardefs[arg_count + idx].var_name); 32572 } 32573 break; 32574 case OP_FMT_none_arg: 32575 idx = (op - OP_get_arg0) % 4; 32576 goto has_arg; 32577 case OP_FMT_arg: 32578 idx = get_u16(tab + pos); 32579 has_arg: 32580 printf(" %d: ", idx); 32581 if (idx < arg_count) { 32582 print_atom(ctx, args ? args[idx].var_name : vardefs[idx].var_name); 32583 } 32584 break; 32585 case OP_FMT_none_var_ref: 32586 idx = (op - OP_get_var_ref0) % 4; 32587 goto has_var_ref; 32588 case OP_FMT_var_ref: 32589 idx = get_u16(tab + pos); 32590 has_var_ref: 32591 printf(" %d: ", idx); 32592 if (idx < closure_var_count) { 32593 print_atom(ctx, closure_var[idx].var_name); 32594 } 32595 break; 32596 default: 32597 break; 32598 } 32599 printf("\n"); 32600 pos += oi->size - 1; 32601 } 32602 if (source) { 32603 if (!in_source) 32604 printf("\n"); 32605 print_lines(source, line, INT32_MAX); 32606 } 32607 js_free(ctx, bits); 32608 } 32609 32610 static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len) 32611 { 32612 const uint8_t *p_end, *p; 32613 int pc, v, line_num, col_num, ret; 32614 unsigned int op; 32615 uint32_t val; 32616 32617 if (len <= 0) 32618 return; 32619 32620 printf("%5s %5s %5s\n", "PC", "LINE", "COL"); 32621 32622 p = buf; 32623 p_end = buf + len; 32624 32625 /* get the function line and column numbers */ 32626 ret = get_leb128(&val, p, p_end); 32627 if (ret < 0) 32628 goto fail; 32629 p += ret; 32630 line_num = val + 1; 32631 32632 ret = get_leb128(&val, p, p_end); 32633 if (ret < 0) 32634 goto fail; 32635 p += ret; 32636 col_num = val + 1; 32637 32638 printf("%5s %5d %5d\n", "-", line_num, col_num); 32639 32640 pc = 0; 32641 while (p < p_end) { 32642 op = *p++; 32643 if (op == 0) { 32644 ret = get_leb128(&val, p, p_end); 32645 if (ret < 0) 32646 goto fail; 32647 pc += val; 32648 p += ret; 32649 ret = get_sleb128(&v, p, p_end); 32650 if (ret < 0) 32651 goto fail; 32652 p += ret; 32653 line_num += v; 32654 } else { 32655 op -= PC2LINE_OP_FIRST; 32656 pc += (op / PC2LINE_RANGE); 32657 line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE; 32658 } 32659 ret = get_sleb128(&v, p, p_end); 32660 if (ret < 0) 32661 goto fail; 32662 p += ret; 32663 col_num += v; 32664 32665 printf("%5d %5d %5d\n", pc, line_num, col_num); 32666 } 32667 fail: ; 32668 } 32669 32670 static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b) 32671 { 32672 int i; 32673 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 32674 const char *str; 32675 32676 if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { 32677 int line_num, col_num; 32678 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename); 32679 line_num = find_line_num(ctx, b, -1, &col_num); 32680 printf("%s:%d:%d: ", str, line_num, col_num); 32681 } 32682 32683 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name); 32684 printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str); 32685 if (b->js_mode) { 32686 printf(" mode:"); 32687 if (b->js_mode & JS_MODE_STRICT) 32688 printf(" strict"); 32689 printf("\n"); 32690 } 32691 if (b->arg_count && b->vardefs) { 32692 printf(" args:"); 32693 for(i = 0; i < b->arg_count; i++) { 32694 printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), 32695 b->vardefs[i].var_name)); 32696 } 32697 printf("\n"); 32698 } 32699 if (b->var_count && b->vardefs) { 32700 printf(" locals:\n"); 32701 for(i = 0; i < b->var_count; i++) { 32702 JSBytecodeVarDef *vd = &b->vardefs[b->arg_count + i]; 32703 printf("%5d: %s %s", i, 32704 vd->var_kind == JS_VAR_CATCH ? "catch" : 32705 (vd->var_kind == JS_VAR_FUNCTION_DECL || 32706 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" : 32707 vd->is_const ? "const" : 32708 vd->is_lexical ? "let" : "var", 32709 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name)); 32710 if (vd->has_scope) 32711 printf(" [next:%d]", vd->scope_next); 32712 printf("\n"); 32713 } 32714 } 32715 if (b->closure_var_count) { 32716 printf(" closure vars:\n"); 32717 for(i = 0; i < b->closure_var_count; i++) { 32718 JSClosureVar *cv = &b->closure_var[i]; 32719 printf("%5d: %s %s", i, 32720 cv->is_const ? "const" : 32721 cv->is_lexical ? "let" : "var", 32722 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name)); 32723 switch(cv->closure_type) { 32724 case JS_CLOSURE_LOCAL: 32725 printf(" [loc%d]\n", cv->var_idx); 32726 break; 32727 case JS_CLOSURE_ARG: 32728 printf(" [arg%d]\n", cv->var_idx); 32729 break; 32730 case JS_CLOSURE_REF: 32731 printf(" [ref%d]\n", cv->var_idx); 32732 break; 32733 case JS_CLOSURE_GLOBAL_REF: 32734 printf(" [global_ref%d]\n", cv->var_idx); 32735 break; 32736 case JS_CLOSURE_GLOBAL_DECL: 32737 printf(" [global_decl]\n"); 32738 break; 32739 case JS_CLOSURE_GLOBAL: 32740 printf(" [global]\n"); 32741 break; 32742 case JS_CLOSURE_MODULE_DECL: 32743 printf(" [module_decl]\n"); 32744 break; 32745 case JS_CLOSURE_MODULE_IMPORT: 32746 printf(" [module_import]\n"); 32747 break; 32748 default: 32749 printf(" [?]\n"); 32750 break; 32751 } 32752 } 32753 } 32754 printf(" stack_size: %d\n", b->stack_size); 32755 printf(" var_ref_count: %d\n", b->var_ref_count); 32756 printf(" opcodes:\n"); 32757 dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, 32758 b->vardefs, 32759 NULL, b->arg_count, 32760 NULL, b->var_count, 32761 b->closure_var, b->closure_var_count, 32762 b->cpool, b->cpool_count, 32763 b->has_debug ? b->debug.source : NULL, 32764 NULL, b); 32765 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32) 32766 if (b->has_debug) 32767 dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len); 32768 #endif 32769 printf("\n"); 32770 } 32771 #endif 32772 32773 static int add_closure_var(JSContext *ctx, JSFunctionDef *s, 32774 JSClosureTypeEnum closure_type, 32775 int var_idx, JSAtom var_name, 32776 BOOL is_const, BOOL is_lexical, 32777 JSVarKindEnum var_kind) 32778 { 32779 JSClosureVar *cv; 32780 32781 /* the closure variable indexes are currently stored on 16 bits */ 32782 if (s->closure_var_count >= JS_MAX_LOCAL_VARS) { 32783 JS_ThrowInternalError(ctx, "too many closure variables"); 32784 return -1; 32785 } 32786 32787 if (js_resize_array(ctx, (void **)&s->closure_var, 32788 sizeof(s->closure_var[0]), 32789 &s->closure_var_size, s->closure_var_count + 1)) 32790 return -1; 32791 cv = &s->closure_var[s->closure_var_count++]; 32792 cv->closure_type = closure_type; 32793 cv->is_const = is_const; 32794 cv->is_lexical = is_lexical; 32795 cv->var_kind = var_kind; 32796 cv->var_idx = var_idx; 32797 cv->var_name = JS_DupAtom(ctx, var_name); 32798 return s->closure_var_count - 1; 32799 } 32800 32801 static int find_closure_var(JSContext *ctx, JSFunctionDef *s, 32802 JSAtom var_name) 32803 { 32804 int i; 32805 for(i = 0; i < s->closure_var_count; i++) { 32806 JSClosureVar *cv = &s->closure_var[i]; 32807 if (cv->var_name == var_name) 32808 return i; 32809 } 32810 return -1; 32811 } 32812 32813 /* 'fd' must be a parent of 's'. Create in 's' a closure referencing 32814 another one in 'fd' */ 32815 static int get_closure_var(JSContext *ctx, JSFunctionDef *s, 32816 JSFunctionDef *fd, JSClosureTypeEnum closure_type, 32817 int var_idx, JSAtom var_name, 32818 BOOL is_const, BOOL is_lexical, 32819 JSVarKindEnum var_kind) 32820 { 32821 int i; 32822 32823 if (fd != s->parent) { 32824 var_idx = get_closure_var(ctx, s->parent, fd, closure_type, 32825 var_idx, var_name, 32826 is_const, is_lexical, var_kind); 32827 if (var_idx < 0) 32828 return -1; 32829 if (closure_type != JS_CLOSURE_GLOBAL_REF) 32830 closure_type = JS_CLOSURE_REF; 32831 } 32832 for(i = 0; i < s->closure_var_count; i++) { 32833 JSClosureVar *cv = &s->closure_var[i]; 32834 if (cv->var_idx == var_idx && cv->closure_type == closure_type) 32835 return i; 32836 } 32837 return add_closure_var(ctx, s, closure_type, var_idx, var_name, 32838 is_const, is_lexical, var_kind); 32839 } 32840 32841 static int get_with_scope_opcode(int op) 32842 { 32843 if (op == OP_scope_get_var_undef) 32844 return OP_with_get_var; 32845 else 32846 return OP_with_get_var + (op - OP_scope_get_var); 32847 } 32848 32849 static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) 32850 { 32851 int opcode = bc_buf[pos]; 32852 return (bc_buf[pos + 1] == OP_put_ref_value && 32853 (opcode == OP_insert3 || 32854 opcode == OP_perm4 || 32855 opcode == OP_nop || 32856 opcode == OP_rot3l)); 32857 } 32858 32859 static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) 32860 { 32861 int opcode = bc_buf[pos]; 32862 return (bc_buf[pos + 1] == OP_put_ref_value && 32863 (opcode == OP_insert3 || 32864 opcode == OP_perm4 || 32865 opcode == OP_nop || 32866 opcode == OP_rot3l)); 32867 } 32868 32869 static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s, 32870 DynBuf *bc, uint8_t *bc_buf, 32871 LabelSlot *ls, int pos_next, 32872 int get_op, int var_idx) 32873 { 32874 int label_pos, end_pos, pos; 32875 32876 /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)` 32877 but only if expr does not modify `a`. 32878 should scan the code between pos_next and label_pos 32879 for operations that can potentially change `a`: 32880 OP_scope_make_ref(a), function calls, jumps and gosub. 32881 */ 32882 /* replace the reference get/put with normal variable 32883 accesses */ 32884 if (bc_buf[pos_next] == OP_get_ref_value) { 32885 dbuf_putc(bc, get_op); 32886 dbuf_put_u16(bc, var_idx); 32887 pos_next++; 32888 } 32889 /* remove the OP_label to make room for replacement */ 32890 /* label should have a refcount of 0 anyway */ 32891 /* XXX: should avoid this patch by inserting nops in phase 1 */ 32892 label_pos = ls->pos; 32893 pos = label_pos - 5; 32894 assert(bc_buf[pos] == OP_label); 32895 /* label points to an instruction pair: 32896 - insert3 / put_ref_value 32897 - perm4 / put_ref_value 32898 - rot3l / put_ref_value 32899 - nop / put_ref_value 32900 */ 32901 end_pos = label_pos + 2; 32902 if (bc_buf[label_pos] == OP_insert3) 32903 bc_buf[pos++] = OP_dup; 32904 bc_buf[pos] = get_op + 1; 32905 put_u16(bc_buf + pos + 1, var_idx); 32906 pos += 3; 32907 /* pad with OP_nop */ 32908 while (pos < end_pos) 32909 bc_buf[pos++] = OP_nop; 32910 return pos_next; 32911 } 32912 32913 static int add_var_this(JSContext *ctx, JSFunctionDef *fd) 32914 { 32915 int idx; 32916 idx = add_var(ctx, fd, JS_ATOM_this); 32917 if (idx >= 0 && fd->is_derived_class_constructor) { 32918 JSVarDef *vd = &fd->vars[idx]; 32919 /* XXX: should have is_this flag or var type */ 32920 vd->is_lexical = 1; /* used to trigger 'uninitialized' checks 32921 in a derived class constructor */ 32922 } 32923 return idx; 32924 } 32925 32926 static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, 32927 JSAtom var_name) 32928 { 32929 int var_idx; 32930 32931 if (!s->has_this_binding) 32932 return -1; 32933 switch(var_name) { 32934 case JS_ATOM_home_object: 32935 /* 'home_object' pseudo variable */ 32936 if (s->home_object_var_idx < 0) 32937 s->home_object_var_idx = add_var(ctx, s, var_name); 32938 var_idx = s->home_object_var_idx; 32939 break; 32940 case JS_ATOM_this_active_func: 32941 /* 'this.active_func' pseudo variable */ 32942 if (s->this_active_func_var_idx < 0) 32943 s->this_active_func_var_idx = add_var(ctx, s, var_name); 32944 var_idx = s->this_active_func_var_idx; 32945 break; 32946 case JS_ATOM_new_target: 32947 /* 'new.target' pseudo variable */ 32948 if (s->new_target_var_idx < 0) 32949 s->new_target_var_idx = add_var(ctx, s, var_name); 32950 var_idx = s->new_target_var_idx; 32951 break; 32952 case JS_ATOM_this: 32953 /* 'this' pseudo variable */ 32954 if (s->this_var_idx < 0) 32955 s->this_var_idx = add_var_this(ctx, s); 32956 var_idx = s->this_var_idx; 32957 break; 32958 default: 32959 var_idx = -1; 32960 break; 32961 } 32962 return var_idx; 32963 } 32964 32965 /* test if 'var_name' is in the variable object on the stack. If is it 32966 the case, handle it and jump to 'label_done' */ 32967 static void var_object_test(JSContext *ctx, JSFunctionDef *s, 32968 JSAtom var_name, int op, DynBuf *bc, 32969 int *plabel_done, BOOL is_with) 32970 { 32971 dbuf_putc(bc, get_with_scope_opcode(op)); 32972 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 32973 if (*plabel_done < 0) { 32974 *plabel_done = new_label_fd(s); 32975 if (*plabel_done < 0) { 32976 dbuf_set_error(bc); 32977 return; 32978 } 32979 } 32980 dbuf_put_u32(bc, *plabel_done); 32981 dbuf_putc(bc, is_with); 32982 update_label(s, *plabel_done, 1); 32983 s->jump_size++; 32984 } 32985 32986 static inline void capture_var(JSFunctionDef *s, JSVarDef *vd) 32987 { 32988 if (!vd->is_captured) { 32989 vd->is_captured = 1; 32990 vd->var_ref_idx = s->var_ref_count++; 32991 } 32992 } 32993 32994 /* return the position of the next opcode or -1 if error */ 32995 static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 32996 JSAtom var_name, int scope_level, int op, 32997 DynBuf *bc, uint8_t *bc_buf, 32998 LabelSlot *ls, int pos_next) 32999 { 33000 int idx, var_idx, is_put; 33001 int label_done; 33002 JSFunctionDef *fd; 33003 JSVarDef *vd; 33004 BOOL is_pseudo_var, is_arg_scope; 33005 33006 label_done = -1; 33007 33008 /* XXX: could be simpler to use a specific function to 33009 resolve the pseudo variables */ 33010 is_pseudo_var = (var_name == JS_ATOM_home_object || 33011 var_name == JS_ATOM_this_active_func || 33012 var_name == JS_ATOM_new_target || 33013 var_name == JS_ATOM_this); 33014 33015 /* resolve local scoped variables */ 33016 var_idx = -1; 33017 for (idx = s->scopes[scope_level].first; idx >= 0;) { 33018 vd = &s->vars[idx]; 33019 if (vd->var_name == var_name) { 33020 if (op == OP_scope_put_var || op == OP_scope_make_ref) { 33021 if (vd->is_const) { 33022 dbuf_putc(bc, OP_throw_error); 33023 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33024 dbuf_putc(bc, JS_THROW_VAR_RO); 33025 goto done; 33026 } 33027 } 33028 var_idx = idx; 33029 break; 33030 } else 33031 if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { 33032 dbuf_putc(bc, OP_get_loc); 33033 dbuf_put_u16(bc, idx); 33034 var_object_test(ctx, s, var_name, op, bc, &label_done, 1); 33035 } 33036 idx = vd->scope_next; 33037 } 33038 is_arg_scope = (idx == ARG_SCOPE_END); 33039 if (var_idx < 0) { 33040 /* argument scope: variables are not visible but pseudo 33041 variables are visible */ 33042 if (!is_arg_scope) { 33043 var_idx = find_var(ctx, s, var_name); 33044 } 33045 33046 if (var_idx < 0 && is_pseudo_var) 33047 var_idx = resolve_pseudo_var(ctx, s, var_name); 33048 33049 if (var_idx < 0 && var_name == JS_ATOM_arguments && 33050 s->has_arguments_binding) { 33051 /* 'arguments' pseudo variable */ 33052 var_idx = add_arguments_var(ctx, s); 33053 } 33054 if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { 33055 /* add a new variable with the function name */ 33056 var_idx = add_func_var(ctx, s, var_name); 33057 } 33058 } 33059 if (var_idx >= 0) { 33060 if ((op == OP_scope_put_var || op == OP_scope_make_ref) && 33061 !(var_idx & ARGUMENT_VAR_OFFSET) && 33062 s->vars[var_idx].is_const) { 33063 /* only happens when assigning a function expression name 33064 in strict mode */ 33065 dbuf_putc(bc, OP_throw_error); 33066 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33067 dbuf_putc(bc, JS_THROW_VAR_RO); 33068 goto done; 33069 } 33070 /* OP_scope_put_var_init is only used to initialize a 33071 lexical variable, so it is never used in a with or var object. It 33072 can be used with a closure (module global variable case). */ 33073 switch (op) { 33074 case OP_scope_make_ref: 33075 if (!(var_idx & ARGUMENT_VAR_OFFSET) && 33076 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { 33077 /* Create a dummy object reference for the func_var */ 33078 dbuf_putc(bc, OP_object); 33079 dbuf_putc(bc, OP_get_loc); 33080 dbuf_put_u16(bc, var_idx); 33081 dbuf_putc(bc, OP_define_field); 33082 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33083 dbuf_putc(bc, OP_push_atom_value); 33084 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33085 } else 33086 if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) { 33087 int get_op; 33088 if (var_idx & ARGUMENT_VAR_OFFSET) { 33089 get_op = OP_get_arg; 33090 var_idx -= ARGUMENT_VAR_OFFSET; 33091 } else { 33092 if (s->vars[var_idx].is_lexical) 33093 get_op = OP_get_loc_check; 33094 else 33095 get_op = OP_get_loc; 33096 } 33097 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, 33098 pos_next, get_op, var_idx); 33099 } else { 33100 /* Create a dummy object with a named slot that is 33101 a reference to the local variable */ 33102 if (var_idx & ARGUMENT_VAR_OFFSET) { 33103 capture_var(s, &s->args[var_idx - ARGUMENT_VAR_OFFSET]); 33104 dbuf_putc(bc, OP_make_arg_ref); 33105 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33106 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); 33107 } else { 33108 capture_var(s, &s->vars[var_idx]); 33109 dbuf_putc(bc, OP_make_loc_ref); 33110 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33111 dbuf_put_u16(bc, var_idx); 33112 } 33113 } 33114 break; 33115 case OP_scope_put_var: 33116 if (!(var_idx & ARGUMENT_VAR_OFFSET) && 33117 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { 33118 /* in non strict mode, modifying the function name is ignored */ 33119 dbuf_putc(bc, OP_drop); 33120 goto done; 33121 } 33122 goto local_scope_var; 33123 case OP_scope_get_ref: 33124 dbuf_putc(bc, OP_undefined); 33125 goto local_scope_var; 33126 case OP_scope_get_var_checkthis: 33127 case OP_scope_get_var_undef: 33128 case OP_scope_get_var: 33129 case OP_scope_put_var_init: 33130 local_scope_var: 33131 is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init); 33132 if (var_idx & ARGUMENT_VAR_OFFSET) { 33133 dbuf_putc(bc, OP_get_arg + is_put); 33134 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); 33135 } else { 33136 if (is_put) { 33137 if (s->vars[var_idx].is_lexical) { 33138 if (op == OP_scope_put_var_init) { 33139 /* 'this' can only be initialized once */ 33140 if (var_name == JS_ATOM_this) 33141 dbuf_putc(bc, OP_put_loc_check_init); 33142 else 33143 dbuf_putc(bc, OP_put_loc); 33144 } else { 33145 dbuf_putc(bc, OP_put_loc_check); 33146 } 33147 } else { 33148 dbuf_putc(bc, OP_put_loc); 33149 } 33150 } else { 33151 if (s->vars[var_idx].is_lexical) { 33152 if (op == OP_scope_get_var_checkthis) { 33153 /* only used for 'this' return in derived class constructors */ 33154 dbuf_putc(bc, OP_get_loc_checkthis); 33155 } else { 33156 dbuf_putc(bc, OP_get_loc_check); 33157 } 33158 } else { 33159 dbuf_putc(bc, OP_get_loc); 33160 } 33161 } 33162 dbuf_put_u16(bc, var_idx); 33163 } 33164 break; 33165 case OP_scope_delete_var: 33166 dbuf_putc(bc, OP_push_false); 33167 break; 33168 } 33169 goto done; 33170 } 33171 /* check eval object */ 33172 if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { 33173 dbuf_putc(bc, OP_get_loc); 33174 dbuf_put_u16(bc, s->var_object_idx); 33175 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 33176 } 33177 /* check eval object in argument scope */ 33178 if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { 33179 dbuf_putc(bc, OP_get_loc); 33180 dbuf_put_u16(bc, s->arg_var_object_idx); 33181 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 33182 } 33183 33184 /* check parent scopes */ 33185 for (fd = s; fd->parent;) { 33186 scope_level = fd->parent_scope_level; 33187 fd = fd->parent; 33188 for (idx = fd->scopes[scope_level].first; idx >= 0;) { 33189 vd = &fd->vars[idx]; 33190 if (vd->var_name == var_name) { 33191 if (op == OP_scope_put_var || op == OP_scope_make_ref) { 33192 if (vd->is_const) { 33193 dbuf_putc(bc, OP_throw_error); 33194 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33195 dbuf_putc(bc, JS_THROW_VAR_RO); 33196 goto done; 33197 } 33198 } 33199 var_idx = idx; 33200 break; 33201 } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { 33202 capture_var(fd, vd); 33203 idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); 33204 if (idx >= 0) { 33205 dbuf_putc(bc, OP_get_var_ref); 33206 dbuf_put_u16(bc, idx); 33207 var_object_test(ctx, s, var_name, op, bc, &label_done, 1); 33208 } 33209 } 33210 idx = vd->scope_next; 33211 } 33212 is_arg_scope = (idx == ARG_SCOPE_END); 33213 if (var_idx >= 0) 33214 break; 33215 33216 if (!is_arg_scope) { 33217 var_idx = find_var(ctx, fd, var_name); 33218 if (var_idx >= 0) 33219 break; 33220 } 33221 if (is_pseudo_var) { 33222 var_idx = resolve_pseudo_var(ctx, fd, var_name); 33223 if (var_idx >= 0) 33224 break; 33225 } 33226 if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { 33227 var_idx = add_arguments_var(ctx, fd); 33228 break; 33229 } 33230 if (fd->is_func_expr && fd->func_name == var_name) { 33231 /* add a new variable with the function name */ 33232 var_idx = add_func_var(ctx, fd, var_name); 33233 break; 33234 } 33235 33236 /* check eval object */ 33237 if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { 33238 vd = &fd->vars[fd->var_object_idx]; 33239 capture_var(fd, vd); 33240 idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, 33241 fd->var_object_idx, vd->var_name, 33242 FALSE, FALSE, JS_VAR_NORMAL); 33243 dbuf_putc(bc, OP_get_var_ref); 33244 dbuf_put_u16(bc, idx); 33245 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 33246 } 33247 33248 /* check eval object in argument scope */ 33249 if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { 33250 vd = &fd->vars[fd->arg_var_object_idx]; 33251 capture_var(fd, vd); 33252 idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, 33253 fd->arg_var_object_idx, vd->var_name, 33254 FALSE, FALSE, JS_VAR_NORMAL); 33255 dbuf_putc(bc, OP_get_var_ref); 33256 dbuf_put_u16(bc, idx); 33257 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 33258 } 33259 33260 if (fd->is_eval) 33261 break; /* it it necessarily the top level function */ 33262 } 33263 33264 /* check direct eval scope (in the closure of the eval function 33265 which is necessarily at the top level) */ 33266 if (!fd) 33267 fd = s; 33268 if (var_idx < 0 && fd->is_eval) { 33269 int idx1; 33270 for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) { 33271 JSClosureVar *cv = &fd->closure_var[idx1]; 33272 if (var_name == cv->var_name) { 33273 if (fd != s) { 33274 JSClosureTypeEnum closure_type; 33275 if (cv->closure_type == JS_CLOSURE_GLOBAL || 33276 cv->closure_type == JS_CLOSURE_GLOBAL_DECL || 33277 cv->closure_type == JS_CLOSURE_GLOBAL_REF) 33278 closure_type = JS_CLOSURE_GLOBAL_REF; 33279 else 33280 closure_type = JS_CLOSURE_REF; 33281 idx = get_closure_var(ctx, s, fd, 33282 closure_type, 33283 idx1, 33284 cv->var_name, cv->is_const, 33285 cv->is_lexical, cv->var_kind); 33286 } else { 33287 idx = idx1; 33288 } 33289 if (cv->closure_type == JS_CLOSURE_GLOBAL || 33290 cv->closure_type == JS_CLOSURE_GLOBAL_DECL || 33291 cv->closure_type == JS_CLOSURE_GLOBAL_REF) 33292 goto has_global_idx; 33293 else 33294 goto has_idx; 33295 } else if ((cv->var_name == JS_ATOM__var_ || 33296 cv->var_name == JS_ATOM__arg_var_ || 33297 cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { 33298 int is_with = (cv->var_name == JS_ATOM__with_); 33299 if (fd != s) { 33300 idx = get_closure_var(ctx, s, fd, 33301 JS_CLOSURE_REF, 33302 idx1, 33303 cv->var_name, FALSE, FALSE, 33304 JS_VAR_NORMAL); 33305 } else { 33306 idx = idx1; 33307 } 33308 dbuf_putc(bc, OP_get_var_ref); 33309 dbuf_put_u16(bc, idx); 33310 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); 33311 } 33312 } 33313 33314 /* not found: add a closure for a global variable access */ 33315 idx1 = add_closure_var(ctx, fd, JS_CLOSURE_GLOBAL, 0, var_name, 33316 FALSE, FALSE, JS_VAR_NORMAL); 33317 if (idx1 < 0) 33318 return -1; 33319 if (fd != s) { 33320 idx = get_closure_var(ctx, s, fd, 33321 JS_CLOSURE_GLOBAL_REF, 33322 idx1, 33323 var_name, FALSE, FALSE, 33324 JS_VAR_NORMAL); 33325 } else { 33326 idx = idx1; 33327 } 33328 has_global_idx: 33329 /* global variable access */ 33330 switch (op) { 33331 case OP_scope_make_ref: 33332 if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) { 33333 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, 33334 pos_next, 33335 OP_get_var, idx); 33336 } else { 33337 dbuf_putc(bc, OP_make_var_ref); 33338 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33339 } 33340 break; 33341 case OP_scope_get_ref: 33342 /* XXX: should create a dummy object with a named slot that is 33343 a reference to the global variable */ 33344 dbuf_putc(bc, OP_undefined); 33345 dbuf_putc(bc, OP_get_var); 33346 dbuf_put_u16(bc, idx); 33347 break; 33348 case OP_scope_get_var_undef: 33349 case OP_scope_get_var: 33350 case OP_scope_put_var: 33351 dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef)); 33352 dbuf_put_u16(bc, idx); 33353 break; 33354 case OP_scope_put_var_init: 33355 dbuf_putc(bc, OP_put_var_init); 33356 dbuf_put_u16(bc, idx); 33357 break; 33358 case OP_scope_delete_var: 33359 dbuf_putc(bc, OP_delete_var); 33360 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33361 break; 33362 } 33363 } else { 33364 /* find the corresponding closure variable */ 33365 if (var_idx & ARGUMENT_VAR_OFFSET) { 33366 capture_var(fd, &fd->args[var_idx - ARGUMENT_VAR_OFFSET]); 33367 idx = get_closure_var(ctx, s, fd, 33368 JS_CLOSURE_ARG, var_idx - ARGUMENT_VAR_OFFSET, 33369 var_name, FALSE, FALSE, JS_VAR_NORMAL); 33370 } else { 33371 capture_var(fd, &fd->vars[var_idx]); 33372 idx = get_closure_var(ctx, s, fd, 33373 JS_CLOSURE_LOCAL, var_idx, 33374 var_name, 33375 fd->vars[var_idx].is_const, 33376 fd->vars[var_idx].is_lexical, 33377 fd->vars[var_idx].var_kind); 33378 } 33379 if (idx >= 0) { 33380 has_idx: 33381 if ((op == OP_scope_put_var || op == OP_scope_make_ref) && 33382 s->closure_var[idx].is_const) { 33383 dbuf_putc(bc, OP_throw_error); 33384 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33385 dbuf_putc(bc, JS_THROW_VAR_RO); 33386 goto done; 33387 } 33388 switch (op) { 33389 case OP_scope_make_ref: 33390 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { 33391 /* Create a dummy object reference for the func_var */ 33392 dbuf_putc(bc, OP_object); 33393 dbuf_putc(bc, OP_get_var_ref); 33394 dbuf_put_u16(bc, idx); 33395 dbuf_putc(bc, OP_define_field); 33396 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33397 dbuf_putc(bc, OP_push_atom_value); 33398 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33399 } else 33400 if (label_done == -1 && 33401 can_opt_put_ref_value(bc_buf, ls->pos)) { 33402 int get_op; 33403 if (s->closure_var[idx].is_lexical) 33404 get_op = OP_get_var_ref_check; 33405 else 33406 get_op = OP_get_var_ref; 33407 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, 33408 pos_next, 33409 get_op, idx); 33410 } else { 33411 /* Create a dummy object with a named slot that is 33412 a reference to the closure variable */ 33413 dbuf_putc(bc, OP_make_var_ref_ref); 33414 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33415 dbuf_put_u16(bc, idx); 33416 } 33417 break; 33418 case OP_scope_put_var: 33419 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { 33420 /* in non strict mode, modifying the function name is ignored */ 33421 dbuf_putc(bc, OP_drop); 33422 goto done; 33423 } 33424 goto closure_scope_var; 33425 case OP_scope_get_ref: 33426 /* XXX: should create a dummy object with a named slot that is 33427 a reference to the closure variable */ 33428 dbuf_putc(bc, OP_undefined); 33429 goto closure_scope_var; 33430 case OP_scope_get_var_undef: 33431 case OP_scope_get_var: 33432 case OP_scope_put_var_init: 33433 closure_scope_var: 33434 is_put = (op == OP_scope_put_var || 33435 op == OP_scope_put_var_init); 33436 if (is_put) { 33437 if (s->closure_var[idx].is_lexical) { 33438 if (op == OP_scope_put_var_init) { 33439 /* 'this' can only be initialized once */ 33440 if (var_name == JS_ATOM_this) 33441 dbuf_putc(bc, OP_put_var_ref_check_init); 33442 else 33443 dbuf_putc(bc, OP_put_var_ref); 33444 } else { 33445 dbuf_putc(bc, OP_put_var_ref_check); 33446 } 33447 } else { 33448 dbuf_putc(bc, OP_put_var_ref); 33449 } 33450 } else { 33451 if (s->closure_var[idx].is_lexical) { 33452 dbuf_putc(bc, OP_get_var_ref_check); 33453 } else { 33454 dbuf_putc(bc, OP_get_var_ref); 33455 } 33456 } 33457 dbuf_put_u16(bc, idx); 33458 break; 33459 case OP_scope_delete_var: 33460 dbuf_putc(bc, OP_push_false); 33461 break; 33462 } 33463 goto done; 33464 } 33465 } 33466 33467 done: 33468 if (label_done >= 0) { 33469 dbuf_putc(bc, OP_label); 33470 dbuf_put_u32(bc, label_done); 33471 s->label_slots[label_done].pos2 = bc->size; 33472 } 33473 return pos_next; 33474 } 33475 33476 /* search in all scopes */ 33477 static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd, 33478 JSAtom name, int scope_level) 33479 { 33480 int idx; 33481 33482 idx = fd->scopes[scope_level].first; 33483 while (idx >= 0) { 33484 if (fd->vars[idx].var_name == name) 33485 return idx; 33486 idx = fd->vars[idx].scope_next; 33487 } 33488 return -1; 33489 } 33490 33491 static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) 33492 { 33493 /* if the field is not initialized, the error is catched when 33494 accessing it */ 33495 if (is_ref) 33496 dbuf_putc(bc, OP_get_var_ref); 33497 else 33498 dbuf_putc(bc, OP_get_loc); 33499 dbuf_put_u16(bc, idx); 33500 } 33501 33502 static int resolve_scope_private_field1(JSContext *ctx, 33503 BOOL *pis_ref, int *pvar_kind, 33504 JSFunctionDef *s, 33505 JSAtom var_name, int scope_level) 33506 { 33507 int idx, var_kind; 33508 JSFunctionDef *fd; 33509 BOOL is_ref; 33510 33511 fd = s; 33512 is_ref = FALSE; 33513 for(;;) { 33514 idx = find_private_class_field_all(ctx, fd, var_name, scope_level); 33515 if (idx >= 0) { 33516 var_kind = fd->vars[idx].var_kind; 33517 if (is_ref) { 33518 capture_var(fd, &fd->vars[idx]); 33519 idx = get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, idx, var_name, 33520 TRUE, TRUE, JS_VAR_NORMAL); 33521 if (idx < 0) 33522 return -1; 33523 } 33524 break; 33525 } 33526 scope_level = fd->parent_scope_level; 33527 if (!fd->parent) { 33528 if (fd->is_eval) { 33529 /* closure of the eval function (top level) */ 33530 for (idx = 0; idx < fd->closure_var_count; idx++) { 33531 JSClosureVar *cv = &fd->closure_var[idx]; 33532 if (cv->var_name == var_name) { 33533 var_kind = cv->var_kind; 33534 is_ref = TRUE; 33535 if (fd != s) { 33536 idx = get_closure_var(ctx, s, fd, 33537 JS_CLOSURE_REF, 33538 idx, 33539 cv->var_name, cv->is_const, 33540 cv->is_lexical, 33541 cv->var_kind); 33542 if (idx < 0) 33543 return -1; 33544 } 33545 goto done; 33546 } 33547 } 33548 } 33549 /* XXX: no line number info */ 33550 JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", 33551 var_name); 33552 return -1; 33553 } else { 33554 fd = fd->parent; 33555 } 33556 is_ref = TRUE; 33557 } 33558 done: 33559 *pis_ref = is_ref; 33560 *pvar_kind = var_kind; 33561 return idx; 33562 } 33563 33564 /* return 0 if OK or -1 if the private field could not be resolved */ 33565 static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, 33566 JSAtom var_name, int scope_level, int op, 33567 DynBuf *bc) 33568 { 33569 int idx, var_kind; 33570 BOOL is_ref; 33571 33572 idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s, 33573 var_name, scope_level); 33574 if (idx < 0) 33575 return -1; 33576 assert(var_kind != JS_VAR_NORMAL); 33577 switch (op) { 33578 case OP_scope_get_private_field: 33579 case OP_scope_get_private_field2: 33580 switch(var_kind) { 33581 case JS_VAR_PRIVATE_FIELD: 33582 if (op == OP_scope_get_private_field2) 33583 dbuf_putc(bc, OP_dup); 33584 get_loc_or_ref(bc, is_ref, idx); 33585 dbuf_putc(bc, OP_get_private_field); 33586 break; 33587 case JS_VAR_PRIVATE_METHOD: 33588 get_loc_or_ref(bc, is_ref, idx); 33589 dbuf_putc(bc, OP_check_brand); 33590 if (op != OP_scope_get_private_field2) 33591 dbuf_putc(bc, OP_nip); 33592 break; 33593 case JS_VAR_PRIVATE_GETTER: 33594 case JS_VAR_PRIVATE_GETTER_SETTER: 33595 if (op == OP_scope_get_private_field2) 33596 dbuf_putc(bc, OP_dup); 33597 get_loc_or_ref(bc, is_ref, idx); 33598 dbuf_putc(bc, OP_check_brand); 33599 dbuf_putc(bc, OP_call_method); 33600 dbuf_put_u16(bc, 0); 33601 break; 33602 case JS_VAR_PRIVATE_SETTER: 33603 /* XXX: add clearer error message */ 33604 dbuf_putc(bc, OP_throw_error); 33605 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33606 dbuf_putc(bc, JS_THROW_VAR_RO); 33607 break; 33608 default: 33609 abort(); 33610 } 33611 break; 33612 case OP_scope_put_private_field: 33613 switch(var_kind) { 33614 case JS_VAR_PRIVATE_FIELD: 33615 get_loc_or_ref(bc, is_ref, idx); 33616 dbuf_putc(bc, OP_put_private_field); 33617 break; 33618 case JS_VAR_PRIVATE_METHOD: 33619 case JS_VAR_PRIVATE_GETTER: 33620 /* XXX: add clearer error message */ 33621 dbuf_putc(bc, OP_throw_error); 33622 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 33623 dbuf_putc(bc, JS_THROW_VAR_RO); 33624 break; 33625 case JS_VAR_PRIVATE_SETTER: 33626 case JS_VAR_PRIVATE_GETTER_SETTER: 33627 { 33628 JSAtom setter_name = get_private_setter_name(ctx, var_name); 33629 if (setter_name == JS_ATOM_NULL) 33630 return -1; 33631 idx = resolve_scope_private_field1(ctx, &is_ref, 33632 &var_kind, s, 33633 setter_name, scope_level); 33634 JS_FreeAtom(ctx, setter_name); 33635 if (idx < 0) 33636 return -1; 33637 assert(var_kind == JS_VAR_PRIVATE_SETTER); 33638 get_loc_or_ref(bc, is_ref, idx); 33639 dbuf_putc(bc, OP_swap); 33640 /* obj func value */ 33641 dbuf_putc(bc, OP_rot3r); 33642 /* value obj func */ 33643 dbuf_putc(bc, OP_check_brand); 33644 dbuf_putc(bc, OP_rot3l); 33645 /* obj func value */ 33646 dbuf_putc(bc, OP_call_method); 33647 dbuf_put_u16(bc, 1); 33648 dbuf_putc(bc, OP_drop); 33649 } 33650 break; 33651 default: 33652 abort(); 33653 } 33654 break; 33655 case OP_scope_in_private_field: 33656 get_loc_or_ref(bc, is_ref, idx); 33657 dbuf_putc(bc, OP_private_in); 33658 break; 33659 default: 33660 abort(); 33661 } 33662 return 0; 33663 } 33664 33665 static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, 33666 int scope_level) 33667 { 33668 int idx; 33669 JSVarDef *vd; 33670 33671 for (idx = s->scopes[scope_level].first; idx >= 0;) { 33672 vd = &s->vars[idx]; 33673 capture_var(s, vd); 33674 idx = vd->scope_next; 33675 } 33676 } 33677 33678 /* XXX: should handle the argument scope generically */ 33679 static BOOL is_var_in_arg_scope(JSAtom var_name, JSVarKindEnum var_kind) 33680 { 33681 return (var_name == JS_ATOM_home_object || 33682 var_name == JS_ATOM_this_active_func || 33683 var_name == JS_ATOM_new_target || 33684 var_name == JS_ATOM_this || 33685 var_name == JS_ATOM__arg_var_ || 33686 var_kind == JS_VAR_FUNCTION_NAME); 33687 } 33688 33689 static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 33690 { 33691 JSFunctionDef *fd; 33692 JSVarDef *vd; 33693 int i, scope_level, scope_idx; 33694 BOOL has_arguments_binding, has_this_binding, is_arg_scope; 33695 33696 /* in non strict mode, variables are created in the caller's 33697 environment object */ 33698 if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) { 33699 s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); 33700 if (s->has_parameter_expressions) { 33701 /* an additional variable object is needed for the 33702 argument scope */ 33703 s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); 33704 } 33705 } 33706 33707 /* eval can potentially use 'arguments' so we must define it */ 33708 has_this_binding = s->has_this_binding; 33709 if (has_this_binding) { 33710 if (s->this_var_idx < 0) 33711 s->this_var_idx = add_var_this(ctx, s); 33712 if (s->new_target_var_idx < 0) 33713 s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target); 33714 if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0) 33715 s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func); 33716 if (s->has_home_object && s->home_object_var_idx < 0) 33717 s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); 33718 } 33719 has_arguments_binding = s->has_arguments_binding; 33720 if (has_arguments_binding) { 33721 add_arguments_var(ctx, s); 33722 /* also add an arguments binding in the argument scope to 33723 raise an error if a direct eval in the argument scope tries 33724 to redefine it */ 33725 if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT)) 33726 add_arguments_arg(ctx, s); 33727 } 33728 if (s->is_func_expr && s->func_name != JS_ATOM_NULL) 33729 add_func_var(ctx, s, s->func_name); 33730 33731 for(i = 0; i < s->arg_count; i++) { 33732 vd = &s->args[i]; 33733 capture_var(s, vd); 33734 } 33735 for(i = 0; i < s->var_count; i++) { 33736 vd = &s->vars[i]; 33737 /* do not close top level last result */ 33738 if (vd->scope_level == 0 && 33739 vd->var_name != JS_ATOM__ret_ && 33740 vd->var_name != JS_ATOM_NULL) { 33741 capture_var(s, vd); 33742 } 33743 } 33744 33745 /* eval can use all the variables of the enclosing functions, so 33746 they must be all put in the closure. The closure variables are 33747 ordered by scope. It works only because no closure are created 33748 before. */ 33749 assert(s->is_eval || s->closure_var_count == 0); 33750 33751 /* XXX: inefficient, but eval performance is less critical */ 33752 fd = s; 33753 for(;;) { 33754 scope_level = fd->parent_scope_level; 33755 fd = fd->parent; 33756 if (!fd) 33757 break; 33758 /* add 'this' if it was not previously added */ 33759 if (!has_this_binding && fd->has_this_binding) { 33760 if (fd->this_var_idx < 0) 33761 fd->this_var_idx = add_var_this(ctx, fd); 33762 if (fd->new_target_var_idx < 0) 33763 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target); 33764 if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0) 33765 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func); 33766 if (fd->has_home_object && fd->home_object_var_idx < 0) 33767 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object); 33768 has_this_binding = TRUE; 33769 } 33770 /* add 'arguments' if it was not previously added */ 33771 if (!has_arguments_binding && fd->has_arguments_binding) { 33772 add_arguments_var(ctx, fd); 33773 has_arguments_binding = TRUE; 33774 } 33775 /* add function name */ 33776 if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL) 33777 add_func_var(ctx, fd, fd->func_name); 33778 33779 /* add lexical variables */ 33780 scope_idx = fd->scopes[scope_level].first; 33781 while (scope_idx >= 0) { 33782 vd = &fd->vars[scope_idx]; 33783 capture_var(fd, vd); 33784 get_closure_var(ctx, s, fd, JS_CLOSURE_LOCAL, scope_idx, 33785 vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); 33786 scope_idx = vd->scope_next; 33787 } 33788 is_arg_scope = (scope_idx == ARG_SCOPE_END); 33789 if (!is_arg_scope) { 33790 /* add unscoped variables */ 33791 /* XXX: propagate is_const and var_kind too ? */ 33792 for(i = 0; i < fd->arg_count; i++) { 33793 vd = &fd->args[i]; 33794 if (vd->var_name != JS_ATOM_NULL) { 33795 capture_var(fd, vd); 33796 get_closure_var(ctx, s, fd, 33797 JS_CLOSURE_ARG, i, vd->var_name, FALSE, 33798 vd->is_lexical, JS_VAR_NORMAL); 33799 } 33800 } 33801 for(i = 0; i < fd->var_count; i++) { 33802 vd = &fd->vars[i]; 33803 /* do not close top level last result */ 33804 if (vd->scope_level == 0 && 33805 vd->var_name != JS_ATOM__ret_ && 33806 vd->var_name != JS_ATOM_NULL) { 33807 capture_var(fd, vd); 33808 get_closure_var(ctx, s, fd, 33809 JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, 33810 vd->is_lexical, JS_VAR_NORMAL); 33811 } 33812 } 33813 } else { 33814 for(i = 0; i < fd->var_count; i++) { 33815 vd = &fd->vars[i]; 33816 /* do not close top level last result */ 33817 if (vd->scope_level == 0 && is_var_in_arg_scope(vd->var_name, vd->var_kind)) { 33818 capture_var(fd, vd); 33819 get_closure_var(ctx, s, fd, 33820 JS_CLOSURE_LOCAL, i, vd->var_name, FALSE, 33821 vd->is_lexical, JS_VAR_NORMAL); 33822 } 33823 } 33824 } 33825 if (fd->is_eval) { 33826 int idx; 33827 /* add direct eval variables (we are necessarily at the 33828 top level). */ 33829 for (idx = 0; idx < fd->closure_var_count; idx++) { 33830 JSClosureVar *cv = &fd->closure_var[idx]; 33831 /* Global variables are removed but module 33832 definitions are kept. */ 33833 if (cv->closure_type != JS_CLOSURE_GLOBAL_REF && 33834 cv->closure_type != JS_CLOSURE_GLOBAL_DECL && 33835 cv->closure_type != JS_CLOSURE_GLOBAL) { 33836 get_closure_var(ctx, s, fd, 33837 JS_CLOSURE_REF, 33838 idx, cv->var_name, cv->is_const, 33839 cv->is_lexical, cv->var_kind); 33840 } 33841 } 33842 } 33843 } 33844 } 33845 33846 static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, 33847 JSBytecodeVarDef *vd, int var_idx) 33848 { 33849 cv->closure_type = JS_CLOSURE_LOCAL; 33850 cv->is_const = vd->is_const; 33851 cv->is_lexical = vd->is_lexical; 33852 cv->var_kind = vd->var_kind; 33853 cv->var_idx = var_idx; 33854 cv->var_name = JS_DupAtom(ctx, vd->var_name); 33855 } 33856 33857 /* for direct eval compilation: add references to the variables of the 33858 calling function */ 33859 static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, 33860 JSFunctionBytecode *b, int scope_idx) 33861 { 33862 int i, count; 33863 JSBytecodeVarDef *vd; 33864 BOOL is_arg_scope; 33865 33866 count = b->arg_count + b->var_count + b->closure_var_count; 33867 s->closure_var = NULL; 33868 s->closure_var_count = 0; 33869 s->closure_var_size = count; 33870 if (count == 0) 33871 return 0; 33872 s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count); 33873 if (!s->closure_var) 33874 return -1; 33875 /* Add lexical variables in scope at the point of evaluation */ 33876 for (i = scope_idx; i >= 0;) { 33877 vd = &b->vardefs[b->arg_count + i]; 33878 if (vd->has_scope) { 33879 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 33880 set_closure_from_var(ctx, cv, vd, i); 33881 } 33882 i = vd->scope_next; 33883 } 33884 is_arg_scope = (i == ARG_SCOPE_END); 33885 if (!is_arg_scope) { 33886 /* Add argument variables */ 33887 for(i = 0; i < b->arg_count; i++) { 33888 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 33889 vd = &b->vardefs[i]; 33890 cv->closure_type = JS_CLOSURE_ARG; 33891 cv->is_const = FALSE; 33892 cv->is_lexical = FALSE; 33893 cv->var_kind = JS_VAR_NORMAL; 33894 cv->var_idx = i; 33895 cv->var_name = JS_DupAtom(ctx, vd->var_name); 33896 } 33897 /* Add local non lexical variables */ 33898 for(i = 0; i < b->var_count; i++) { 33899 vd = &b->vardefs[b->arg_count + i]; 33900 if (!vd->has_scope && vd->var_name != JS_ATOM__ret_) { 33901 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 33902 set_closure_from_var(ctx, cv, vd, i); 33903 } 33904 } 33905 } else { 33906 /* only add pseudo variables */ 33907 for(i = 0; i < b->var_count; i++) { 33908 vd = &b->vardefs[b->arg_count + i]; 33909 if (!vd->has_scope && is_var_in_arg_scope(vd->var_name, vd->var_kind)) { 33910 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 33911 set_closure_from_var(ctx, cv, vd, i); 33912 } 33913 } 33914 } 33915 for(i = 0; i < b->closure_var_count; i++) { 33916 JSClosureVar *cv0 = &b->closure_var[i]; 33917 JSClosureVar *cv; 33918 33919 switch(cv0->closure_type) { 33920 case JS_CLOSURE_LOCAL: 33921 case JS_CLOSURE_ARG: 33922 case JS_CLOSURE_REF: 33923 case JS_CLOSURE_MODULE_DECL: 33924 case JS_CLOSURE_MODULE_IMPORT: 33925 break; 33926 case JS_CLOSURE_GLOBAL_REF: 33927 case JS_CLOSURE_GLOBAL_DECL: 33928 case JS_CLOSURE_GLOBAL: 33929 continue; /* not necessary to add global variables */ 33930 default: 33931 abort(); 33932 } 33933 cv = &s->closure_var[s->closure_var_count++]; 33934 cv->closure_type = JS_CLOSURE_REF; 33935 cv->is_const = cv0->is_const; 33936 cv->is_lexical = cv0->is_lexical; 33937 cv->var_kind = cv0->var_kind; 33938 cv->var_idx = i; 33939 cv->var_name = JS_DupAtom(ctx, cv0->var_name); 33940 } 33941 return 0; 33942 } 33943 33944 typedef struct CodeContext { 33945 const uint8_t *bc_buf; /* code buffer */ 33946 int bc_len; /* length of the code buffer */ 33947 int pos; /* position past the matched code pattern */ 33948 int line_num; /* last visited OP_line_num parameter or -1 */ 33949 int op; 33950 int idx; 33951 int label; 33952 int val; 33953 JSAtom atom; 33954 } CodeContext; 33955 33956 #define M2(op1, op2) ((int)((uint32_t)(op1) | ((uint32_t)(op2) << 8))) 33957 #define M3(op1, op2, op3) ((int)((uint32_t)(op1) | ((uint32_t)(op2) << 8) | ((uint32_t)(op3) << 16))) 33958 #define M4(op1, op2, op3, op4) ((int)((uint32_t)(op1) | ((uint32_t)(op2) << 8) | ((uint32_t)(op3) << 16) | ((uint32_t)(op4) << 24))) 33959 33960 static BOOL code_match(CodeContext *s, int pos, ...) 33961 { 33962 const uint8_t *tab = s->bc_buf; 33963 int op, len, op1, line_num, pos_next; 33964 va_list ap; 33965 BOOL ret = FALSE; 33966 33967 line_num = -1; 33968 va_start(ap, pos); 33969 33970 for(;;) { 33971 op1 = va_arg(ap, int); 33972 if (op1 == -1) { 33973 s->pos = pos; 33974 s->line_num = line_num; 33975 ret = TRUE; 33976 break; 33977 } 33978 for (;;) { 33979 if (pos >= s->bc_len) 33980 goto done; 33981 op = tab[pos]; 33982 len = opcode_info[op].size; 33983 pos_next = pos + len; 33984 if (pos_next > s->bc_len) 33985 goto done; 33986 if (op == OP_line_num) { 33987 line_num = get_u32(tab + pos + 1); 33988 pos = pos_next; 33989 } else { 33990 break; 33991 } 33992 } 33993 if (op != op1) { 33994 if (op1 == (uint8_t)op1 || !op) 33995 break; 33996 if (op != (uint8_t)op1 33997 && op != (uint8_t)(op1 >> 8) 33998 && op != (uint8_t)(op1 >> 16) 33999 && op != (uint8_t)(op1 >> 24)) { 34000 break; 34001 } 34002 s->op = op; 34003 } 34004 34005 pos++; 34006 switch(opcode_info[op].fmt) { 34007 case OP_FMT_loc8: 34008 case OP_FMT_u8: 34009 { 34010 int idx = tab[pos]; 34011 int arg = va_arg(ap, int); 34012 if (arg == -1) { 34013 s->idx = idx; 34014 } else { 34015 if (arg != idx) 34016 goto done; 34017 } 34018 break; 34019 } 34020 case OP_FMT_u16: 34021 case OP_FMT_npop: 34022 case OP_FMT_loc: 34023 case OP_FMT_arg: 34024 case OP_FMT_var_ref: 34025 { 34026 int idx = get_u16(tab + pos); 34027 int arg = va_arg(ap, int); 34028 if (arg == -1) { 34029 s->idx = idx; 34030 } else { 34031 if (arg != idx) 34032 goto done; 34033 } 34034 break; 34035 } 34036 case OP_FMT_i32: 34037 case OP_FMT_u32: 34038 case OP_FMT_label: 34039 case OP_FMT_const: 34040 { 34041 s->label = get_u32(tab + pos); 34042 break; 34043 } 34044 case OP_FMT_label_u16: 34045 { 34046 s->label = get_u32(tab + pos); 34047 s->val = get_u16(tab + pos + 4); 34048 break; 34049 } 34050 case OP_FMT_atom: 34051 { 34052 s->atom = get_u32(tab + pos); 34053 break; 34054 } 34055 case OP_FMT_atom_u8: 34056 { 34057 s->atom = get_u32(tab + pos); 34058 s->val = get_u8(tab + pos + 4); 34059 break; 34060 } 34061 case OP_FMT_atom_u16: 34062 { 34063 s->atom = get_u32(tab + pos); 34064 s->val = get_u16(tab + pos + 4); 34065 break; 34066 } 34067 case OP_FMT_atom_label_u8: 34068 { 34069 s->atom = get_u32(tab + pos); 34070 s->label = get_u32(tab + pos + 4); 34071 s->val = get_u8(tab + pos + 8); 34072 break; 34073 } 34074 default: 34075 break; 34076 } 34077 pos = pos_next; 34078 } 34079 done: 34080 va_end(ap); 34081 return ret; 34082 } 34083 34084 static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) 34085 { 34086 int i, idx, label_next = -1; 34087 34088 /* add the hoisted functions in arguments and local variables */ 34089 for(i = 0; i < s->arg_count; i++) { 34090 JSVarDef *vd = &s->args[i]; 34091 if (vd->func_pool_idx >= 0) { 34092 dbuf_putc(bc, OP_fclosure); 34093 dbuf_put_u32(bc, vd->func_pool_idx); 34094 dbuf_putc(bc, OP_put_arg); 34095 dbuf_put_u16(bc, i); 34096 } 34097 } 34098 for(i = 0; i < s->var_count; i++) { 34099 JSVarDef *vd = &s->vars[i]; 34100 if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { 34101 dbuf_putc(bc, OP_fclosure); 34102 dbuf_put_u32(bc, vd->func_pool_idx); 34103 dbuf_putc(bc, OP_put_loc); 34104 dbuf_put_u16(bc, i); 34105 } 34106 } 34107 34108 /* the module global variables must be initialized before 34109 evaluating the module so that the exported functions are 34110 visible if there are cyclic module references */ 34111 if (s->module) { 34112 label_next = new_label_fd(s); 34113 if (label_next < 0) { 34114 dbuf_set_error(bc); 34115 return; 34116 } 34117 /* if 'this' is true, initialize the global variables and return */ 34118 dbuf_putc(bc, OP_push_this); 34119 dbuf_putc(bc, OP_if_false); 34120 dbuf_put_u32(bc, label_next); 34121 update_label(s, label_next, 1); 34122 s->jump_size++; 34123 } 34124 34125 /* add the global variables (only happens if s->is_global_var is 34126 true) */ 34127 /* XXX: inefficient, add a closure index in JSGlobalVar */ 34128 for(i = 0; i < s->global_var_count; i++) { 34129 JSGlobalVar *hf = &s->global_vars[i]; 34130 BOOL has_var_obj = FALSE; 34131 BOOL force_init = hf->force_init; 34132 /* we are in an eval, so the closure contains all the 34133 enclosing variables */ 34134 /* If the outer function has a variable environment, 34135 create a property for the variable there */ 34136 for(idx = 0; idx < s->closure_var_count; idx++) { 34137 JSClosureVar *cv = &s->closure_var[idx]; 34138 if (cv->var_name == hf->var_name) { 34139 force_init = FALSE; 34140 goto closure_found; 34141 } 34142 if (cv->var_name == JS_ATOM__var_ || 34143 cv->var_name == JS_ATOM__arg_var_) { 34144 dbuf_putc(bc, OP_get_var_ref); 34145 dbuf_put_u16(bc, idx); 34146 has_var_obj = TRUE; 34147 force_init = TRUE; 34148 goto closure_found; 34149 } 34150 } 34151 abort(); 34152 closure_found: 34153 if (hf->cpool_idx >= 0 || force_init) { 34154 if (hf->cpool_idx >= 0) { 34155 dbuf_putc(bc, OP_fclosure); 34156 dbuf_put_u32(bc, hf->cpool_idx); 34157 if (hf->var_name == JS_ATOM__default_) { 34158 /* set default export function name */ 34159 dbuf_putc(bc, OP_set_name); 34160 dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default)); 34161 } 34162 } else { 34163 dbuf_putc(bc, OP_undefined); 34164 } 34165 if (!has_var_obj) { 34166 dbuf_putc(bc, OP_put_var_ref); 34167 dbuf_put_u16(bc, idx); 34168 } else { 34169 dbuf_putc(bc, OP_define_field); 34170 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); 34171 dbuf_putc(bc, OP_drop); 34172 } 34173 } 34174 JS_FreeAtom(ctx, hf->var_name); 34175 } 34176 34177 if (s->module) { 34178 dbuf_putc(bc, OP_return_undef); 34179 34180 dbuf_putc(bc, OP_label); 34181 dbuf_put_u32(bc, label_next); 34182 s->label_slots[label_next].pos2 = bc->size; 34183 } 34184 34185 js_free(ctx, s->global_vars); 34186 s->global_vars = NULL; 34187 s->global_var_count = 0; 34188 s->global_var_size = 0; 34189 } 34190 34191 static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, 34192 int pos, int *linep) 34193 { 34194 int op, len, label; 34195 34196 for (; pos < bc_len; pos += len) { 34197 op = bc_buf[pos]; 34198 len = opcode_info[op].size; 34199 if (op == OP_line_num) { 34200 *linep = get_u32(bc_buf + pos + 1); 34201 } else 34202 if (op == OP_label) { 34203 label = get_u32(bc_buf + pos + 1); 34204 if (update_label(s, label, 0) > 0) 34205 break; 34206 #if 0 34207 if (s->label_slots[label].first_reloc) { 34208 printf("line %d: unreferenced label %d:%d has relocations\n", 34209 *linep, label, s->label_slots[label].pos2); 34210 } 34211 #endif 34212 assert(s->label_slots[label].first_reloc == NULL); 34213 } else { 34214 /* XXX: output a warning for unreachable code? */ 34215 JSAtom atom; 34216 switch(opcode_info[op].fmt) { 34217 case OP_FMT_label: 34218 case OP_FMT_label_u16: 34219 label = get_u32(bc_buf + pos + 1); 34220 update_label(s, label, -1); 34221 break; 34222 case OP_FMT_atom_label_u8: 34223 case OP_FMT_atom_label_u16: 34224 label = get_u32(bc_buf + pos + 5); 34225 update_label(s, label, -1); 34226 /* fall thru */ 34227 case OP_FMT_atom: 34228 case OP_FMT_atom_u8: 34229 case OP_FMT_atom_u16: 34230 atom = get_u32(bc_buf + pos + 1); 34231 JS_FreeAtom(s->ctx, atom); 34232 break; 34233 default: 34234 break; 34235 } 34236 } 34237 } 34238 return pos; 34239 } 34240 34241 static int get_label_pos(JSFunctionDef *s, int label) 34242 { 34243 int i, pos; 34244 for (i = 0; i < 20; i++) { 34245 pos = s->label_slots[label].pos; 34246 for (;;) { 34247 switch (s->byte_code.buf[pos]) { 34248 case OP_line_num: 34249 case OP_label: 34250 pos += 5; 34251 continue; 34252 case OP_goto: 34253 label = get_u32(s->byte_code.buf + pos + 1); 34254 break; 34255 default: 34256 return pos; 34257 } 34258 break; 34259 } 34260 } 34261 return pos; 34262 } 34263 34264 /* convert global variable accesses to local variables or closure 34265 variables when necessary */ 34266 static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) 34267 { 34268 int pos, pos_next, bc_len, op, len, line_num, i, idx; 34269 uint8_t *bc_buf; 34270 JSAtom var_name; 34271 DynBuf bc_out; 34272 CodeContext cc; 34273 int scope; 34274 34275 cc.bc_buf = bc_buf = s->byte_code.buf; 34276 cc.bc_len = bc_len = s->byte_code.size; 34277 js_dbuf_bytecode_init(ctx, &bc_out); 34278 34279 /* first pass for runtime checks (must be done before the 34280 variables are created) */ 34281 /* XXX: inefficient */ 34282 for(i = 0; i < s->global_var_count; i++) { 34283 JSGlobalVar *hf = &s->global_vars[i]; 34284 34285 /* check if global variable (XXX: simplify) */ 34286 for(idx = 0; idx < s->closure_var_count; idx++) { 34287 JSClosureVar *cv = &s->closure_var[idx]; 34288 if (cv->closure_type == JS_CLOSURE_GLOBAL_REF || 34289 cv->closure_type == JS_CLOSURE_GLOBAL_DECL || 34290 cv->closure_type == JS_CLOSURE_GLOBAL || 34291 cv->closure_type == JS_CLOSURE_MODULE_DECL || 34292 cv->closure_type == JS_CLOSURE_MODULE_IMPORT) 34293 goto next; /* don't look at global variables (they are at the end) */ 34294 if (cv->var_name == hf->var_name) { 34295 if (s->eval_type == JS_EVAL_TYPE_DIRECT && 34296 cv->is_lexical) { 34297 /* Check if a lexical variable is 34298 redefined as 'var'. XXX: Could abort 34299 compilation here, but for consistency 34300 with the other checks, we delay the 34301 error generation. */ 34302 dbuf_putc(&bc_out, OP_throw_error); 34303 dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); 34304 dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); 34305 } 34306 goto next; 34307 } 34308 if (cv->var_name == JS_ATOM__var_ || 34309 cv->var_name == JS_ATOM__arg_var_) 34310 goto next; 34311 } 34312 next: ; 34313 } 34314 34315 line_num = 0; /* avoid warning */ 34316 for (pos = 0; pos < bc_len; pos = pos_next) { 34317 op = bc_buf[pos]; 34318 len = opcode_info[op].size; 34319 pos_next = pos + len; 34320 switch(op) { 34321 case OP_line_num: 34322 line_num = get_u32(bc_buf + pos + 1); 34323 s->line_number_size++; 34324 goto no_change; 34325 34326 case OP_eval: /* convert scope index to adjusted variable index */ 34327 { 34328 int call_argc = get_u16(bc_buf + pos + 1); 34329 scope = get_u16(bc_buf + pos + 1 + 2); 34330 mark_eval_captured_variables(ctx, s, scope); 34331 dbuf_putc(&bc_out, op); 34332 dbuf_put_u16(&bc_out, call_argc); 34333 dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); 34334 } 34335 break; 34336 case OP_apply_eval: /* convert scope index to adjusted variable index */ 34337 scope = get_u16(bc_buf + pos + 1); 34338 mark_eval_captured_variables(ctx, s, scope); 34339 dbuf_putc(&bc_out, op); 34340 dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END); 34341 break; 34342 case OP_scope_get_var_checkthis: 34343 case OP_scope_get_var_undef: 34344 case OP_scope_get_var: 34345 case OP_scope_put_var: 34346 case OP_scope_delete_var: 34347 case OP_scope_get_ref: 34348 case OP_scope_put_var_init: 34349 var_name = get_u32(bc_buf + pos + 1); 34350 scope = get_u16(bc_buf + pos + 5); 34351 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, 34352 NULL, NULL, pos_next); 34353 JS_FreeAtom(ctx, var_name); 34354 break; 34355 case OP_scope_make_ref: 34356 { 34357 int label; 34358 LabelSlot *ls; 34359 var_name = get_u32(bc_buf + pos + 1); 34360 label = get_u32(bc_buf + pos + 5); 34361 scope = get_u16(bc_buf + pos + 9); 34362 ls = &s->label_slots[label]; 34363 ls->ref_count--; /* always remove label reference */ 34364 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, 34365 bc_buf, ls, pos_next); 34366 JS_FreeAtom(ctx, var_name); 34367 } 34368 break; 34369 case OP_scope_get_private_field: 34370 case OP_scope_get_private_field2: 34371 case OP_scope_put_private_field: 34372 case OP_scope_in_private_field: 34373 { 34374 int ret; 34375 var_name = get_u32(bc_buf + pos + 1); 34376 scope = get_u16(bc_buf + pos + 5); 34377 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out); 34378 if (ret < 0) 34379 goto fail; 34380 JS_FreeAtom(ctx, var_name); 34381 } 34382 break; 34383 case OP_gosub: 34384 s->jump_size++; 34385 if (OPTIMIZE) { 34386 /* remove calls to empty finalizers */ 34387 int label; 34388 LabelSlot *ls; 34389 34390 label = get_u32(bc_buf + pos + 1); 34391 assert(label >= 0 && label < s->label_count); 34392 ls = &s->label_slots[label]; 34393 if (code_match(&cc, ls->pos, OP_ret, -1)) { 34394 ls->ref_count--; 34395 break; 34396 } 34397 } 34398 goto no_change; 34399 case OP_drop: 34400 if (0) { 34401 /* remove drops before return_undef */ 34402 /* do not perform this optimization in pass2 because 34403 it breaks patterns recognised in resolve_labels */ 34404 int pos1 = pos_next; 34405 int line1 = line_num; 34406 while (code_match(&cc, pos1, OP_drop, -1)) { 34407 if (cc.line_num >= 0) line1 = cc.line_num; 34408 pos1 = cc.pos; 34409 } 34410 if (code_match(&cc, pos1, OP_return_undef, -1)) { 34411 pos_next = pos1; 34412 if (line1 != -1 && line1 != line_num) { 34413 line_num = line1; 34414 s->line_number_size++; 34415 dbuf_putc(&bc_out, OP_line_num); 34416 dbuf_put_u32(&bc_out, line_num); 34417 } 34418 break; 34419 } 34420 } 34421 goto no_change; 34422 case OP_insert3: 34423 if (OPTIMIZE) { 34424 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */ 34425 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) { 34426 dbuf_putc(&bc_out, cc.op); 34427 pos_next = cc.pos; 34428 if (cc.line_num != -1 && cc.line_num != line_num) { 34429 line_num = cc.line_num; 34430 s->line_number_size++; 34431 dbuf_putc(&bc_out, OP_line_num); 34432 dbuf_put_u32(&bc_out, line_num); 34433 } 34434 break; 34435 } 34436 } 34437 goto no_change; 34438 34439 case OP_goto: 34440 s->jump_size++; 34441 /* fall thru */ 34442 case OP_tail_call: 34443 case OP_tail_call_method: 34444 case OP_return: 34445 case OP_return_undef: 34446 case OP_throw: 34447 case OP_throw_error: 34448 case OP_ret: 34449 if (OPTIMIZE) { 34450 /* remove dead code */ 34451 int line = -1; 34452 dbuf_put(&bc_out, bc_buf + pos, len); 34453 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line); 34454 pos_next = pos; 34455 if (pos < bc_len && line >= 0 && line_num != line) { 34456 line_num = line; 34457 s->line_number_size++; 34458 dbuf_putc(&bc_out, OP_line_num); 34459 dbuf_put_u32(&bc_out, line_num); 34460 } 34461 break; 34462 } 34463 goto no_change; 34464 34465 case OP_label: 34466 { 34467 int label; 34468 LabelSlot *ls; 34469 34470 label = get_u32(bc_buf + pos + 1); 34471 assert(label >= 0 && label < s->label_count); 34472 ls = &s->label_slots[label]; 34473 ls->pos2 = bc_out.size + opcode_info[op].size; 34474 } 34475 goto no_change; 34476 34477 case OP_enter_scope: 34478 { 34479 int scope_idx, scope = get_u16(bc_buf + pos + 1); 34480 34481 if (scope == s->body_scope) { 34482 instantiate_hoisted_definitions(ctx, s, &bc_out); 34483 } 34484 34485 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { 34486 JSVarDef *vd = &s->vars[scope_idx]; 34487 if (vd->scope_level == scope) { 34488 if (scope_idx != s->arguments_arg_idx) { 34489 if (vd->var_kind == JS_VAR_FUNCTION_DECL || 34490 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { 34491 /* Initialize lexical variable upon entering scope */ 34492 dbuf_putc(&bc_out, OP_fclosure); 34493 dbuf_put_u32(&bc_out, vd->func_pool_idx); 34494 dbuf_putc(&bc_out, OP_put_loc); 34495 dbuf_put_u16(&bc_out, scope_idx); 34496 } else { 34497 /* XXX: should check if variable can be used 34498 before initialization */ 34499 dbuf_putc(&bc_out, OP_set_loc_uninitialized); 34500 dbuf_put_u16(&bc_out, scope_idx); 34501 } 34502 } 34503 scope_idx = vd->scope_next; 34504 } else { 34505 break; 34506 } 34507 } 34508 } 34509 break; 34510 34511 case OP_leave_scope: 34512 { 34513 int scope_idx, scope = get_u16(bc_buf + pos + 1); 34514 34515 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { 34516 JSVarDef *vd = &s->vars[scope_idx]; 34517 if (vd->scope_level == scope) { 34518 if (vd->is_captured) { 34519 dbuf_putc(&bc_out, OP_close_loc); 34520 dbuf_put_u16(&bc_out, scope_idx); 34521 } 34522 scope_idx = vd->scope_next; 34523 } else { 34524 break; 34525 } 34526 } 34527 } 34528 break; 34529 34530 case OP_set_name: 34531 { 34532 /* remove dummy set_name opcodes */ 34533 JSAtom name = get_u32(bc_buf + pos + 1); 34534 if (name == JS_ATOM_NULL) 34535 break; 34536 } 34537 goto no_change; 34538 34539 case OP_if_false: 34540 case OP_if_true: 34541 case OP_catch: 34542 s->jump_size++; 34543 goto no_change; 34544 34545 case OP_dup: 34546 if (OPTIMIZE) { 34547 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */ 34548 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */ 34549 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) { 34550 int lab0, lab1, op1, pos1, line1, pos2; 34551 lab0 = lab1 = cc.label; 34552 assert(lab1 >= 0 && lab1 < s->label_count); 34553 op1 = cc.op; 34554 pos1 = cc.pos; 34555 line1 = cc.line_num; 34556 while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) { 34557 lab1 = cc.label; 34558 } 34559 if (code_match(&cc, pos2, op1, -1)) { 34560 s->jump_size++; 34561 update_label(s, lab0, -1); 34562 update_label(s, cc.label, +1); 34563 dbuf_putc(&bc_out, op1); 34564 dbuf_put_u32(&bc_out, cc.label); 34565 pos_next = pos1; 34566 if (line1 != -1 && line1 != line_num) { 34567 line_num = line1; 34568 s->line_number_size++; 34569 dbuf_putc(&bc_out, OP_line_num); 34570 dbuf_put_u32(&bc_out, line_num); 34571 } 34572 break; 34573 } 34574 } 34575 } 34576 goto no_change; 34577 34578 case OP_nop: 34579 /* remove erased code */ 34580 break; 34581 case OP_set_class_name: 34582 /* only used during parsing */ 34583 break; 34584 34585 case OP_get_field_opt_chain: /* equivalent to OP_get_field */ 34586 { 34587 JSAtom name = get_u32(bc_buf + pos + 1); 34588 dbuf_putc(&bc_out, OP_get_field); 34589 dbuf_put_u32(&bc_out, name); 34590 } 34591 break; 34592 case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */ 34593 dbuf_putc(&bc_out, OP_get_array_el); 34594 break; 34595 34596 default: 34597 no_change: 34598 dbuf_put(&bc_out, bc_buf + pos, len); 34599 break; 34600 } 34601 } 34602 34603 /* set the new byte code */ 34604 dbuf_free(&s->byte_code); 34605 s->byte_code = bc_out; 34606 if (dbuf_error(&s->byte_code)) { 34607 JS_ThrowOutOfMemory(ctx); 34608 return -1; 34609 } 34610 return 0; 34611 fail: 34612 /* continue the copy to keep the atom refcounts consistent */ 34613 /* XXX: find a better solution ? */ 34614 for (; pos < bc_len; pos = pos_next) { 34615 op = bc_buf[pos]; 34616 len = opcode_info[op].size; 34617 pos_next = pos + len; 34618 dbuf_put(&bc_out, bc_buf + pos, len); 34619 } 34620 dbuf_free(&s->byte_code); 34621 s->byte_code = bc_out; 34622 return -1; 34623 } 34624 34625 /* the pc2line table gives a source position for each PC value */ 34626 static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, uint32_t source_pos) 34627 { 34628 if (s->line_number_slots != NULL 34629 && s->line_number_count < s->line_number_size 34630 && pc >= s->line_number_last_pc 34631 && source_pos != s->line_number_last) { 34632 s->line_number_slots[s->line_number_count].pc = pc; 34633 s->line_number_slots[s->line_number_count].source_pos = source_pos; 34634 s->line_number_count++; 34635 s->line_number_last_pc = pc; 34636 s->line_number_last = source_pos; 34637 } 34638 } 34639 34640 /* XXX: could use a more compact storage */ 34641 /* XXX: get_line_col_cached() is slow. For more predictable 34642 performance, line/cols could be stored every N source 34643 bytes. Alternatively, get_line_col_cached() could be issued in 34644 emit_source_pos() so that the deltas are more likely to be 34645 small. */ 34646 static void compute_pc2line_info(JSFunctionDef *s) 34647 { 34648 if (!s->strip_debug) { 34649 int last_line_num, last_col_num; 34650 uint32_t last_pc = 0; 34651 int i, line_num, col_num; 34652 const uint8_t *buf_start = s->get_line_col_cache->buf_start; 34653 js_dbuf_init(s->ctx, &s->pc2line); 34654 34655 last_line_num = get_line_col_cached(s->get_line_col_cache, 34656 &last_col_num, 34657 buf_start + s->source_pos); 34658 dbuf_put_leb128(&s->pc2line, last_line_num); /* line number minus 1 */ 34659 dbuf_put_leb128(&s->pc2line, last_col_num); /* column number minus 1 */ 34660 34661 for (i = 0; i < s->line_number_count; i++) { 34662 uint32_t pc = s->line_number_slots[i].pc; 34663 uint32_t source_pos = s->line_number_slots[i].source_pos; 34664 int diff_pc, diff_line, diff_col; 34665 34666 if (source_pos == -1) 34667 continue; 34668 diff_pc = pc - last_pc; 34669 if (diff_pc < 0) 34670 continue; 34671 34672 line_num = get_line_col_cached(s->get_line_col_cache, &col_num, 34673 buf_start + source_pos); 34674 diff_line = line_num - last_line_num; 34675 diff_col = col_num - last_col_num; 34676 if (diff_line == 0 && diff_col == 0) 34677 continue; 34678 34679 if (diff_line >= PC2LINE_BASE && 34680 diff_line < PC2LINE_BASE + PC2LINE_RANGE && 34681 diff_pc <= PC2LINE_DIFF_PC_MAX) { 34682 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) + 34683 diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST); 34684 } else { 34685 /* longer encoding */ 34686 dbuf_putc(&s->pc2line, 0); 34687 dbuf_put_leb128(&s->pc2line, diff_pc); 34688 dbuf_put_sleb128(&s->pc2line, diff_line); 34689 } 34690 dbuf_put_sleb128(&s->pc2line, diff_col); 34691 34692 last_pc = pc; 34693 last_line_num = line_num; 34694 last_col_num = col_num; 34695 } 34696 } 34697 } 34698 34699 static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size) 34700 { 34701 RelocEntry *re; 34702 re = js_malloc(ctx, sizeof(*re)); 34703 if (!re) 34704 return NULL; 34705 re->addr = addr; 34706 re->size = size; 34707 re->next = ls->first_reloc; 34708 ls->first_reloc = re; 34709 return re; 34710 } 34711 34712 static BOOL code_has_label(CodeContext *s, int pos, int label) 34713 { 34714 while (pos < s->bc_len) { 34715 int op = s->bc_buf[pos]; 34716 if (op == OP_line_num) { 34717 pos += 5; 34718 continue; 34719 } 34720 if (op == OP_label) { 34721 int lab = get_u32(s->bc_buf + pos + 1); 34722 if (lab == label) 34723 return TRUE; 34724 pos += 5; 34725 continue; 34726 } 34727 if (op == OP_goto) { 34728 int lab = get_u32(s->bc_buf + pos + 1); 34729 if (lab == label) 34730 return TRUE; 34731 } 34732 break; 34733 } 34734 return FALSE; 34735 } 34736 34737 /* return the target label, following the OP_goto jumps 34738 the first opcode at destination is stored in *pop 34739 */ 34740 static int find_jump_target(JSFunctionDef *s, int label0, int *pop, int *pline) 34741 { 34742 int i, pos, op, label; 34743 34744 label = label0; 34745 update_label(s, label, -1); 34746 for (i = 0; i < 10; i++) { 34747 assert(label >= 0 && label < s->label_count); 34748 pos = s->label_slots[label].pos2; 34749 for (;;) { 34750 switch(op = s->byte_code.buf[pos]) { 34751 case OP_line_num: 34752 if (pline) 34753 *pline = get_u32(s->byte_code.buf + pos + 1); 34754 /* fall thru */ 34755 case OP_label: 34756 pos += opcode_info[op].size; 34757 continue; 34758 case OP_goto: 34759 label = get_u32(s->byte_code.buf + pos + 1); 34760 break; 34761 case OP_drop: 34762 /* ignore drop opcodes if followed by OP_return_undef */ 34763 while (s->byte_code.buf[++pos] == OP_drop) 34764 continue; 34765 if (s->byte_code.buf[pos] == OP_return_undef) 34766 op = OP_return_undef; 34767 /* fall thru */ 34768 default: 34769 goto done; 34770 } 34771 break; 34772 } 34773 } 34774 /* cycle detected, could issue a warning */ 34775 /* XXX: the combination of find_jump_target() and skip_dead_code() 34776 seems incorrect with cyclic labels. See for exemple: 34777 34778 for (;;) { 34779 l:break l; 34780 l:break l; 34781 l:break l; 34782 l:break l; 34783 } 34784 34785 Avoiding changing the target is just a workaround and might not 34786 suffice to completely fix the problem. */ 34787 label = label0; 34788 done: 34789 *pop = op; 34790 update_label(s, label, +1); 34791 return label; 34792 } 34793 34794 static void push_short_int(DynBuf *bc_out, int val) 34795 { 34796 #if SHORT_OPCODES 34797 if (val >= -1 && val <= 7) { 34798 dbuf_putc(bc_out, OP_push_0 + val); 34799 return; 34800 } 34801 if (val == (int8_t)val) { 34802 dbuf_putc(bc_out, OP_push_i8); 34803 dbuf_putc(bc_out, val); 34804 return; 34805 } 34806 if (val == (int16_t)val) { 34807 dbuf_putc(bc_out, OP_push_i16); 34808 dbuf_put_u16(bc_out, val); 34809 return; 34810 } 34811 #endif 34812 dbuf_putc(bc_out, OP_push_i32); 34813 dbuf_put_u32(bc_out, val); 34814 } 34815 34816 static void put_short_code(DynBuf *bc_out, int op, int idx) 34817 { 34818 #if SHORT_OPCODES 34819 if (idx < 4) { 34820 switch (op) { 34821 case OP_get_loc: 34822 dbuf_putc(bc_out, OP_get_loc0 + idx); 34823 return; 34824 case OP_put_loc: 34825 dbuf_putc(bc_out, OP_put_loc0 + idx); 34826 return; 34827 case OP_set_loc: 34828 dbuf_putc(bc_out, OP_set_loc0 + idx); 34829 return; 34830 case OP_get_arg: 34831 dbuf_putc(bc_out, OP_get_arg0 + idx); 34832 return; 34833 case OP_put_arg: 34834 dbuf_putc(bc_out, OP_put_arg0 + idx); 34835 return; 34836 case OP_set_arg: 34837 dbuf_putc(bc_out, OP_set_arg0 + idx); 34838 return; 34839 case OP_get_var_ref: 34840 dbuf_putc(bc_out, OP_get_var_ref0 + idx); 34841 return; 34842 case OP_put_var_ref: 34843 dbuf_putc(bc_out, OP_put_var_ref0 + idx); 34844 return; 34845 case OP_set_var_ref: 34846 dbuf_putc(bc_out, OP_set_var_ref0 + idx); 34847 return; 34848 case OP_call: 34849 dbuf_putc(bc_out, OP_call0 + idx); 34850 return; 34851 } 34852 } 34853 if (idx < 256) { 34854 switch (op) { 34855 case OP_get_loc: 34856 dbuf_putc(bc_out, OP_get_loc8); 34857 dbuf_putc(bc_out, idx); 34858 return; 34859 case OP_put_loc: 34860 dbuf_putc(bc_out, OP_put_loc8); 34861 dbuf_putc(bc_out, idx); 34862 return; 34863 case OP_set_loc: 34864 dbuf_putc(bc_out, OP_set_loc8); 34865 dbuf_putc(bc_out, idx); 34866 return; 34867 } 34868 } 34869 #endif 34870 dbuf_putc(bc_out, op); 34871 dbuf_put_u16(bc_out, idx); 34872 } 34873 34874 /* peephole optimizations and resolve goto/labels */ 34875 static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) 34876 { 34877 int pos, pos_next, bc_len, op, op1, len, i, line_num; 34878 const uint8_t *bc_buf; 34879 DynBuf bc_out; 34880 LabelSlot *label_slots, *ls; 34881 RelocEntry *re, *re_next; 34882 CodeContext cc; 34883 int label; 34884 #if SHORT_OPCODES 34885 JumpSlot *jp; 34886 #endif 34887 34888 label_slots = s->label_slots; 34889 34890 line_num = s->source_pos; 34891 34892 cc.bc_buf = bc_buf = s->byte_code.buf; 34893 cc.bc_len = bc_len = s->byte_code.size; 34894 js_dbuf_bytecode_init(ctx, &bc_out); 34895 34896 #if SHORT_OPCODES 34897 if (s->jump_size) { 34898 s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size); 34899 if (s->jump_slots == NULL) 34900 return -1; 34901 } 34902 #endif 34903 /* XXX: Should skip this phase if not generating SHORT_OPCODES */ 34904 if (s->line_number_size && !s->strip_debug) { 34905 s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size); 34906 if (s->line_number_slots == NULL) 34907 return -1; 34908 s->line_number_last = s->source_pos; 34909 s->line_number_last_pc = 0; 34910 } 34911 34912 /* initialize the 'home_object' variable if needed */ 34913 if (s->home_object_var_idx >= 0) { 34914 dbuf_putc(&bc_out, OP_special_object); 34915 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT); 34916 put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx); 34917 } 34918 /* initialize the 'this.active_func' variable if needed */ 34919 if (s->this_active_func_var_idx >= 0) { 34920 dbuf_putc(&bc_out, OP_special_object); 34921 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); 34922 put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx); 34923 } 34924 /* initialize the 'new.target' variable if needed */ 34925 if (s->new_target_var_idx >= 0) { 34926 dbuf_putc(&bc_out, OP_special_object); 34927 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET); 34928 put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx); 34929 } 34930 /* initialize the 'this' variable if needed. In a derived class 34931 constructor, this is initially uninitialized. */ 34932 if (s->this_var_idx >= 0) { 34933 if (s->is_derived_class_constructor) { 34934 dbuf_putc(&bc_out, OP_set_loc_uninitialized); 34935 dbuf_put_u16(&bc_out, s->this_var_idx); 34936 } else { 34937 dbuf_putc(&bc_out, OP_push_this); 34938 put_short_code(&bc_out, OP_put_loc, s->this_var_idx); 34939 } 34940 } 34941 /* initialize the 'arguments' variable if needed */ 34942 if (s->arguments_var_idx >= 0) { 34943 if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) { 34944 dbuf_putc(&bc_out, OP_special_object); 34945 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS); 34946 } else { 34947 dbuf_putc(&bc_out, OP_special_object); 34948 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); 34949 /* the arguments are implicitly captured because 34950 references to them are created with the 'argument' 34951 object */ 34952 for(i = 0; i < s->arg_count; i++) 34953 capture_var(s, &s->args[i]); 34954 } 34955 if (s->arguments_arg_idx >= 0) 34956 put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); 34957 put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); 34958 } 34959 /* initialize a reference to the current function if needed */ 34960 if (s->func_var_idx >= 0) { 34961 dbuf_putc(&bc_out, OP_special_object); 34962 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); 34963 put_short_code(&bc_out, OP_put_loc, s->func_var_idx); 34964 } 34965 /* initialize the variable environment object if needed */ 34966 if (s->var_object_idx >= 0) { 34967 dbuf_putc(&bc_out, OP_special_object); 34968 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); 34969 put_short_code(&bc_out, OP_put_loc, s->var_object_idx); 34970 } 34971 if (s->arg_var_object_idx >= 0) { 34972 dbuf_putc(&bc_out, OP_special_object); 34973 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); 34974 put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); 34975 } 34976 34977 for (pos = 0; pos < bc_len; pos = pos_next) { 34978 int val; 34979 op = bc_buf[pos]; 34980 len = opcode_info[op].size; 34981 pos_next = pos + len; 34982 switch(op) { 34983 case OP_line_num: 34984 /* line number info (for debug). We put it in a separate 34985 compressed table to reduce memory usage and get better 34986 performance */ 34987 line_num = get_u32(bc_buf + pos + 1); 34988 break; 34989 34990 case OP_label: 34991 { 34992 label = get_u32(bc_buf + pos + 1); 34993 assert(label >= 0 && label < s->label_count); 34994 ls = &label_slots[label]; 34995 assert(ls->addr == -1); 34996 ls->addr = bc_out.size; 34997 /* resolve the relocation entries */ 34998 for(re = ls->first_reloc; re != NULL; re = re_next) { 34999 int diff = ls->addr - re->addr; 35000 re_next = re->next; 35001 switch (re->size) { 35002 case 4: 35003 put_u32(bc_out.buf + re->addr, diff); 35004 break; 35005 case 2: 35006 assert(diff == (int16_t)diff); 35007 put_u16(bc_out.buf + re->addr, diff); 35008 break; 35009 case 1: 35010 assert(diff == (int8_t)diff); 35011 put_u8(bc_out.buf + re->addr, diff); 35012 break; 35013 } 35014 js_free(ctx, re); 35015 } 35016 ls->first_reloc = NULL; 35017 } 35018 break; 35019 35020 case OP_call: 35021 case OP_call_method: 35022 { 35023 /* detect and transform tail calls */ 35024 int argc; 35025 argc = get_u16(bc_buf + pos + 1); 35026 if (code_match(&cc, pos_next, OP_return, -1)) { 35027 if (cc.line_num >= 0) line_num = cc.line_num; 35028 add_pc2line_info(s, bc_out.size, line_num); 35029 put_short_code(&bc_out, op + 1, argc); 35030 pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num); 35031 break; 35032 } 35033 add_pc2line_info(s, bc_out.size, line_num); 35034 put_short_code(&bc_out, op, argc); 35035 break; 35036 } 35037 goto no_change; 35038 35039 case OP_return: 35040 case OP_return_undef: 35041 case OP_return_async: 35042 case OP_throw: 35043 case OP_throw_error: 35044 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 35045 goto no_change; 35046 35047 case OP_goto: 35048 label = get_u32(bc_buf + pos + 1); 35049 has_goto: 35050 if (OPTIMIZE) { 35051 int line1 = -1; 35052 /* Use custom matcher because multiple labels can follow */ 35053 label = find_jump_target(s, label, &op1, &line1); 35054 if (code_has_label(&cc, pos_next, label)) { 35055 /* jump to next instruction: remove jump */ 35056 update_label(s, label, -1); 35057 break; 35058 } 35059 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) { 35060 /* jump to return/throw: remove jump, append return/throw */ 35061 /* updating the line number obfuscates assembly listing */ 35062 //if (line1 != -1) line_num = line1; 35063 update_label(s, label, -1); 35064 add_pc2line_info(s, bc_out.size, line_num); 35065 dbuf_putc(&bc_out, op1); 35066 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 35067 break; 35068 } 35069 /* XXX: should duplicate single instructions followed by goto or return */ 35070 /* For example, can match one of these followed by return: 35071 push_i32 / push_const / push_atom_value / get_var / 35072 undefined / null / push_false / push_true / get_ref_value / 35073 get_loc / get_arg / get_var_ref 35074 */ 35075 } 35076 goto has_label; 35077 35078 case OP_gosub: 35079 label = get_u32(bc_buf + pos + 1); 35080 if (0 && OPTIMIZE) { 35081 label = find_jump_target(s, label, &op1, NULL); 35082 if (op1 == OP_ret) { 35083 update_label(s, label, -1); 35084 /* empty finally clause: remove gosub */ 35085 break; 35086 } 35087 } 35088 goto has_label; 35089 35090 case OP_catch: 35091 label = get_u32(bc_buf + pos + 1); 35092 goto has_label; 35093 35094 case OP_if_true: 35095 case OP_if_false: 35096 label = get_u32(bc_buf + pos + 1); 35097 if (OPTIMIZE) { 35098 label = find_jump_target(s, label, &op1, NULL); 35099 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */ 35100 if (code_has_label(&cc, pos_next, label)) { 35101 update_label(s, label, -1); 35102 dbuf_putc(&bc_out, OP_drop); 35103 break; 35104 } 35105 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */ 35106 if (code_match(&cc, pos_next, OP_goto, -1)) { 35107 int pos1 = cc.pos; 35108 int line1 = cc.line_num; 35109 if (code_has_label(&cc, pos1, label)) { 35110 if (line1 != -1) line_num = line1; 35111 pos_next = pos1; 35112 update_label(s, label, -1); 35113 label = cc.label; 35114 op ^= OP_if_true ^ OP_if_false; 35115 } 35116 } 35117 } 35118 has_label: 35119 add_pc2line_info(s, bc_out.size, line_num); 35120 if (op == OP_goto) { 35121 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 35122 } 35123 assert(label >= 0 && label < s->label_count); 35124 ls = &label_slots[label]; 35125 #if SHORT_OPCODES 35126 jp = &s->jump_slots[s->jump_count++]; 35127 jp->op = op; 35128 jp->size = 4; 35129 jp->pos = bc_out.size + 1; 35130 jp->label = label; 35131 35132 if (ls->addr == -1) { 35133 int diff = ls->pos2 - pos - 1; 35134 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { 35135 jp->size = 1; 35136 jp->op = OP_if_false8 + (op - OP_if_false); 35137 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); 35138 dbuf_putc(&bc_out, 0); 35139 if (!add_reloc(ctx, ls, bc_out.size - 1, 1)) 35140 goto fail; 35141 break; 35142 } 35143 if (diff < 32768 && op == OP_goto) { 35144 jp->size = 2; 35145 jp->op = OP_goto16; 35146 dbuf_putc(&bc_out, OP_goto16); 35147 dbuf_put_u16(&bc_out, 0); 35148 if (!add_reloc(ctx, ls, bc_out.size - 2, 2)) 35149 goto fail; 35150 break; 35151 } 35152 } else { 35153 int diff = ls->addr - bc_out.size - 1; 35154 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { 35155 jp->size = 1; 35156 jp->op = OP_if_false8 + (op - OP_if_false); 35157 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); 35158 dbuf_putc(&bc_out, diff); 35159 break; 35160 } 35161 if (diff == (int16_t)diff && op == OP_goto) { 35162 jp->size = 2; 35163 jp->op = OP_goto16; 35164 dbuf_putc(&bc_out, OP_goto16); 35165 dbuf_put_u16(&bc_out, diff); 35166 break; 35167 } 35168 } 35169 #endif 35170 dbuf_putc(&bc_out, op); 35171 dbuf_put_u32(&bc_out, ls->addr - bc_out.size); 35172 if (ls->addr == -1) { 35173 /* unresolved yet: create a new relocation entry */ 35174 if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) 35175 goto fail; 35176 } 35177 break; 35178 case OP_with_get_var: 35179 case OP_with_put_var: 35180 case OP_with_delete_var: 35181 case OP_with_make_ref: 35182 case OP_with_get_ref: 35183 { 35184 JSAtom atom; 35185 int is_with; 35186 35187 atom = get_u32(bc_buf + pos + 1); 35188 label = get_u32(bc_buf + pos + 5); 35189 is_with = bc_buf[pos + 9]; 35190 if (OPTIMIZE) { 35191 label = find_jump_target(s, label, &op1, NULL); 35192 } 35193 assert(label >= 0 && label < s->label_count); 35194 ls = &label_slots[label]; 35195 add_pc2line_info(s, bc_out.size, line_num); 35196 #if SHORT_OPCODES 35197 jp = &s->jump_slots[s->jump_count++]; 35198 jp->op = op; 35199 jp->size = 4; 35200 jp->pos = bc_out.size + 5; 35201 jp->label = label; 35202 #endif 35203 dbuf_putc(&bc_out, op); 35204 dbuf_put_u32(&bc_out, atom); 35205 dbuf_put_u32(&bc_out, ls->addr - bc_out.size); 35206 if (ls->addr == -1) { 35207 /* unresolved yet: create a new relocation entry */ 35208 if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) 35209 goto fail; 35210 } 35211 dbuf_putc(&bc_out, is_with); 35212 } 35213 break; 35214 35215 case OP_drop: 35216 if (OPTIMIZE) { 35217 /* remove useless drops before return */ 35218 if (code_match(&cc, pos_next, OP_return_undef, -1)) { 35219 if (cc.line_num >= 0) line_num = cc.line_num; 35220 break; 35221 } 35222 } 35223 goto no_change; 35224 35225 case OP_null: 35226 #if SHORT_OPCODES 35227 if (OPTIMIZE) { 35228 /* transform null strict_eq into is_null */ 35229 if (code_match(&cc, pos_next, OP_strict_eq, -1)) { 35230 if (cc.line_num >= 0) line_num = cc.line_num; 35231 add_pc2line_info(s, bc_out.size, line_num); 35232 dbuf_putc(&bc_out, OP_is_null); 35233 pos_next = cc.pos; 35234 break; 35235 } 35236 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */ 35237 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { 35238 if (cc.line_num >= 0) line_num = cc.line_num; 35239 add_pc2line_info(s, bc_out.size, line_num); 35240 dbuf_putc(&bc_out, OP_is_null); 35241 pos_next = cc.pos; 35242 label = cc.label; 35243 op = cc.op ^ OP_if_false ^ OP_if_true; 35244 goto has_label; 35245 } 35246 } 35247 #endif 35248 /* fall thru */ 35249 case OP_push_false: 35250 case OP_push_true: 35251 if (OPTIMIZE) { 35252 val = (op == OP_push_true); 35253 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 35254 has_constant_test: 35255 if (cc.line_num >= 0) line_num = cc.line_num; 35256 if (val == cc.op - OP_if_false) { 35257 /* transform null if_false(l1) -> goto l1 */ 35258 /* transform false if_false(l1) -> goto l1 */ 35259 /* transform true if_true(l1) -> goto l1 */ 35260 pos_next = cc.pos; 35261 op = OP_goto; 35262 label = cc.label; 35263 goto has_goto; 35264 } else { 35265 /* transform null if_true(l1) -> nop */ 35266 /* transform false if_true(l1) -> nop */ 35267 /* transform true if_false(l1) -> nop */ 35268 pos_next = cc.pos; 35269 update_label(s, cc.label, -1); 35270 break; 35271 } 35272 } 35273 } 35274 goto no_change; 35275 35276 case OP_push_i32: 35277 if (OPTIMIZE) { 35278 /* transform i32(val) neg -> i32(-val) */ 35279 val = get_i32(bc_buf + pos + 1); 35280 if ((val != INT32_MIN && val != 0) 35281 && code_match(&cc, pos_next, OP_neg, -1)) { 35282 if (cc.line_num >= 0) line_num = cc.line_num; 35283 if (code_match(&cc, cc.pos, OP_drop, -1)) { 35284 if (cc.line_num >= 0) line_num = cc.line_num; 35285 } else { 35286 add_pc2line_info(s, bc_out.size, line_num); 35287 push_short_int(&bc_out, -val); 35288 } 35289 pos_next = cc.pos; 35290 break; 35291 } 35292 /* remove push/drop pairs generated by the parser */ 35293 if (code_match(&cc, pos_next, OP_drop, -1)) { 35294 if (cc.line_num >= 0) line_num = cc.line_num; 35295 pos_next = cc.pos; 35296 break; 35297 } 35298 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */ 35299 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 35300 val = (val != 0); 35301 goto has_constant_test; 35302 } 35303 add_pc2line_info(s, bc_out.size, line_num); 35304 push_short_int(&bc_out, val); 35305 break; 35306 } 35307 goto no_change; 35308 35309 case OP_push_bigint_i32: 35310 if (OPTIMIZE) { 35311 /* transform i32(val) neg -> i32(-val) */ 35312 val = get_i32(bc_buf + pos + 1); 35313 if (val != INT32_MIN 35314 && code_match(&cc, pos_next, OP_neg, -1)) { 35315 if (cc.line_num >= 0) line_num = cc.line_num; 35316 if (code_match(&cc, cc.pos, OP_drop, -1)) { 35317 if (cc.line_num >= 0) line_num = cc.line_num; 35318 } else { 35319 add_pc2line_info(s, bc_out.size, line_num); 35320 dbuf_putc(&bc_out, OP_push_bigint_i32); 35321 dbuf_put_u32(&bc_out, -val); 35322 } 35323 pos_next = cc.pos; 35324 break; 35325 } 35326 } 35327 goto no_change; 35328 35329 #if SHORT_OPCODES 35330 case OP_push_const: 35331 case OP_fclosure: 35332 if (OPTIMIZE) { 35333 int idx = get_u32(bc_buf + pos + 1); 35334 if (idx < 256) { 35335 add_pc2line_info(s, bc_out.size, line_num); 35336 dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const); 35337 dbuf_putc(&bc_out, idx); 35338 break; 35339 } 35340 } 35341 goto no_change; 35342 35343 case OP_get_field: 35344 if (OPTIMIZE) { 35345 JSAtom atom = get_u32(bc_buf + pos + 1); 35346 if (atom == JS_ATOM_length) { 35347 JS_FreeAtom(ctx, atom); 35348 add_pc2line_info(s, bc_out.size, line_num); 35349 dbuf_putc(&bc_out, OP_get_length); 35350 break; 35351 } 35352 } 35353 goto no_change; 35354 #endif 35355 case OP_push_atom_value: 35356 if (OPTIMIZE) { 35357 JSAtom atom = get_u32(bc_buf + pos + 1); 35358 /* remove push/drop pairs generated by the parser */ 35359 if (code_match(&cc, pos_next, OP_drop, -1)) { 35360 JS_FreeAtom(ctx, atom); 35361 if (cc.line_num >= 0) line_num = cc.line_num; 35362 pos_next = cc.pos; 35363 break; 35364 } 35365 #if SHORT_OPCODES 35366 if (atom == JS_ATOM_empty_string) { 35367 JS_FreeAtom(ctx, atom); 35368 add_pc2line_info(s, bc_out.size, line_num); 35369 dbuf_putc(&bc_out, OP_push_empty_string); 35370 break; 35371 } 35372 #endif 35373 } 35374 goto no_change; 35375 35376 case OP_to_propkey: 35377 if (OPTIMIZE) { 35378 /* remove redundant to_propkey opcodes when storing simple data */ 35379 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) 35380 || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1) 35381 || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { 35382 break; 35383 } 35384 } 35385 goto no_change; 35386 35387 case OP_undefined: 35388 if (OPTIMIZE) { 35389 /* remove push/drop pairs generated by the parser */ 35390 if (code_match(&cc, pos_next, OP_drop, -1)) { 35391 if (cc.line_num >= 0) line_num = cc.line_num; 35392 pos_next = cc.pos; 35393 break; 35394 } 35395 /* transform undefined return -> return_undefined */ 35396 if (code_match(&cc, pos_next, OP_return, -1)) { 35397 if (cc.line_num >= 0) line_num = cc.line_num; 35398 add_pc2line_info(s, bc_out.size, line_num); 35399 dbuf_putc(&bc_out, OP_return_undef); 35400 pos_next = cc.pos; 35401 break; 35402 } 35403 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */ 35404 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 35405 val = 0; 35406 goto has_constant_test; 35407 } 35408 #if SHORT_OPCODES 35409 /* transform undefined strict_eq -> is_undefined */ 35410 if (code_match(&cc, pos_next, OP_strict_eq, -1)) { 35411 if (cc.line_num >= 0) line_num = cc.line_num; 35412 add_pc2line_info(s, bc_out.size, line_num); 35413 dbuf_putc(&bc_out, OP_is_undefined); 35414 pos_next = cc.pos; 35415 break; 35416 } 35417 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */ 35418 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { 35419 if (cc.line_num >= 0) line_num = cc.line_num; 35420 add_pc2line_info(s, bc_out.size, line_num); 35421 dbuf_putc(&bc_out, OP_is_undefined); 35422 pos_next = cc.pos; 35423 label = cc.label; 35424 op = cc.op ^ OP_if_false ^ OP_if_true; 35425 goto has_label; 35426 } 35427 #endif 35428 } 35429 goto no_change; 35430 35431 case OP_insert2: 35432 if (OPTIMIZE) { 35433 /* Transformation: 35434 insert2 put_field(a) drop -> put_field(a) 35435 */ 35436 if (code_match(&cc, pos_next, OP_put_field, OP_drop, -1)) { 35437 if (cc.line_num >= 0) line_num = cc.line_num; 35438 add_pc2line_info(s, bc_out.size, line_num); 35439 dbuf_putc(&bc_out, OP_put_field); 35440 dbuf_put_u32(&bc_out, cc.atom); 35441 pos_next = cc.pos; 35442 break; 35443 } 35444 } 35445 goto no_change; 35446 35447 case OP_dup: 35448 if (OPTIMIZE) { 35449 /* Transformation: dup put_x(n) drop -> put_x(n) */ 35450 int op1, line2 = -1; 35451 /* Transformation: dup put_x(n) -> set_x(n) */ 35452 if (code_match(&cc, pos_next, M4(OP_put_loc, OP_put_loc_check, OP_put_arg, OP_put_var_ref), -1, -1)) { 35453 if (cc.line_num >= 0) line_num = cc.line_num; 35454 op1 = cc.op + 1; /* put_x -> set_x */ 35455 pos_next = cc.pos; 35456 if (code_match(&cc, cc.pos, OP_drop, -1)) { 35457 if (cc.line_num >= 0) line_num = cc.line_num; 35458 op1 -= 1; /* set_x drop -> put_x */ 35459 pos_next = cc.pos; 35460 if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) { 35461 line2 = cc.line_num; /* delay line number update */ 35462 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ 35463 pos_next = cc.pos; 35464 } 35465 } 35466 add_pc2line_info(s, bc_out.size, line_num); 35467 put_short_code(&bc_out, op1, cc.idx); 35468 if (line2 >= 0) line_num = line2; 35469 break; 35470 } 35471 } 35472 goto no_change; 35473 35474 case OP_get_loc: 35475 if (OPTIMIZE) { 35476 /* transformation: 35477 get_loc(n) post_dec put_loc(n) drop -> dec_loc(n) 35478 get_loc(n) post_inc put_loc(n) drop -> inc_loc(n) 35479 get_loc(n) dec dup put_loc(n) drop -> dec_loc(n) 35480 get_loc(n) inc dup put_loc(n) drop -> inc_loc(n) 35481 */ 35482 int idx; 35483 idx = get_u16(bc_buf + pos + 1); 35484 if (idx >= 256) 35485 goto no_change; 35486 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) || 35487 code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) { 35488 if (cc.line_num >= 0) line_num = cc.line_num; 35489 add_pc2line_info(s, bc_out.size, line_num); 35490 dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc); 35491 dbuf_putc(&bc_out, idx); 35492 pos_next = cc.pos; 35493 break; 35494 } 35495 /* transformation: 35496 get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n) 35497 */ 35498 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { 35499 if (cc.line_num >= 0) line_num = cc.line_num; 35500 add_pc2line_info(s, bc_out.size, line_num); 35501 #if SHORT_OPCODES 35502 if (cc.atom == JS_ATOM_empty_string) { 35503 JS_FreeAtom(ctx, cc.atom); 35504 dbuf_putc(&bc_out, OP_push_empty_string); 35505 } else 35506 #endif 35507 { 35508 dbuf_putc(&bc_out, OP_push_atom_value); 35509 dbuf_put_u32(&bc_out, cc.atom); 35510 } 35511 dbuf_putc(&bc_out, OP_add_loc); 35512 dbuf_putc(&bc_out, idx); 35513 pos_next = cc.pos; 35514 break; 35515 } 35516 /* transformation: 35517 get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n) 35518 */ 35519 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { 35520 if (cc.line_num >= 0) line_num = cc.line_num; 35521 add_pc2line_info(s, bc_out.size, line_num); 35522 push_short_int(&bc_out, cc.label); 35523 dbuf_putc(&bc_out, OP_add_loc); 35524 dbuf_putc(&bc_out, idx); 35525 pos_next = cc.pos; 35526 break; 35527 } 35528 /* transformation: XXX: also do these: 35529 get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n) 35530 get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n) 35531 get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n) 35532 */ 35533 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { 35534 if (cc.line_num >= 0) line_num = cc.line_num; 35535 add_pc2line_info(s, bc_out.size, line_num); 35536 put_short_code(&bc_out, cc.op, cc.idx); 35537 dbuf_putc(&bc_out, OP_add_loc); 35538 dbuf_putc(&bc_out, idx); 35539 pos_next = cc.pos; 35540 break; 35541 } 35542 add_pc2line_info(s, bc_out.size, line_num); 35543 put_short_code(&bc_out, op, idx); 35544 break; 35545 } 35546 goto no_change; 35547 #if SHORT_OPCODES 35548 case OP_get_arg: 35549 case OP_get_var_ref: 35550 if (OPTIMIZE) { 35551 int idx; 35552 idx = get_u16(bc_buf + pos + 1); 35553 add_pc2line_info(s, bc_out.size, line_num); 35554 put_short_code(&bc_out, op, idx); 35555 break; 35556 } 35557 goto no_change; 35558 #endif 35559 case OP_put_loc: 35560 case OP_put_loc_check: 35561 case OP_put_arg: 35562 case OP_put_var_ref: 35563 if (OPTIMIZE) { 35564 /* transformation: put_x(n) get_x(n) -> set_x(n) */ 35565 int idx; 35566 idx = get_u16(bc_buf + pos + 1); 35567 if (code_match(&cc, pos_next, op - 1, idx, -1)) { 35568 if (cc.line_num >= 0) line_num = cc.line_num; 35569 add_pc2line_info(s, bc_out.size, line_num); 35570 put_short_code(&bc_out, op + 1, idx); 35571 pos_next = cc.pos; 35572 break; 35573 } 35574 add_pc2line_info(s, bc_out.size, line_num); 35575 put_short_code(&bc_out, op, idx); 35576 break; 35577 } 35578 goto no_change; 35579 35580 case OP_post_inc: 35581 case OP_post_dec: 35582 if (OPTIMIZE) { 35583 /* transformation: 35584 post_inc put_x drop -> inc put_x 35585 post_inc perm3 put_field drop -> inc put_field 35586 post_inc perm4 put_array_el drop -> inc put_array_el 35587 */ 35588 int op1, idx; 35589 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) { 35590 if (cc.line_num >= 0) line_num = cc.line_num; 35591 op1 = cc.op; 35592 idx = cc.idx; 35593 pos_next = cc.pos; 35594 if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) { 35595 if (cc.line_num >= 0) line_num = cc.line_num; 35596 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ 35597 pos_next = cc.pos; 35598 } 35599 add_pc2line_info(s, bc_out.size, line_num); 35600 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 35601 put_short_code(&bc_out, op1, idx); 35602 break; 35603 } 35604 if (code_match(&cc, pos_next, OP_perm3, OP_put_field, OP_drop, -1)) { 35605 if (cc.line_num >= 0) line_num = cc.line_num; 35606 add_pc2line_info(s, bc_out.size, line_num); 35607 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 35608 dbuf_putc(&bc_out, OP_put_field); 35609 dbuf_put_u32(&bc_out, cc.atom); 35610 pos_next = cc.pos; 35611 break; 35612 } 35613 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) { 35614 if (cc.line_num >= 0) line_num = cc.line_num; 35615 add_pc2line_info(s, bc_out.size, line_num); 35616 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 35617 dbuf_putc(&bc_out, OP_put_array_el); 35618 pos_next = cc.pos; 35619 break; 35620 } 35621 } 35622 goto no_change; 35623 35624 #if SHORT_OPCODES 35625 case OP_typeof: 35626 if (OPTIMIZE) { 35627 /* simplify typeof tests */ 35628 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { 35629 if (cc.line_num >= 0) line_num = cc.line_num; 35630 int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; 35631 int op2 = -1; 35632 switch (cc.atom) { 35633 case JS_ATOM_undefined: 35634 op2 = OP_typeof_is_undefined; 35635 break; 35636 case JS_ATOM_function: 35637 op2 = OP_typeof_is_function; 35638 break; 35639 } 35640 if (op2 >= 0) { 35641 /* transform typeof(s) == "<type>" into is_<type> */ 35642 if (op1 == OP_strict_eq) { 35643 add_pc2line_info(s, bc_out.size, line_num); 35644 dbuf_putc(&bc_out, op2); 35645 JS_FreeAtom(ctx, cc.atom); 35646 pos_next = cc.pos; 35647 break; 35648 } 35649 if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) { 35650 /* transform typeof(s) != "<type>" if_false into is_<type> if_true */ 35651 if (cc.line_num >= 0) line_num = cc.line_num; 35652 add_pc2line_info(s, bc_out.size, line_num); 35653 dbuf_putc(&bc_out, op2); 35654 JS_FreeAtom(ctx, cc.atom); 35655 pos_next = cc.pos; 35656 label = cc.label; 35657 op = OP_if_true; 35658 goto has_label; 35659 } 35660 } 35661 } 35662 } 35663 goto no_change; 35664 #endif 35665 35666 default: 35667 no_change: 35668 add_pc2line_info(s, bc_out.size, line_num); 35669 dbuf_put(&bc_out, bc_buf + pos, len); 35670 break; 35671 } 35672 } 35673 35674 /* check that there were no missing labels */ 35675 for(i = 0; i < s->label_count; i++) { 35676 assert(label_slots[i].first_reloc == NULL); 35677 } 35678 #if SHORT_OPCODES 35679 if (OPTIMIZE) { 35680 /* more jump optimizations */ 35681 int patch_offsets = 0; 35682 for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) { 35683 LabelSlot *ls; 35684 JumpSlot *jp1; 35685 int j, pos, diff, delta; 35686 35687 delta = 3; 35688 switch (op = jp->op) { 35689 case OP_goto16: 35690 delta = 1; 35691 /* fall thru */ 35692 case OP_if_false: 35693 case OP_if_true: 35694 case OP_goto: 35695 pos = jp->pos; 35696 diff = s->label_slots[jp->label].addr - pos; 35697 if (diff >= -128 && diff <= 127 + delta) { 35698 //put_u8(bc_out.buf + pos, diff); 35699 jp->size = 1; 35700 if (op == OP_goto16) { 35701 bc_out.buf[pos - 1] = jp->op = OP_goto8; 35702 } else { 35703 bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); 35704 } 35705 goto shrink; 35706 } else 35707 if (diff == (int16_t)diff && op == OP_goto) { 35708 //put_u16(bc_out.buf + pos, diff); 35709 jp->size = 2; 35710 delta = 2; 35711 bc_out.buf[pos - 1] = jp->op = OP_goto16; 35712 shrink: 35713 /* XXX: should reduce complexity, using 2 finger copy scheme */ 35714 memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta, 35715 bc_out.size - pos - jp->size - delta); 35716 bc_out.size -= delta; 35717 patch_offsets++; 35718 for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) { 35719 if (ls->addr > pos) 35720 ls->addr -= delta; 35721 } 35722 for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) { 35723 if (jp1->pos > pos) 35724 jp1->pos -= delta; 35725 } 35726 for (j = 0; j < s->line_number_count; j++) { 35727 if (s->line_number_slots[j].pc > pos) 35728 s->line_number_slots[j].pc -= delta; 35729 } 35730 continue; 35731 } 35732 break; 35733 } 35734 } 35735 if (patch_offsets) { 35736 JumpSlot *jp1; 35737 int j; 35738 for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) { 35739 int diff1 = s->label_slots[jp1->label].addr - jp1->pos; 35740 switch (jp1->size) { 35741 case 1: 35742 put_u8(bc_out.buf + jp1->pos, diff1); 35743 break; 35744 case 2: 35745 put_u16(bc_out.buf + jp1->pos, diff1); 35746 break; 35747 case 4: 35748 put_u32(bc_out.buf + jp1->pos, diff1); 35749 break; 35750 } 35751 } 35752 } 35753 } 35754 js_free(ctx, s->jump_slots); 35755 s->jump_slots = NULL; 35756 #endif 35757 js_free(ctx, s->label_slots); 35758 s->label_slots = NULL; 35759 /* XXX: should delay until copying to runtime bytecode function */ 35760 compute_pc2line_info(s); 35761 js_free(ctx, s->line_number_slots); 35762 s->line_number_slots = NULL; 35763 /* set the new byte code */ 35764 dbuf_free(&s->byte_code); 35765 s->byte_code = bc_out; 35766 s->use_short_opcodes = TRUE; 35767 if (dbuf_error(&s->byte_code)) { 35768 JS_ThrowOutOfMemory(ctx); 35769 return -1; 35770 } 35771 return 0; 35772 fail: 35773 /* XXX: not safe */ 35774 dbuf_free(&bc_out); 35775 return -1; 35776 } 35777 35778 /* compute the maximum stack size needed by the function */ 35779 35780 typedef struct StackSizeState { 35781 int bc_len; 35782 int stack_len_max; 35783 uint16_t *stack_level_tab; 35784 int32_t *catch_pos_tab; 35785 int *pc_stack; 35786 int pc_stack_len; 35787 int pc_stack_size; 35788 } StackSizeState; 35789 35790 /* 'op' is only used for error indication */ 35791 static __exception int ss_check(JSContext *ctx, StackSizeState *s, 35792 int pos, int op, int stack_len, int catch_pos) 35793 { 35794 if ((unsigned)pos >= s->bc_len) { 35795 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); 35796 return -1; 35797 } 35798 if (stack_len > s->stack_len_max) { 35799 s->stack_len_max = stack_len; 35800 if (s->stack_len_max > JS_STACK_SIZE_MAX) { 35801 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); 35802 return -1; 35803 } 35804 } 35805 if (s->stack_level_tab[pos] != 0xffff) { 35806 /* already explored: check that the stack size is consistent */ 35807 if (s->stack_level_tab[pos] != stack_len) { 35808 JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)", 35809 s->stack_level_tab[pos], stack_len, pos); 35810 return -1; 35811 } else if (s->catch_pos_tab[pos] != catch_pos) { 35812 JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)", 35813 s->catch_pos_tab[pos], catch_pos, pos); 35814 return -1; 35815 } else { 35816 return 0; 35817 } 35818 } 35819 35820 /* mark as explored and store the stack size */ 35821 s->stack_level_tab[pos] = stack_len; 35822 s->catch_pos_tab[pos] = catch_pos; 35823 35824 /* queue the new PC to explore */ 35825 if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), 35826 &s->pc_stack_size, s->pc_stack_len + 1)) 35827 return -1; 35828 s->pc_stack[s->pc_stack_len++] = pos; 35829 return 0; 35830 } 35831 35832 static __exception int compute_stack_size(JSContext *ctx, 35833 JSFunctionDef *fd, 35834 int *pstack_size) 35835 { 35836 StackSizeState s_s, *s = &s_s; 35837 int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; 35838 const JSOpCode *oi; 35839 const uint8_t *bc_buf; 35840 35841 bc_buf = fd->byte_code.buf; 35842 s->bc_len = fd->byte_code.size; 35843 /* bc_len > 0 */ 35844 s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * 35845 s->bc_len); 35846 if (!s->stack_level_tab) 35847 return -1; 35848 for(i = 0; i < s->bc_len; i++) 35849 s->stack_level_tab[i] = 0xffff; 35850 s->pc_stack = NULL; 35851 s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * 35852 s->bc_len); 35853 if (!s->catch_pos_tab) 35854 goto fail; 35855 35856 s->stack_len_max = 0; 35857 s->pc_stack_len = 0; 35858 s->pc_stack_size = 0; 35859 35860 /* breadth-first graph exploration */ 35861 if (ss_check(ctx, s, 0, OP_invalid, 0, -1)) 35862 goto fail; 35863 35864 while (s->pc_stack_len > 0) { 35865 pos = s->pc_stack[--s->pc_stack_len]; 35866 stack_len = s->stack_level_tab[pos]; 35867 catch_pos = s->catch_pos_tab[pos]; 35868 op = bc_buf[pos]; 35869 if (op == 0 || op >= OP_COUNT) { 35870 JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); 35871 goto fail; 35872 } 35873 oi = &short_opcode_info(op); 35874 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64) 35875 printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos); 35876 #endif 35877 pos_next = pos + oi->size; 35878 if (pos_next > s->bc_len) { 35879 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); 35880 goto fail; 35881 } 35882 n_pop = oi->n_pop; 35883 /* call pops a variable number of arguments */ 35884 if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) { 35885 n_pop += get_u16(bc_buf + pos + 1); 35886 } else { 35887 #if SHORT_OPCODES 35888 if (oi->fmt == OP_FMT_npopx) { 35889 n_pop += op - OP_call0; 35890 } 35891 #endif 35892 } 35893 35894 if (stack_len < n_pop) { 35895 JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); 35896 goto fail; 35897 } 35898 stack_len += oi->n_push - n_pop; 35899 if (stack_len > s->stack_len_max) { 35900 s->stack_len_max = stack_len; 35901 if (s->stack_len_max > JS_STACK_SIZE_MAX) { 35902 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); 35903 goto fail; 35904 } 35905 } 35906 switch(op) { 35907 case OP_tail_call: 35908 case OP_tail_call_method: 35909 case OP_return: 35910 case OP_return_undef: 35911 case OP_return_async: 35912 case OP_throw: 35913 case OP_throw_error: 35914 case OP_ret: 35915 goto done_insn; 35916 case OP_goto: 35917 diff = get_u32(bc_buf + pos + 1); 35918 pos_next = pos + 1 + diff; 35919 break; 35920 #if SHORT_OPCODES 35921 case OP_goto16: 35922 diff = (int16_t)get_u16(bc_buf + pos + 1); 35923 pos_next = pos + 1 + diff; 35924 break; 35925 case OP_goto8: 35926 diff = (int8_t)bc_buf[pos + 1]; 35927 pos_next = pos + 1 + diff; 35928 break; 35929 case OP_if_true8: 35930 case OP_if_false8: 35931 diff = (int8_t)bc_buf[pos + 1]; 35932 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 35933 goto fail; 35934 break; 35935 #endif 35936 case OP_if_true: 35937 case OP_if_false: 35938 diff = get_u32(bc_buf + pos + 1); 35939 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 35940 goto fail; 35941 break; 35942 case OP_gosub: 35943 diff = get_u32(bc_buf + pos + 1); 35944 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos)) 35945 goto fail; 35946 break; 35947 case OP_with_get_var: 35948 case OP_with_delete_var: 35949 diff = get_u32(bc_buf + pos + 5); 35950 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos)) 35951 goto fail; 35952 break; 35953 case OP_with_make_ref: 35954 case OP_with_get_ref: 35955 diff = get_u32(bc_buf + pos + 5); 35956 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos)) 35957 goto fail; 35958 break; 35959 case OP_with_put_var: 35960 diff = get_u32(bc_buf + pos + 5); 35961 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos)) 35962 goto fail; 35963 break; 35964 case OP_catch: 35965 diff = get_u32(bc_buf + pos + 1); 35966 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 35967 goto fail; 35968 catch_pos = pos; 35969 break; 35970 case OP_for_of_start: 35971 case OP_for_await_of_start: 35972 catch_pos = pos; 35973 break; 35974 /* we assume the catch offset entry is only removed with 35975 some op codes */ 35976 case OP_drop: 35977 catch_level = stack_len; 35978 goto check_catch; 35979 case OP_nip: 35980 catch_level = stack_len - 1; 35981 goto check_catch; 35982 case OP_nip1: 35983 catch_level = stack_len - 1; 35984 goto check_catch; 35985 case OP_iterator_close: 35986 catch_level = stack_len + 2; 35987 check_catch: 35988 /* Note: for for_of_start/for_await_of_start we consider 35989 the catch offset is on the first stack entry instead of 35990 the thirst */ 35991 if (catch_pos >= 0) { 35992 int level; 35993 level = s->stack_level_tab[catch_pos]; 35994 if (bc_buf[catch_pos] != OP_catch) 35995 level++; /* for_of_start, for_wait_of_start */ 35996 /* catch_level = stack_level before op_catch is executed ? */ 35997 if (catch_level == level) { 35998 catch_pos = s->catch_pos_tab[catch_pos]; 35999 } 36000 } 36001 break; 36002 case OP_nip_catch: 36003 if (catch_pos < 0) { 36004 JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos); 36005 goto fail; 36006 } 36007 stack_len = s->stack_level_tab[catch_pos]; 36008 if (bc_buf[catch_pos] != OP_catch) 36009 stack_len++; /* for_of_start, for_wait_of_start */ 36010 stack_len++; /* no stack overflow is possible by construction */ 36011 catch_pos = s->catch_pos_tab[catch_pos]; 36012 break; 36013 default: 36014 break; 36015 } 36016 if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos)) 36017 goto fail; 36018 done_insn: ; 36019 } 36020 js_free(ctx, s->pc_stack); 36021 js_free(ctx, s->catch_pos_tab); 36022 js_free(ctx, s->stack_level_tab); 36023 *pstack_size = s->stack_len_max; 36024 return 0; 36025 fail: 36026 js_free(ctx, s->pc_stack); 36027 js_free(ctx, s->catch_pos_tab); 36028 js_free(ctx, s->stack_level_tab); 36029 *pstack_size = 0; 36030 return -1; 36031 } 36032 36033 static int add_global_variables(JSContext *ctx, JSFunctionDef *fd) 36034 { 36035 int i, idx; 36036 JSModuleDef *m = fd->module; 36037 JSExportEntry *me; 36038 JSGlobalVar *hf; 36039 BOOL need_global_closures; 36040 36041 /* Script: add the defined global variables. In the non strict 36042 direct eval not in global scope, the global variables are 36043 created in the enclosing scope so they are not created as 36044 variable references. 36045 36046 In modules, the imported global variables were added as closure 36047 global variables in js_parse_import(). 36048 */ 36049 need_global_closures = TRUE; 36050 if (fd->eval_type == JS_EVAL_TYPE_DIRECT && !(fd->js_mode & JS_MODE_STRICT)) { 36051 /* XXX: add a flag ? */ 36052 for(idx = 0; idx < fd->closure_var_count; idx++) { 36053 JSClosureVar *cv = &fd->closure_var[idx]; 36054 if (cv->var_name == JS_ATOM__var_ || 36055 cv->var_name == JS_ATOM__arg_var_) { 36056 need_global_closures = FALSE; 36057 break; 36058 } 36059 } 36060 } 36061 36062 if (need_global_closures) { 36063 JSClosureTypeEnum closure_type; 36064 if (fd->module) 36065 closure_type = JS_CLOSURE_MODULE_DECL; 36066 else 36067 closure_type = JS_CLOSURE_GLOBAL_DECL; 36068 for(i = 0; i < fd->global_var_count; i++) { 36069 JSVarKindEnum var_kind; 36070 hf = &fd->global_vars[i]; 36071 if (hf->cpool_idx >= 0 && !hf->is_lexical) { 36072 var_kind = JS_VAR_GLOBAL_FUNCTION_DECL; 36073 } else { 36074 var_kind = JS_VAR_NORMAL; 36075 } 36076 if (add_closure_var(ctx, fd, closure_type, i, hf->var_name, hf->is_const, 36077 hf->is_lexical, var_kind) < 0) 36078 return -1; 36079 } 36080 } 36081 36082 if (fd->module) { 36083 /* resolve the variable names of the local exports */ 36084 for(i = 0; i < m->export_entries_count; i++) { 36085 me = &m->export_entries[i]; 36086 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 36087 idx = find_closure_var(ctx, fd, me->local_name); 36088 if (idx < 0) { 36089 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", 36090 me->local_name); 36091 return -1; 36092 } 36093 me->u.local.var_idx = idx; 36094 } 36095 } 36096 } 36097 return 0; 36098 } 36099 36100 /* create a function object from a function definition. The function 36101 definition is freed. All the child functions are also created. It 36102 must be done this way to resolve all the variables. */ 36103 static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) 36104 { 36105 JSValue func_obj; 36106 JSFunctionBytecode *b; 36107 struct list_head *el, *el1; 36108 int stack_size, scope, idx; 36109 int function_size, byte_code_offset, cpool_offset; 36110 int closure_var_offset, vardefs_offset; 36111 BOOL strip_var_debug; 36112 36113 /* recompute scope linkage */ 36114 for (scope = 0; scope < fd->scope_count; scope++) { 36115 fd->scopes[scope].first = -1; 36116 } 36117 if (fd->has_parameter_expressions) { 36118 /* special end of variable list marker for the argument scope */ 36119 fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; 36120 } 36121 for (idx = 0; idx < fd->var_count; idx++) { 36122 JSVarDef *vd = &fd->vars[idx]; 36123 vd->scope_next = fd->scopes[vd->scope_level].first; 36124 fd->scopes[vd->scope_level].first = idx; 36125 } 36126 for (scope = 2; scope < fd->scope_count; scope++) { 36127 JSVarScope *sd = &fd->scopes[scope]; 36128 if (sd->first < 0) 36129 sd->first = fd->scopes[sd->parent].first; 36130 } 36131 for (idx = 0; idx < fd->var_count; idx++) { 36132 JSVarDef *vd = &fd->vars[idx]; 36133 if (vd->scope_next < 0 && vd->scope_level > 1) { 36134 scope = fd->scopes[vd->scope_level].parent; 36135 vd->scope_next = fd->scopes[scope].first; 36136 } 36137 } 36138 36139 /* if the function contains an eval call, the closure variables 36140 are used to compile the eval and they must be ordered by scope, 36141 so it is necessary to create the closure variables before any 36142 other variable lookup is done. */ 36143 if (fd->has_eval_call) 36144 add_eval_variables(ctx, fd); 36145 36146 /* add the module global variables in the closure */ 36147 if (fd->is_eval) { 36148 if (add_global_variables(ctx, fd)) 36149 goto fail; 36150 } 36151 36152 /* first create all the child functions */ 36153 list_for_each_safe(el, el1, &fd->child_list) { 36154 JSFunctionDef *fd1; 36155 int cpool_idx; 36156 36157 fd1 = list_entry(el, JSFunctionDef, link); 36158 cpool_idx = fd1->parent_cpool_idx; 36159 func_obj = js_create_function(ctx, fd1); 36160 if (JS_IsException(func_obj)) 36161 goto fail; 36162 /* save it in the constant pool */ 36163 assert(cpool_idx >= 0); 36164 fd->cpool[cpool_idx] = func_obj; 36165 } 36166 36167 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4) 36168 if (!fd->strip_debug) { 36169 printf("pass 1\n"); 36170 dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, 36171 NULL, fd->args, fd->arg_count, fd->vars, fd->var_count, 36172 fd->closure_var, fd->closure_var_count, 36173 fd->cpool, fd->cpool_count, fd->source, 36174 fd->label_slots, NULL); 36175 printf("\n"); 36176 } 36177 #endif 36178 36179 if (resolve_variables(ctx, fd)) 36180 goto fail; 36181 36182 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2) 36183 if (!fd->strip_debug) { 36184 printf("pass 2\n"); 36185 dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, 36186 NULL, fd->args, fd->arg_count, fd->vars, fd->var_count, 36187 fd->closure_var, fd->closure_var_count, 36188 fd->cpool, fd->cpool_count, fd->source, 36189 fd->label_slots, NULL); 36190 printf("\n"); 36191 } 36192 #endif 36193 36194 if (resolve_labels(ctx, fd)) 36195 goto fail; 36196 36197 if (compute_stack_size(ctx, fd, &stack_size) < 0) 36198 goto fail; 36199 36200 if (fd->strip_debug) { 36201 function_size = offsetof(JSFunctionBytecode, debug); 36202 } else { 36203 function_size = sizeof(*b); 36204 } 36205 cpool_offset = function_size; 36206 function_size += fd->cpool_count * sizeof(*fd->cpool); 36207 vardefs_offset = function_size; 36208 function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs); 36209 closure_var_offset = function_size; 36210 function_size += fd->closure_var_count * sizeof(*fd->closure_var); 36211 byte_code_offset = function_size; 36212 function_size += fd->byte_code.size; 36213 36214 b = js_mallocz(ctx, function_size); 36215 if (!b) 36216 goto fail; 36217 js_rc(b)->ref_count = 1; 36218 36219 b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); 36220 b->byte_code_len = fd->byte_code.size; 36221 memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size); 36222 js_free(ctx, fd->byte_code.buf); 36223 fd->byte_code.buf = NULL; 36224 36225 strip_var_debug = fd->strip_debug && !fd->has_eval_call; /* XXX: check */ 36226 b->func_name = fd->func_name; 36227 if (fd->arg_count + fd->var_count > 0) { 36228 int i; 36229 b->vardefs = (void *)((uint8_t*)b + vardefs_offset); 36230 for(i = 0; i < fd->arg_count; i++) { 36231 JSVarDef *vd = &fd->args[i]; 36232 JSBytecodeVarDef *vd1 = &b->vardefs[i]; 36233 if (strip_var_debug) { 36234 JS_FreeAtom(ctx, vd->var_name); 36235 vd1->var_name = JS_ATOM_NULL; 36236 } else { 36237 vd1->var_name = vd->var_name; 36238 } 36239 vd1->has_scope = (vd->scope_level != 0); 36240 vd1->scope_next = vd->scope_next; 36241 vd1->is_const = vd->is_const; 36242 vd1->is_lexical = vd->is_lexical; 36243 vd1->is_captured = vd->is_captured; 36244 vd1->var_kind = vd->var_kind; 36245 vd1->var_ref_idx = vd->var_ref_idx; 36246 } 36247 36248 for(i = 0; i < fd->var_count; i++) { 36249 JSVarDef *vd = &fd->vars[i]; 36250 JSBytecodeVarDef *vd1 = &b->vardefs[i + fd->arg_count]; 36251 if (strip_var_debug) { 36252 JS_FreeAtom(ctx, vd->var_name); 36253 vd1->var_name = JS_ATOM_NULL; 36254 } else { 36255 vd1->var_name = vd->var_name; 36256 } 36257 vd1->has_scope = (vd->scope_level != 0); 36258 vd1->scope_next = vd->scope_next; 36259 vd1->is_const = vd->is_const; 36260 vd1->is_lexical = vd->is_lexical; 36261 vd1->is_captured = vd->is_captured; 36262 vd1->var_kind = vd->var_kind; 36263 vd1->var_ref_idx = vd->var_ref_idx; 36264 } 36265 b->var_count = fd->var_count; 36266 b->arg_count = fd->arg_count; 36267 b->defined_arg_count = fd->defined_arg_count; 36268 b->var_ref_count = fd->var_ref_count; 36269 js_free(ctx, fd->args); 36270 js_free(ctx, fd->vars); 36271 } 36272 b->cpool_count = fd->cpool_count; 36273 if (b->cpool_count) { 36274 b->cpool = (void *)((uint8_t*)b + cpool_offset); 36275 memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool)); 36276 } 36277 js_free(ctx, fd->cpool); 36278 fd->cpool = NULL; 36279 36280 b->stack_size = stack_size; 36281 36282 b->perf_trampoline = NULL; 36283 36284 if (fd->strip_debug) { 36285 JS_FreeAtom(ctx, fd->filename); 36286 dbuf_free(&fd->pc2line); // probably useless 36287 } else { 36288 /* XXX: source and pc2line info should be packed at the end of the 36289 JSFunctionBytecode structure, avoiding allocation overhead 36290 */ 36291 b->has_debug = 1; 36292 b->debug.filename = fd->filename; 36293 36294 //DynBuf pc2line; 36295 //compute_pc2line_info(fd, &pc2line); 36296 //js_free(ctx, fd->line_number_slots) 36297 b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size); 36298 if (!b->debug.pc2line_buf) 36299 b->debug.pc2line_buf = fd->pc2line.buf; 36300 b->debug.pc2line_len = fd->pc2line.size; 36301 b->debug.source = fd->source; 36302 b->debug.source_len = fd->source_len; 36303 } 36304 if (fd->scopes != fd->def_scope_array) 36305 js_free(ctx, fd->scopes); 36306 36307 b->closure_var_count = fd->closure_var_count; 36308 if (b->closure_var_count) { 36309 if (strip_var_debug) { 36310 int i; 36311 for(i = 0; i < fd->closure_var_count; i++) { 36312 JSClosureVar *cv = &fd->closure_var[i]; 36313 if (cv->closure_type != JS_CLOSURE_GLOBAL_REF && 36314 cv->closure_type != JS_CLOSURE_GLOBAL_DECL && 36315 cv->closure_type != JS_CLOSURE_GLOBAL && 36316 cv->closure_type != JS_CLOSURE_MODULE_DECL && 36317 cv->closure_type != JS_CLOSURE_MODULE_IMPORT) { 36318 JS_FreeAtom(ctx, cv->var_name); 36319 cv->var_name = JS_ATOM_NULL; 36320 } 36321 } 36322 } 36323 b->closure_var = (void *)((uint8_t*)b + closure_var_offset); 36324 memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var)); 36325 } 36326 js_free(ctx, fd->closure_var); 36327 fd->closure_var = NULL; 36328 36329 b->has_prototype = fd->has_prototype; 36330 b->has_simple_parameter_list = fd->has_simple_parameter_list; 36331 b->js_mode = fd->js_mode; 36332 b->is_derived_class_constructor = fd->is_derived_class_constructor; 36333 b->func_kind = fd->func_kind; 36334 b->need_home_object = (fd->home_object_var_idx >= 0 || 36335 fd->need_home_object); 36336 b->new_target_allowed = fd->new_target_allowed; 36337 b->super_call_allowed = fd->super_call_allowed; 36338 b->super_allowed = fd->super_allowed; 36339 b->arguments_allowed = fd->arguments_allowed; 36340 b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT || 36341 fd->eval_type == JS_EVAL_TYPE_INDIRECT); 36342 b->realm = JS_DupContext(ctx); 36343 36344 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); 36345 36346 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1) 36347 if (!fd->strip_debug) { 36348 js_dump_function_bytecode(ctx, b); 36349 } 36350 #endif 36351 36352 if (fd->parent) { 36353 /* remove from parent list */ 36354 list_del(&fd->link); 36355 } 36356 36357 js_free(ctx, fd); 36358 return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); 36359 fail: 36360 js_free_function_def(ctx, fd); 36361 return JS_EXCEPTION; 36362 } 36363 36364 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) 36365 { 36366 int i; 36367 36368 #if 0 36369 { 36370 char buf[ATOM_GET_STR_BUF_SIZE]; 36371 printf("freeing %s\n", 36372 JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); 36373 } 36374 #endif 36375 if (b->byte_code_buf) 36376 free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); 36377 36378 if (b->vardefs) { 36379 for(i = 0; i < b->arg_count + b->var_count; i++) { 36380 JS_FreeAtomRT(rt, b->vardefs[i].var_name); 36381 } 36382 } 36383 for(i = 0; i < b->cpool_count; i++) 36384 JS_FreeValueRT(rt, b->cpool[i]); 36385 36386 for(i = 0; i < b->closure_var_count; i++) { 36387 JSClosureVar *cv = &b->closure_var[i]; 36388 JS_FreeAtomRT(rt, cv->var_name); 36389 } 36390 if (b->realm) 36391 JS_FreeContext(b->realm); 36392 36393 JS_FreeAtomRT(rt, b->func_name); 36394 if (b->has_debug) { 36395 JS_FreeAtomRT(rt, b->debug.filename); 36396 js_free_rt(rt, b->debug.pc2line_buf); 36397 js_free_rt(rt, b->debug.source); 36398 } 36399 36400 remove_gc_object(&b->header); 36401 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && js_rc(b)->ref_count != 0) { 36402 list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); 36403 } else { 36404 js_free_rt(rt, b); 36405 } 36406 } 36407 36408 static __exception int js_parse_directives(JSParseState *s) 36409 { 36410 char str[20]; 36411 JSParsePos pos; 36412 BOOL has_semi; 36413 36414 if (s->token.val != TOK_STRING) 36415 return 0; 36416 36417 js_parse_get_pos(s, &pos); 36418 36419 while(s->token.val == TOK_STRING) { 36420 /* Copy actual source string representation */ 36421 snprintf(str, sizeof str, "%.*s", 36422 (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1); 36423 36424 if (next_token(s)) 36425 return -1; 36426 36427 has_semi = FALSE; 36428 switch (s->token.val) { 36429 case ';': 36430 if (next_token(s)) 36431 return -1; 36432 has_semi = TRUE; 36433 break; 36434 case '}': 36435 case TOK_EOF: 36436 has_semi = TRUE; 36437 break; 36438 case TOK_NUMBER: 36439 case TOK_STRING: 36440 case TOK_TEMPLATE: 36441 case TOK_IDENT: 36442 case TOK_REGEXP: 36443 case TOK_DEC: 36444 case TOK_INC: 36445 case TOK_NULL: 36446 case TOK_FALSE: 36447 case TOK_TRUE: 36448 case TOK_IF: 36449 case TOK_RETURN: 36450 case TOK_VAR: 36451 case TOK_THIS: 36452 case TOK_DELETE: 36453 case TOK_TYPEOF: 36454 case TOK_NEW: 36455 case TOK_DO: 36456 case TOK_WHILE: 36457 case TOK_FOR: 36458 case TOK_SWITCH: 36459 case TOK_THROW: 36460 case TOK_TRY: 36461 case TOK_FUNCTION: 36462 case TOK_DEBUGGER: 36463 case TOK_WITH: 36464 case TOK_CLASS: 36465 case TOK_CONST: 36466 case TOK_ENUM: 36467 case TOK_EXPORT: 36468 case TOK_IMPORT: 36469 case TOK_SUPER: 36470 case TOK_INTERFACE: 36471 case TOK_LET: 36472 case TOK_PACKAGE: 36473 case TOK_PRIVATE: 36474 case TOK_PROTECTED: 36475 case TOK_PUBLIC: 36476 case TOK_STATIC: 36477 /* automatic insertion of ';' */ 36478 if (s->got_lf) 36479 has_semi = TRUE; 36480 break; 36481 default: 36482 break; 36483 } 36484 if (!has_semi) 36485 break; 36486 if (!strcmp(str, "use strict")) { 36487 s->cur_func->has_use_strict = TRUE; 36488 s->cur_func->js_mode |= JS_MODE_STRICT; 36489 } 36490 } 36491 return js_parse_seek_token(s, &pos); 36492 } 36493 36494 /* return TRUE if the keyword is forbidden only in strict mode */ 36495 static BOOL is_strict_future_keyword(JSAtom atom) 36496 { 36497 return (atom >= JS_ATOM_LAST_KEYWORD + 1 && atom <= JS_ATOM_LAST_STRICT_KEYWORD); 36498 } 36499 36500 static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, 36501 JSAtom func_name) 36502 { 36503 JSAtom name; 36504 int i, idx; 36505 36506 if (fd->js_mode & JS_MODE_STRICT) { 36507 if (!fd->has_simple_parameter_list && fd->has_use_strict) { 36508 return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); 36509 } 36510 if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments || 36511 is_strict_future_keyword(func_name)) { 36512 return js_parse_error(s, "invalid function name in strict code"); 36513 } 36514 for (idx = 0; idx < fd->arg_count; idx++) { 36515 name = fd->args[idx].var_name; 36516 36517 if (name == JS_ATOM_eval || name == JS_ATOM_arguments || 36518 is_strict_future_keyword(name)) { 36519 return js_parse_error(s, "invalid argument name in strict code"); 36520 } 36521 } 36522 } 36523 /* check async_generator case */ 36524 if ((fd->js_mode & JS_MODE_STRICT) 36525 || !fd->has_simple_parameter_list 36526 || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC) 36527 || fd->func_type == JS_PARSE_FUNC_ARROW 36528 || fd->func_type == JS_PARSE_FUNC_METHOD) { 36529 for (idx = 0; idx < fd->arg_count; idx++) { 36530 name = fd->args[idx].var_name; 36531 if (name != JS_ATOM_NULL) { 36532 for (i = 0; i < idx; i++) { 36533 if (fd->args[i].var_name == name) 36534 goto duplicate; 36535 } 36536 /* Check if argument name duplicates a destructuring parameter */ 36537 /* XXX: should have a flag for such variables */ 36538 for (i = 0; i < fd->var_count; i++) { 36539 if (fd->vars[i].var_name == name && 36540 fd->vars[i].scope_level == 0) 36541 goto duplicate; 36542 } 36543 } 36544 } 36545 } 36546 return 0; 36547 36548 duplicate: 36549 return js_parse_error(s, "duplicate argument names not allowed in this context"); 36550 } 36551 36552 /* create a function to initialize class fields */ 36553 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) 36554 { 36555 JSFunctionDef *fd; 36556 36557 fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, 36558 s->filename, s->buf_start, 36559 &s->get_line_col_cache); 36560 if (!fd) 36561 return NULL; 36562 fd->func_name = JS_ATOM_NULL; 36563 fd->has_prototype = FALSE; 36564 fd->has_home_object = TRUE; 36565 36566 fd->has_arguments_binding = FALSE; 36567 fd->has_this_binding = TRUE; 36568 fd->is_derived_class_constructor = FALSE; 36569 fd->new_target_allowed = TRUE; 36570 fd->super_call_allowed = FALSE; 36571 fd->super_allowed = fd->has_home_object; 36572 fd->arguments_allowed = FALSE; 36573 36574 fd->func_kind = JS_FUNC_NORMAL; 36575 fd->func_type = JS_PARSE_FUNC_METHOD; 36576 return fd; 36577 } 36578 36579 /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and 36580 JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ 36581 static __exception int js_parse_function_decl2(JSParseState *s, 36582 JSParseFunctionEnum func_type, 36583 JSFunctionKindEnum func_kind, 36584 JSAtom func_name, 36585 const uint8_t *ptr, 36586 JSParseExportEnum export_flag, 36587 JSFunctionDef **pfd) 36588 { 36589 JSContext *ctx = s->ctx; 36590 JSFunctionDef *fd = s->cur_func; 36591 BOOL is_expr; 36592 int func_idx, lexical_func_idx = -1; 36593 BOOL has_opt_arg; 36594 BOOL create_func_var = FALSE; 36595 36596 is_expr = (func_type != JS_PARSE_FUNC_STATEMENT && 36597 func_type != JS_PARSE_FUNC_VAR); 36598 36599 if (func_type == JS_PARSE_FUNC_STATEMENT || 36600 func_type == JS_PARSE_FUNC_VAR || 36601 func_type == JS_PARSE_FUNC_EXPR) { 36602 if (func_kind == JS_FUNC_NORMAL && 36603 token_is_pseudo_keyword(s, JS_ATOM_async) && 36604 peek_token(s, TRUE) != '\n') { 36605 if (next_token(s)) 36606 return -1; 36607 func_kind = JS_FUNC_ASYNC; 36608 } 36609 if (next_token(s)) 36610 return -1; 36611 if (s->token.val == '*') { 36612 if (next_token(s)) 36613 return -1; 36614 func_kind |= JS_FUNC_GENERATOR; 36615 } 36616 36617 if (s->token.val == TOK_IDENT) { 36618 if (s->token.u.ident.is_reserved || 36619 (s->token.u.ident.atom == JS_ATOM_yield && 36620 func_type == JS_PARSE_FUNC_EXPR && 36621 (func_kind & JS_FUNC_GENERATOR)) || 36622 (s->token.u.ident.atom == JS_ATOM_await && 36623 ((func_type == JS_PARSE_FUNC_EXPR && 36624 (func_kind & JS_FUNC_ASYNC)) || 36625 func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { 36626 return js_parse_error_reserved_identifier(s); 36627 } 36628 } 36629 if (s->token.val == TOK_IDENT || 36630 (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) || 36631 (s->token.val == TOK_AWAIT && !s->is_module)) && 36632 func_type == JS_PARSE_FUNC_EXPR)) { 36633 func_name = JS_DupAtom(ctx, s->token.u.ident.atom); 36634 if (next_token(s)) { 36635 JS_FreeAtom(ctx, func_name); 36636 return -1; 36637 } 36638 } else { 36639 if (func_type != JS_PARSE_FUNC_EXPR && 36640 export_flag != JS_PARSE_EXPORT_DEFAULT) { 36641 return js_parse_error(s, "function name expected"); 36642 } 36643 } 36644 } else if (func_type != JS_PARSE_FUNC_ARROW) { 36645 func_name = JS_DupAtom(ctx, func_name); 36646 } 36647 36648 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && 36649 (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { 36650 JSGlobalVar *hf; 36651 hf = find_global_var(fd, func_name); 36652 /* XXX: should check scope chain */ 36653 if (hf && hf->scope_level == fd->scope_level) { 36654 js_parse_error(s, "invalid redefinition of global identifier in module code"); 36655 JS_FreeAtom(ctx, func_name); 36656 return -1; 36657 } 36658 } 36659 36660 if (func_type == JS_PARSE_FUNC_VAR) { 36661 if (!(fd->js_mode & JS_MODE_STRICT) 36662 && func_kind == JS_FUNC_NORMAL 36663 && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 36664 && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) 36665 && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { 36666 create_func_var = TRUE; 36667 } 36668 /* Create the lexical name here so that the function closure 36669 contains it */ 36670 if (fd->is_eval && 36671 (fd->eval_type == JS_EVAL_TYPE_GLOBAL || 36672 fd->eval_type == JS_EVAL_TYPE_MODULE) && 36673 fd->scope_level == fd->body_scope) { 36674 /* avoid creating a lexical variable in the global 36675 scope. XXX: check annex B */ 36676 JSGlobalVar *hf; 36677 hf = find_global_var(fd, func_name); 36678 /* XXX: should check scope chain */ 36679 if (hf && hf->scope_level == fd->scope_level) { 36680 js_parse_error(s, "invalid redefinition of global identifier"); 36681 JS_FreeAtom(ctx, func_name); 36682 return -1; 36683 } 36684 } else { 36685 /* Always create a lexical name, fail if at the same scope as 36686 existing name */ 36687 /* Lexical variable will be initialized upon entering scope */ 36688 lexical_func_idx = define_var(s, fd, func_name, 36689 func_kind != JS_FUNC_NORMAL ? 36690 JS_VAR_DEF_NEW_FUNCTION_DECL : 36691 JS_VAR_DEF_FUNCTION_DECL); 36692 if (lexical_func_idx < 0) { 36693 JS_FreeAtom(ctx, func_name); 36694 return -1; 36695 } 36696 } 36697 } 36698 36699 fd = js_new_function_def(ctx, fd, FALSE, is_expr, 36700 s->filename, ptr, 36701 &s->get_line_col_cache); 36702 if (!fd) { 36703 JS_FreeAtom(ctx, func_name); 36704 return -1; 36705 } 36706 if (pfd) 36707 *pfd = fd; 36708 s->cur_func = fd; 36709 fd->func_name = func_name; 36710 /* XXX: test !fd->is_generator is always false */ 36711 fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT || 36712 func_type == JS_PARSE_FUNC_VAR || 36713 func_type == JS_PARSE_FUNC_EXPR) && 36714 func_kind == JS_FUNC_NORMAL; 36715 fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD || 36716 func_type == JS_PARSE_FUNC_GETTER || 36717 func_type == JS_PARSE_FUNC_SETTER || 36718 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || 36719 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); 36720 fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && 36721 func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT); 36722 fd->has_this_binding = fd->has_arguments_binding; 36723 fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); 36724 if (func_type == JS_PARSE_FUNC_ARROW) { 36725 fd->new_target_allowed = fd->parent->new_target_allowed; 36726 fd->super_call_allowed = fd->parent->super_call_allowed; 36727 fd->super_allowed = fd->parent->super_allowed; 36728 fd->arguments_allowed = fd->parent->arguments_allowed; 36729 } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { 36730 fd->new_target_allowed = TRUE; // although new.target === undefined 36731 fd->super_call_allowed = FALSE; 36732 fd->super_allowed = TRUE; 36733 fd->arguments_allowed = FALSE; 36734 } else { 36735 fd->new_target_allowed = TRUE; 36736 fd->super_call_allowed = fd->is_derived_class_constructor; 36737 fd->super_allowed = fd->has_home_object; 36738 fd->arguments_allowed = TRUE; 36739 } 36740 36741 /* fd->in_function_body == FALSE prevents yield/await during the parsing 36742 of the arguments in generator/async functions. They are parsed as 36743 regular identifiers for other function kinds. */ 36744 fd->func_kind = func_kind; 36745 fd->func_type = func_type; 36746 36747 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || 36748 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { 36749 /* error if not invoked as a constructor */ 36750 emit_op(s, OP_check_ctor); 36751 } 36752 36753 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { 36754 emit_class_field_init(s); 36755 } 36756 36757 /* parse arguments */ 36758 fd->has_simple_parameter_list = TRUE; 36759 fd->has_parameter_expressions = FALSE; 36760 has_opt_arg = FALSE; 36761 if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { 36762 JSAtom name; 36763 if (s->token.u.ident.is_reserved) { 36764 js_parse_error_reserved_identifier(s); 36765 goto fail; 36766 } 36767 name = s->token.u.ident.atom; 36768 if (add_arg(ctx, fd, name) < 0) 36769 goto fail; 36770 fd->defined_arg_count = 1; 36771 } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { 36772 if (s->token.val == '(') { 36773 int skip_bits; 36774 /* if there is an '=' inside the parameter list, we 36775 consider there is a parameter expression inside */ 36776 js_parse_skip_parens_token(s, &skip_bits, FALSE); 36777 if (skip_bits & SKIP_HAS_ASSIGNMENT) 36778 fd->has_parameter_expressions = TRUE; 36779 if (next_token(s)) 36780 goto fail; 36781 } else { 36782 if (js_parse_expect(s, '(')) 36783 goto fail; 36784 } 36785 36786 if (fd->has_parameter_expressions) { 36787 fd->scope_level = -1; /* force no parent scope */ 36788 if (push_scope(s) < 0) 36789 return -1; 36790 } 36791 36792 while (s->token.val != ')') { 36793 JSAtom name; 36794 BOOL rest = FALSE; 36795 int idx, has_initializer; 36796 36797 if (s->token.val == TOK_ELLIPSIS) { 36798 if (func_type == JS_PARSE_FUNC_SETTER) 36799 goto fail_accessor; 36800 fd->has_simple_parameter_list = FALSE; 36801 rest = TRUE; 36802 if (next_token(s)) 36803 goto fail; 36804 } 36805 if (s->token.val == '[' || s->token.val == '{') { 36806 fd->has_simple_parameter_list = FALSE; 36807 if (rest) { 36808 emit_op(s, OP_rest); 36809 emit_u16(s, fd->arg_count); 36810 } else { 36811 /* unnamed arg for destructuring */ 36812 idx = add_arg(ctx, fd, JS_ATOM_NULL); 36813 emit_op(s, OP_get_arg); 36814 emit_u16(s, idx); 36815 } 36816 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE, FALSE); 36817 if (has_initializer < 0) 36818 goto fail; 36819 if (has_initializer) 36820 has_opt_arg = TRUE; 36821 if (!has_opt_arg) 36822 fd->defined_arg_count++; 36823 } else if (s->token.val == TOK_IDENT) { 36824 if (s->token.u.ident.is_reserved) { 36825 js_parse_error_reserved_identifier(s); 36826 goto fail; 36827 } 36828 name = s->token.u.ident.atom; 36829 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { 36830 js_parse_error_reserved_identifier(s); 36831 goto fail; 36832 } 36833 if (fd->has_parameter_expressions) { 36834 if (js_parse_check_duplicate_parameter(s, name)) 36835 goto fail; 36836 if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) 36837 goto fail; 36838 } 36839 /* XXX: could avoid allocating an argument if rest is true */ 36840 idx = add_arg(ctx, fd, name); 36841 if (idx < 0) 36842 goto fail; 36843 if (next_token(s)) 36844 goto fail; 36845 if (rest) { 36846 emit_op(s, OP_rest); 36847 emit_u16(s, idx); 36848 if (fd->has_parameter_expressions) { 36849 emit_op(s, OP_dup); 36850 emit_op(s, OP_scope_put_var_init); 36851 emit_atom(s, name); 36852 emit_u16(s, fd->scope_level); 36853 } 36854 emit_op(s, OP_put_arg); 36855 emit_u16(s, idx); 36856 fd->has_simple_parameter_list = FALSE; 36857 has_opt_arg = TRUE; 36858 } else if (s->token.val == '=') { 36859 int label; 36860 36861 fd->has_simple_parameter_list = FALSE; 36862 has_opt_arg = TRUE; 36863 36864 if (next_token(s)) 36865 goto fail; 36866 36867 label = new_label(s); 36868 emit_op(s, OP_get_arg); 36869 emit_u16(s, idx); 36870 emit_op(s, OP_dup); 36871 emit_op(s, OP_undefined); 36872 emit_op(s, OP_strict_eq); 36873 emit_goto(s, OP_if_false, label); 36874 emit_op(s, OP_drop); 36875 if (js_parse_assign_expr(s)) 36876 goto fail; 36877 set_object_name(s, name); 36878 emit_op(s, OP_dup); 36879 emit_op(s, OP_put_arg); 36880 emit_u16(s, idx); 36881 emit_label(s, label); 36882 emit_op(s, OP_scope_put_var_init); 36883 emit_atom(s, name); 36884 emit_u16(s, fd->scope_level); 36885 } else { 36886 if (!has_opt_arg) { 36887 fd->defined_arg_count++; 36888 } 36889 if (fd->has_parameter_expressions) { 36890 /* copy the argument to the argument scope */ 36891 emit_op(s, OP_get_arg); 36892 emit_u16(s, idx); 36893 emit_op(s, OP_scope_put_var_init); 36894 emit_atom(s, name); 36895 emit_u16(s, fd->scope_level); 36896 } 36897 } 36898 } else { 36899 js_parse_error(s, "missing formal parameter"); 36900 goto fail; 36901 } 36902 if (rest && s->token.val != ')') { 36903 js_parse_expect(s, ')'); 36904 goto fail; 36905 } 36906 if (s->token.val == ')') 36907 break; 36908 if (js_parse_expect(s, ',')) 36909 goto fail; 36910 } 36911 if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || 36912 (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { 36913 fail_accessor: 36914 js_parse_error(s, "invalid number of arguments for getter or setter"); 36915 goto fail; 36916 } 36917 } 36918 36919 if (fd->has_parameter_expressions) { 36920 int idx; 36921 36922 /* Copy the variables in the argument scope to the variable 36923 scope (see FunctionDeclarationInstantiation() in spec). The 36924 normal arguments are already present, so no need to copy 36925 them. */ 36926 idx = fd->scopes[fd->scope_level].first; 36927 while (idx >= 0) { 36928 JSVarDef *vd = &fd->vars[idx]; 36929 if (vd->scope_level != fd->scope_level) 36930 break; 36931 if (find_var(ctx, fd, vd->var_name) < 0) { 36932 if (add_var(ctx, fd, vd->var_name) < 0) 36933 goto fail; 36934 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ 36935 emit_op(s, OP_scope_get_var); 36936 emit_atom(s, vd->var_name); 36937 emit_u16(s, fd->scope_level); 36938 emit_op(s, OP_scope_put_var); 36939 emit_atom(s, vd->var_name); 36940 emit_u16(s, 0); 36941 } 36942 idx = vd->scope_next; 36943 } 36944 36945 /* the argument scope has no parent, hence we don't use pop_scope(s) */ 36946 emit_op(s, OP_leave_scope); 36947 emit_u16(s, fd->scope_level); 36948 36949 /* set the variable scope as the current scope */ 36950 fd->scope_level = 0; 36951 fd->scope_first = fd->scopes[fd->scope_level].first; 36952 } 36953 36954 if (next_token(s)) 36955 goto fail; 36956 36957 /* generator function: yield after the parameters are evaluated */ 36958 if (func_kind == JS_FUNC_GENERATOR || 36959 func_kind == JS_FUNC_ASYNC_GENERATOR) 36960 emit_op(s, OP_initial_yield); 36961 36962 /* in generators, yield expression is forbidden during the parsing 36963 of the arguments */ 36964 fd->in_function_body = TRUE; 36965 push_scope(s); /* enter body scope */ 36966 fd->body_scope = fd->scope_level; 36967 36968 if (s->token.val == TOK_ARROW && func_type == JS_PARSE_FUNC_ARROW) { 36969 if (next_token(s)) 36970 goto fail; 36971 36972 if (s->token.val != '{') { 36973 if (js_parse_function_check_names(s, fd, func_name)) 36974 goto fail; 36975 36976 if (js_parse_assign_expr(s)) 36977 goto fail; 36978 36979 if (func_kind != JS_FUNC_NORMAL) 36980 emit_op(s, OP_return_async); 36981 else 36982 emit_op(s, OP_return); 36983 36984 if (!fd->strip_source) { 36985 /* save the function source code */ 36986 /* the end of the function source code is after the last 36987 token of the function source stored into s->last_ptr */ 36988 fd->source_len = s->last_ptr - ptr; 36989 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); 36990 if (!fd->source) 36991 goto fail; 36992 } 36993 goto done; 36994 } 36995 } 36996 36997 if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { 36998 if (js_parse_expect(s, '{')) 36999 goto fail; 37000 } 37001 37002 if (js_parse_directives(s)) 37003 goto fail; 37004 37005 /* in strict_mode, check function and argument names */ 37006 if (js_parse_function_check_names(s, fd, func_name)) 37007 goto fail; 37008 37009 while (s->token.val != '}') { 37010 if (js_parse_source_element(s)) 37011 goto fail; 37012 } 37013 if (!fd->strip_source) { 37014 /* save the function source code */ 37015 fd->source_len = s->buf_ptr - ptr; 37016 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); 37017 if (!fd->source) 37018 goto fail; 37019 } 37020 37021 if (next_token(s)) { 37022 /* consume the '}' */ 37023 goto fail; 37024 } 37025 37026 /* in case there is no return, add one */ 37027 if (js_is_live_code(s)) { 37028 emit_return(s, FALSE); 37029 } 37030 done: 37031 s->cur_func = fd->parent; 37032 37033 /* Reparse identifiers after the function is terminated so that 37034 the token is parsed in the englobing function. It could be done 37035 by just using next_token() here for normal functions, but it is 37036 necessary for arrow functions with an expression body. */ 37037 reparse_ident_token(s); 37038 37039 /* create the function object */ 37040 { 37041 int idx; 37042 JSAtom func_name = fd->func_name; 37043 37044 /* the real object will be set at the end of the compilation */ 37045 idx = cpool_add(s, JS_NULL); 37046 fd->parent_cpool_idx = idx; 37047 37048 if (is_expr) { 37049 /* for constructors, no code needs to be generated here */ 37050 if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR && 37051 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { 37052 /* OP_fclosure creates the function object from the bytecode 37053 and adds the scope information */ 37054 emit_op(s, OP_fclosure); 37055 emit_u32(s, idx); 37056 if (func_name == JS_ATOM_NULL) { 37057 emit_op(s, OP_set_name); 37058 emit_u32(s, JS_ATOM_NULL); 37059 } 37060 } 37061 } else if (func_type == JS_PARSE_FUNC_VAR) { 37062 emit_op(s, OP_fclosure); 37063 emit_u32(s, idx); 37064 if (create_func_var) { 37065 if (s->cur_func->is_global_var) { 37066 JSGlobalVar *hf; 37067 /* the global variable must be defined at the start of the 37068 function */ 37069 hf = add_global_var(ctx, s->cur_func, func_name); 37070 if (!hf) 37071 goto fail; 37072 /* it is considered as defined at the top level 37073 (needed for annex B.3.3.4 and B.3.3.5 37074 checks) */ 37075 hf->scope_level = 0; 37076 hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0); 37077 /* store directly into global var, bypass lexical scope */ 37078 emit_op(s, OP_dup); 37079 emit_op(s, OP_scope_put_var); 37080 emit_atom(s, func_name); 37081 emit_u16(s, 0); 37082 } else { 37083 /* do not call define_var to bypass lexical scope check */ 37084 func_idx = find_var(ctx, s->cur_func, func_name); 37085 if (func_idx < 0) { 37086 func_idx = add_var(ctx, s->cur_func, func_name); 37087 if (func_idx < 0) 37088 goto fail; 37089 } 37090 /* store directly into local var, bypass lexical catch scope */ 37091 emit_op(s, OP_dup); 37092 emit_op(s, OP_scope_put_var); 37093 emit_atom(s, func_name); 37094 emit_u16(s, 0); 37095 } 37096 } 37097 if (lexical_func_idx >= 0) { 37098 /* lexical variable will be initialized upon entering scope */ 37099 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; 37100 emit_op(s, OP_drop); 37101 } else { 37102 /* store function object into its lexical name */ 37103 /* XXX: could use OP_put_loc directly */ 37104 emit_op(s, OP_scope_put_var_init); 37105 emit_atom(s, func_name); 37106 emit_u16(s, s->cur_func->scope_level); 37107 } 37108 } else { 37109 if (!s->cur_func->is_global_var) { 37110 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR); 37111 37112 if (var_idx < 0) 37113 goto fail; 37114 /* the variable will be assigned at the top of the function */ 37115 if (var_idx & ARGUMENT_VAR_OFFSET) { 37116 s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; 37117 } else { 37118 s->cur_func->vars[var_idx].func_pool_idx = idx; 37119 } 37120 } else { 37121 JSAtom func_var_name; 37122 JSGlobalVar *hf; 37123 if (func_name == JS_ATOM_NULL) 37124 func_var_name = JS_ATOM__default_; /* export default */ 37125 else 37126 func_var_name = func_name; 37127 /* the variable will be assigned at the top of the function */ 37128 hf = add_global_var(ctx, s->cur_func, func_var_name); 37129 if (!hf) 37130 goto fail; 37131 hf->cpool_idx = idx; 37132 if (export_flag != JS_PARSE_EXPORT_NONE) { 37133 if (!add_export_entry(s, s->cur_func->module, func_var_name, 37134 export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) 37135 goto fail; 37136 } 37137 } 37138 } 37139 } 37140 return 0; 37141 fail: 37142 s->cur_func = fd->parent; 37143 js_free_function_def(ctx, fd); 37144 if (pfd) 37145 *pfd = NULL; 37146 return -1; 37147 } 37148 37149 static __exception int js_parse_function_decl(JSParseState *s, 37150 JSParseFunctionEnum func_type, 37151 JSFunctionKindEnum func_kind, 37152 JSAtom func_name, 37153 const uint8_t *ptr) 37154 { 37155 return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, 37156 JS_PARSE_EXPORT_NONE, NULL); 37157 } 37158 37159 static __exception int js_parse_program(JSParseState *s) 37160 { 37161 JSFunctionDef *fd = s->cur_func; 37162 int idx; 37163 37164 if (next_token(s)) 37165 return -1; 37166 37167 if (js_parse_directives(s)) 37168 return -1; 37169 37170 fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) || 37171 (fd->eval_type == JS_EVAL_TYPE_MODULE) || 37172 !(fd->js_mode & JS_MODE_STRICT); 37173 37174 if (!s->is_module) { 37175 /* hidden variable for the return value */ 37176 fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_); 37177 if (idx < 0) 37178 return -1; 37179 } 37180 37181 while (s->token.val != TOK_EOF) { 37182 if (js_parse_source_element(s)) 37183 return -1; 37184 } 37185 37186 if (!s->is_module) { 37187 /* return the value of the hidden variable eval_ret_idx */ 37188 if (fd->func_kind == JS_FUNC_ASYNC) { 37189 /* wrap the return value in an object so that promises can 37190 be safely returned */ 37191 emit_op(s, OP_object); 37192 emit_op(s, OP_dup); 37193 37194 emit_op(s, OP_get_loc); 37195 emit_u16(s, fd->eval_ret_idx); 37196 37197 emit_op(s, OP_put_field); 37198 emit_atom(s, JS_ATOM_value); 37199 } else { 37200 emit_op(s, OP_get_loc); 37201 emit_u16(s, fd->eval_ret_idx); 37202 } 37203 emit_return(s, TRUE); 37204 } else { 37205 emit_return(s, FALSE); 37206 } 37207 37208 return 0; 37209 } 37210 37211 static void js_parse_init(JSContext *ctx, JSParseState *s, 37212 const char *input, size_t input_len, 37213 const char *filename) 37214 { 37215 memset(s, 0, sizeof(*s)); 37216 s->ctx = ctx; 37217 s->filename = filename; 37218 s->buf_start = s->buf_ptr = (const uint8_t *)input; 37219 s->buf_end = s->buf_ptr + input_len; 37220 s->token.val = ' '; 37221 s->token.ptr = s->buf_ptr; 37222 37223 s->get_line_col_cache.ptr = s->buf_start; 37224 s->get_line_col_cache.buf_start = s->buf_start; 37225 s->get_line_col_cache.line_num = 0; 37226 s->get_line_col_cache.col_num = 0; 37227 } 37228 37229 static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, 37230 JSValueConst this_obj, 37231 JSVarRef **var_refs, JSStackFrame *sf) 37232 { 37233 JSValue ret_val; 37234 uint32_t tag; 37235 37236 tag = JS_VALUE_GET_TAG(fun_obj); 37237 if (tag == JS_TAG_FUNCTION_BYTECODE) { 37238 fun_obj = js_closure(ctx, fun_obj, var_refs, sf, TRUE); 37239 if (JS_IsException(fun_obj)) 37240 return JS_EXCEPTION; 37241 ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL); 37242 } else if (tag == JS_TAG_MODULE) { 37243 JSModuleDef *m; 37244 m = JS_VALUE_GET_PTR(fun_obj); 37245 /* the module refcount should be >= 2 */ 37246 JS_FreeValue(ctx, fun_obj); 37247 if (js_create_module_function(ctx, m) < 0) 37248 goto fail; 37249 if (js_link_module(ctx, m) < 0) 37250 goto fail; 37251 ret_val = js_evaluate_module(ctx, m); 37252 if (JS_IsException(ret_val)) { 37253 fail: 37254 return JS_EXCEPTION; 37255 } 37256 } else { 37257 JS_FreeValue(ctx, fun_obj); 37258 ret_val = JS_ThrowTypeError(ctx, "bytecode function expected"); 37259 } 37260 return ret_val; 37261 } 37262 37263 JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) 37264 { 37265 return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); 37266 } 37267 37268 /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ 37269 static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 37270 const char *input, size_t input_len, 37271 const char *filename, int flags, int scope_idx) 37272 { 37273 JSParseState s1, *s = &s1; 37274 int err, js_mode, eval_type; 37275 JSValue fun_obj, ret_val; 37276 JSStackFrame *sf; 37277 JSVarRef **var_refs; 37278 JSFunctionBytecode *b; 37279 JSFunctionDef *fd; 37280 JSModuleDef *m; 37281 37282 js_parse_init(ctx, s, input, input_len, filename); 37283 skip_shebang(&s->buf_ptr, s->buf_end); 37284 37285 eval_type = flags & JS_EVAL_TYPE_MASK; 37286 m = NULL; 37287 if (eval_type == JS_EVAL_TYPE_DIRECT) { 37288 JSObject *p; 37289 sf = ctx->rt->current_stack_frame; 37290 assert(sf != NULL); 37291 assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); 37292 p = JS_VALUE_GET_OBJ(sf->cur_func); 37293 assert(js_class_has_bytecode(p->class_id)); 37294 b = p->u.func.function_bytecode; 37295 var_refs = p->u.func.var_refs; 37296 js_mode = b->js_mode; 37297 } else { 37298 sf = NULL; 37299 b = NULL; 37300 var_refs = NULL; 37301 js_mode = 0; 37302 if (flags & JS_EVAL_FLAG_STRICT) 37303 js_mode |= JS_MODE_STRICT; 37304 if (eval_type == JS_EVAL_TYPE_MODULE) { 37305 JSAtom module_name = JS_NewAtom(ctx, filename); 37306 if (module_name == JS_ATOM_NULL) 37307 return JS_EXCEPTION; 37308 m = js_new_module_def(ctx, module_name); 37309 if (!m) 37310 return JS_EXCEPTION; 37311 js_mode |= JS_MODE_STRICT; 37312 } 37313 } 37314 fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 37315 s->buf_start, &s->get_line_col_cache); 37316 if (!fd) 37317 goto fail1; 37318 s->cur_func = fd; 37319 fd->eval_type = eval_type; 37320 fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); 37321 if (eval_type == JS_EVAL_TYPE_DIRECT) { 37322 fd->new_target_allowed = b->new_target_allowed; 37323 fd->super_call_allowed = b->super_call_allowed; 37324 fd->super_allowed = b->super_allowed; 37325 fd->arguments_allowed = b->arguments_allowed; 37326 } else { 37327 fd->new_target_allowed = FALSE; 37328 fd->super_call_allowed = FALSE; 37329 fd->super_allowed = FALSE; 37330 fd->arguments_allowed = TRUE; 37331 } 37332 fd->js_mode = js_mode; 37333 fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_); 37334 if (b) { 37335 if (add_closure_variables(ctx, fd, b, scope_idx)) { 37336 goto fail; 37337 } 37338 } 37339 fd->module = m; 37340 if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) { 37341 fd->in_function_body = TRUE; 37342 fd->func_kind = JS_FUNC_ASYNC; 37343 } 37344 s->is_module = (m != NULL); 37345 s->allow_html_comments = !s->is_module; 37346 37347 push_scope(s); /* body scope */ 37348 fd->body_scope = fd->scope_level; 37349 37350 err = js_parse_program(s); 37351 if (err) { 37352 fail: 37353 free_token(s, &s->token); 37354 js_free_function_def(ctx, fd); 37355 goto fail1; 37356 } 37357 37358 if (m != NULL) 37359 m->has_tla = fd->has_await; 37360 37361 /* create the function object and all the enclosed functions */ 37362 fun_obj = js_create_function(ctx, fd); 37363 if (JS_IsException(fun_obj)) 37364 goto fail1; 37365 /* Could add a flag to avoid resolution if necessary */ 37366 if (m) { 37367 m->func_obj = fun_obj; 37368 if (js_resolve_module(ctx, m) < 0) 37369 goto fail1; 37370 fun_obj = JS_NewModuleValue(ctx, m); 37371 } 37372 if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { 37373 ret_val = fun_obj; 37374 } else { 37375 ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf); 37376 } 37377 return ret_val; 37378 fail1: 37379 /* XXX: should free all the unresolved dependencies */ 37380 if (m) 37381 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); 37382 return JS_EXCEPTION; 37383 } 37384 37385 /* the indirection is needed to make 'eval' optional */ 37386 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 37387 const char *input, size_t input_len, 37388 const char *filename, int flags, int scope_idx) 37389 { 37390 BOOL backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); 37391 int saved_js_mode = 0; 37392 JSValue ret; 37393 37394 if (unlikely(!ctx->eval_internal)) { 37395 return JS_ThrowTypeError(ctx, "eval is not supported"); 37396 } 37397 if (backtrace_barrier && ctx->rt->current_stack_frame) { 37398 saved_js_mode = ctx->rt->current_stack_frame->js_mode; 37399 ctx->rt->current_stack_frame->js_mode |= JS_MODE_BACKTRACE_BARRIER; 37400 } 37401 ret = ctx->eval_internal(ctx, this_obj, input, input_len, filename, 37402 flags, scope_idx); 37403 if (backtrace_barrier && ctx->rt->current_stack_frame) 37404 ctx->rt->current_stack_frame->js_mode = saved_js_mode; 37405 return ret; 37406 } 37407 37408 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, 37409 JSValueConst val, int flags, int scope_idx) 37410 { 37411 JSValue ret; 37412 const char *str; 37413 size_t len; 37414 37415 if (!JS_IsString(val)) 37416 return JS_DupValue(ctx, val); 37417 str = JS_ToCStringLen(ctx, &len, val); 37418 if (!str) 37419 return JS_EXCEPTION; 37420 ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx); 37421 JS_FreeCString(ctx, str); 37422 return ret; 37423 } 37424 37425 JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, 37426 const char *input, size_t input_len, 37427 const char *filename, int eval_flags) 37428 { 37429 int eval_type = eval_flags & JS_EVAL_TYPE_MASK; 37430 JSValue ret; 37431 37432 assert(eval_type == JS_EVAL_TYPE_GLOBAL || 37433 eval_type == JS_EVAL_TYPE_MODULE); 37434 ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, 37435 eval_flags, -1); 37436 return ret; 37437 } 37438 37439 JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, 37440 const char *filename, int eval_flags) 37441 { 37442 return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 37443 eval_flags); 37444 } 37445 37446 int JS_ResolveModule(JSContext *ctx, JSValueConst obj) 37447 { 37448 if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { 37449 JSModuleDef *m = JS_VALUE_GET_PTR(obj); 37450 if (js_resolve_module(ctx, m) < 0) { 37451 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); 37452 return -1; 37453 } 37454 } 37455 return 0; 37456 } 37457 37458 /*******************************************************************/ 37459 /* object list */ 37460 37461 typedef struct { 37462 JSObject *obj; 37463 uint32_t hash_next; /* -1 if no next entry */ 37464 } JSObjectListEntry; 37465 37466 /* XXX: reuse it to optimize weak references */ 37467 typedef struct { 37468 JSObjectListEntry *object_tab; 37469 int object_count; 37470 int object_size; 37471 uint32_t *hash_table; 37472 uint32_t hash_size; 37473 } JSObjectList; 37474 37475 static void js_object_list_init(JSObjectList *s) 37476 { 37477 memset(s, 0, sizeof(*s)); 37478 } 37479 37480 static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) 37481 { 37482 return ((uintptr_t)p * 3163) & (hash_size - 1); 37483 } 37484 37485 static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, 37486 uint32_t new_hash_size) 37487 { 37488 JSObjectListEntry *e; 37489 uint32_t i, h, *new_hash_table; 37490 37491 new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); 37492 if (!new_hash_table) 37493 return -1; 37494 js_free(ctx, s->hash_table); 37495 s->hash_table = new_hash_table; 37496 s->hash_size = new_hash_size; 37497 37498 for(i = 0; i < s->hash_size; i++) { 37499 s->hash_table[i] = -1; 37500 } 37501 for(i = 0; i < s->object_count; i++) { 37502 e = &s->object_tab[i]; 37503 h = js_object_list_get_hash(e->obj, s->hash_size); 37504 e->hash_next = s->hash_table[h]; 37505 s->hash_table[h] = i; 37506 } 37507 return 0; 37508 } 37509 37510 /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if 37511 memory error */ 37512 static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) 37513 { 37514 JSObjectListEntry *e; 37515 uint32_t h, new_hash_size; 37516 37517 if (js_resize_array(ctx, (void *)&s->object_tab, 37518 sizeof(s->object_tab[0]), 37519 &s->object_size, s->object_count + 1)) 37520 return -1; 37521 if (unlikely((s->object_count + 1) >= s->hash_size)) { 37522 new_hash_size = max_uint32(s->hash_size, 4); 37523 while (new_hash_size <= s->object_count) 37524 new_hash_size *= 2; 37525 if (js_object_list_resize_hash(ctx, s, new_hash_size)) 37526 return -1; 37527 } 37528 e = &s->object_tab[s->object_count++]; 37529 h = js_object_list_get_hash(obj, s->hash_size); 37530 e->obj = obj; 37531 e->hash_next = s->hash_table[h]; 37532 s->hash_table[h] = s->object_count - 1; 37533 return 0; 37534 } 37535 37536 /* return -1 if not present or the object index */ 37537 static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) 37538 { 37539 JSObjectListEntry *e; 37540 uint32_t h, p; 37541 37542 /* must test empty size because there is no hash table */ 37543 if (s->object_count == 0) 37544 return -1; 37545 h = js_object_list_get_hash(obj, s->hash_size); 37546 p = s->hash_table[h]; 37547 while (p != -1) { 37548 e = &s->object_tab[p]; 37549 if (e->obj == obj) 37550 return p; 37551 p = e->hash_next; 37552 } 37553 return -1; 37554 } 37555 37556 static void js_object_list_end(JSContext *ctx, JSObjectList *s) 37557 { 37558 js_free(ctx, s->object_tab); 37559 js_free(ctx, s->hash_table); 37560 } 37561 37562 /*******************************************************************/ 37563 /* binary object writer & reader */ 37564 37565 typedef enum BCTagEnum { 37566 BC_TAG_NULL = 1, 37567 BC_TAG_UNDEFINED, 37568 BC_TAG_BOOL_FALSE, 37569 BC_TAG_BOOL_TRUE, 37570 BC_TAG_INT32, 37571 BC_TAG_FLOAT64, 37572 BC_TAG_STRING, 37573 BC_TAG_OBJECT, 37574 BC_TAG_ARRAY, 37575 BC_TAG_BIG_INT, 37576 BC_TAG_TEMPLATE_OBJECT, 37577 BC_TAG_FUNCTION_BYTECODE, 37578 BC_TAG_MODULE, 37579 BC_TAG_TYPED_ARRAY, 37580 BC_TAG_ARRAY_BUFFER, 37581 BC_TAG_SHARED_ARRAY_BUFFER, 37582 BC_TAG_DATE, 37583 BC_TAG_OBJECT_VALUE, 37584 BC_TAG_OBJECT_REFERENCE, 37585 } BCTagEnum; 37586 37587 #define BC_VERSION 5 37588 37589 typedef struct BCWriterState { 37590 JSContext *ctx; 37591 DynBuf dbuf; 37592 BOOL allow_bytecode : 8; 37593 BOOL allow_sab : 8; 37594 BOOL allow_reference : 8; 37595 uint32_t first_atom; 37596 uint32_t *atom_to_idx; 37597 int atom_to_idx_size; 37598 JSAtom *idx_to_atom; 37599 int idx_to_atom_count; 37600 int idx_to_atom_size; 37601 uint8_t **sab_tab; 37602 int sab_tab_len; 37603 int sab_tab_size; 37604 /* list of referenced objects (used if allow_reference = TRUE) */ 37605 JSObjectList object_list; 37606 } BCWriterState; 37607 37608 #ifdef DUMP_READ_OBJECT 37609 static const char * const bc_tag_str[] = { 37610 "invalid", 37611 "null", 37612 "undefined", 37613 "false", 37614 "true", 37615 "int32", 37616 "float64", 37617 "string", 37618 "object", 37619 "array", 37620 "bigint", 37621 "template", 37622 "function", 37623 "module", 37624 "TypedArray", 37625 "ArrayBuffer", 37626 "SharedArrayBuffer", 37627 "Date", 37628 "ObjectValue", 37629 "ObjectReference", 37630 }; 37631 #endif 37632 37633 static inline BOOL is_be(void) 37634 { 37635 union { 37636 uint16_t a; 37637 uint8_t b; 37638 } u = {0x100}; 37639 return u.b; 37640 } 37641 37642 static void bc_put_u8(BCWriterState *s, uint8_t v) 37643 { 37644 dbuf_putc(&s->dbuf, v); 37645 } 37646 37647 static void bc_put_u16(BCWriterState *s, uint16_t v) 37648 { 37649 if (is_be()) 37650 v = bswap16(v); 37651 dbuf_put_u16(&s->dbuf, v); 37652 } 37653 37654 static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) 37655 { 37656 if (is_be()) 37657 v = bswap32(v); 37658 dbuf_put_u32(&s->dbuf, v); 37659 } 37660 37661 static void bc_put_u64(BCWriterState *s, uint64_t v) 37662 { 37663 if (is_be()) 37664 v = bswap64(v); 37665 dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); 37666 } 37667 37668 static void bc_put_leb128(BCWriterState *s, uint32_t v) 37669 { 37670 dbuf_put_leb128(&s->dbuf, v); 37671 } 37672 37673 static void bc_put_sleb128(BCWriterState *s, int32_t v) 37674 { 37675 dbuf_put_sleb128(&s->dbuf, v); 37676 } 37677 37678 static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n) 37679 { 37680 *pflags = *pflags | (val << *pidx); 37681 *pidx += n; 37682 } 37683 37684 static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) 37685 { 37686 uint32_t v; 37687 37688 if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { 37689 *pres = atom; 37690 return 0; 37691 } 37692 atom -= s->first_atom; 37693 if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { 37694 *pres = s->atom_to_idx[atom]; 37695 return 0; 37696 } 37697 if (atom >= s->atom_to_idx_size) { 37698 int old_size, i; 37699 old_size = s->atom_to_idx_size; 37700 if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, 37701 sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, 37702 atom + 1)) 37703 return -1; 37704 /* XXX: could add a specific js_resize_array() function to do it */ 37705 for(i = old_size; i < s->atom_to_idx_size; i++) 37706 s->atom_to_idx[i] = 0; 37707 } 37708 if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, 37709 sizeof(s->idx_to_atom[0]), 37710 &s->idx_to_atom_size, s->idx_to_atom_count + 1)) 37711 goto fail; 37712 37713 v = s->idx_to_atom_count++; 37714 s->idx_to_atom[v] = atom + s->first_atom; 37715 v += s->first_atom; 37716 s->atom_to_idx[atom] = v; 37717 *pres = v; 37718 return 0; 37719 fail: 37720 *pres = 0; 37721 return -1; 37722 } 37723 37724 static int bc_put_atom(BCWriterState *s, JSAtom atom) 37725 { 37726 uint32_t v; 37727 37728 if (__JS_AtomIsTaggedInt(atom)) { 37729 v = (__JS_AtomToUInt32(atom) << 1) | 1; 37730 } else { 37731 if (bc_atom_to_idx(s, &v, atom)) 37732 return -1; 37733 v <<= 1; 37734 } 37735 bc_put_leb128(s, v); 37736 return 0; 37737 } 37738 37739 static void bc_byte_swap(uint8_t *bc_buf, int bc_len) 37740 { 37741 int pos, len, op, fmt; 37742 37743 pos = 0; 37744 while (pos < bc_len) { 37745 op = bc_buf[pos]; 37746 len = short_opcode_info(op).size; 37747 fmt = short_opcode_info(op).fmt; 37748 switch(fmt) { 37749 case OP_FMT_u16: 37750 case OP_FMT_i16: 37751 case OP_FMT_label16: 37752 case OP_FMT_npop: 37753 case OP_FMT_loc: 37754 case OP_FMT_arg: 37755 case OP_FMT_var_ref: 37756 put_u16(bc_buf + pos + 1, 37757 bswap16(get_u16(bc_buf + pos + 1))); 37758 break; 37759 case OP_FMT_i32: 37760 case OP_FMT_u32: 37761 case OP_FMT_const: 37762 case OP_FMT_label: 37763 case OP_FMT_atom: 37764 case OP_FMT_atom_u8: 37765 put_u32(bc_buf + pos + 1, 37766 bswap32(get_u32(bc_buf + pos + 1))); 37767 break; 37768 case OP_FMT_atom_u16: 37769 case OP_FMT_label_u16: 37770 put_u32(bc_buf + pos + 1, 37771 bswap32(get_u32(bc_buf + pos + 1))); 37772 put_u16(bc_buf + pos + 1 + 4, 37773 bswap16(get_u16(bc_buf + pos + 1 + 4))); 37774 break; 37775 case OP_FMT_atom_label_u8: 37776 case OP_FMT_atom_label_u16: 37777 put_u32(bc_buf + pos + 1, 37778 bswap32(get_u32(bc_buf + pos + 1))); 37779 put_u32(bc_buf + pos + 1 + 4, 37780 bswap32(get_u32(bc_buf + pos + 1 + 4))); 37781 if (fmt == OP_FMT_atom_label_u16) { 37782 put_u16(bc_buf + pos + 1 + 4 + 4, 37783 bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); 37784 } 37785 break; 37786 case OP_FMT_npop_u16: 37787 put_u16(bc_buf + pos + 1, 37788 bswap16(get_u16(bc_buf + pos + 1))); 37789 put_u16(bc_buf + pos + 1 + 2, 37790 bswap16(get_u16(bc_buf + pos + 1 + 2))); 37791 break; 37792 default: 37793 break; 37794 } 37795 pos += len; 37796 } 37797 } 37798 37799 static int JS_WriteFunctionBytecode(BCWriterState *s, 37800 const uint8_t *bc_buf1, int bc_len) 37801 { 37802 int pos, len, op; 37803 JSAtom atom; 37804 uint8_t *bc_buf; 37805 uint32_t val; 37806 37807 bc_buf = js_malloc(s->ctx, bc_len); 37808 if (!bc_buf) 37809 return -1; 37810 memcpy(bc_buf, bc_buf1, bc_len); 37811 37812 pos = 0; 37813 while (pos < bc_len) { 37814 op = bc_buf[pos]; 37815 len = short_opcode_info(op).size; 37816 switch(short_opcode_info(op).fmt) { 37817 case OP_FMT_atom: 37818 case OP_FMT_atom_u8: 37819 case OP_FMT_atom_u16: 37820 case OP_FMT_atom_label_u8: 37821 case OP_FMT_atom_label_u16: 37822 atom = get_u32(bc_buf + pos + 1); 37823 if (bc_atom_to_idx(s, &val, atom)) 37824 goto fail; 37825 put_u32(bc_buf + pos + 1, val); 37826 break; 37827 default: 37828 break; 37829 } 37830 pos += len; 37831 } 37832 37833 if (is_be()) 37834 bc_byte_swap(bc_buf, bc_len); 37835 37836 dbuf_put(&s->dbuf, bc_buf, bc_len); 37837 37838 js_free(s->ctx, bc_buf); 37839 return 0; 37840 fail: 37841 js_free(s->ctx, bc_buf); 37842 return -1; 37843 } 37844 37845 static void JS_WriteString(BCWriterState *s, JSString *p) 37846 { 37847 int i; 37848 bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); 37849 if (p->is_wide_char) { 37850 for(i = 0; i < p->len; i++) 37851 bc_put_u16(s, p->u.str16[i]); 37852 } else { 37853 dbuf_put(&s->dbuf, p->u.str8, p->len); 37854 } 37855 } 37856 37857 static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj) 37858 { 37859 JSBigIntBuf buf; 37860 JSBigInt *p; 37861 uint32_t len, i; 37862 js_limb_t v, b; 37863 int shift; 37864 37865 bc_put_u8(s, BC_TAG_BIG_INT); 37866 37867 if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT) 37868 p = js_bigint_set_short(&buf, obj); 37869 else 37870 p = JS_VALUE_GET_PTR(obj); 37871 if (p->len == 1 && p->tab[0] == 0) { 37872 /* zero case */ 37873 len = 0; 37874 } else { 37875 /* compute the length of the two's complement representation 37876 in bytes */ 37877 len = p->len * (JS_LIMB_BITS / 8); 37878 v = p->tab[p->len - 1]; 37879 shift = JS_LIMB_BITS - 8; 37880 while (shift > 0) { 37881 b = (v >> shift) & 0xff; 37882 if (b != 0x00 && b != 0xff) 37883 break; 37884 if ((b & 1) != ((v >> (shift - 1)) & 1)) 37885 break; 37886 shift -= 8; 37887 len--; 37888 } 37889 } 37890 bc_put_leb128(s, len); 37891 if (len > 0) { 37892 for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) { 37893 #if JS_LIMB_BITS == 32 37894 bc_put_u32(s, p->tab[i]); 37895 #else 37896 bc_put_u64(s, p->tab[i]); 37897 #endif 37898 } 37899 for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) { 37900 bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff); 37901 } 37902 } 37903 return 0; 37904 } 37905 37906 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); 37907 37908 static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) 37909 { 37910 JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); 37911 uint32_t flags; 37912 int idx, i; 37913 37914 bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); 37915 flags = idx = 0; 37916 bc_set_flags(&flags, &idx, b->has_prototype, 1); 37917 bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); 37918 bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); 37919 bc_set_flags(&flags, &idx, b->need_home_object, 1); 37920 bc_set_flags(&flags, &idx, b->func_kind, 2); 37921 bc_set_flags(&flags, &idx, b->new_target_allowed, 1); 37922 bc_set_flags(&flags, &idx, b->super_call_allowed, 1); 37923 bc_set_flags(&flags, &idx, b->super_allowed, 1); 37924 bc_set_flags(&flags, &idx, b->arguments_allowed, 1); 37925 bc_set_flags(&flags, &idx, b->has_debug, 1); 37926 bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1); 37927 assert(idx <= 16); 37928 bc_put_u16(s, flags); 37929 bc_put_u8(s, b->js_mode); 37930 bc_put_atom(s, b->func_name); 37931 37932 bc_put_leb128(s, b->arg_count); 37933 bc_put_leb128(s, b->var_count); 37934 bc_put_leb128(s, b->defined_arg_count); 37935 bc_put_leb128(s, b->stack_size); 37936 bc_put_leb128(s, b->var_ref_count); 37937 bc_put_leb128(s, b->closure_var_count); 37938 bc_put_leb128(s, b->cpool_count); 37939 bc_put_leb128(s, b->byte_code_len); 37940 if (b->vardefs) { 37941 bc_put_leb128(s, b->arg_count + b->var_count); 37942 for(i = 0; i < b->arg_count + b->var_count; i++) { 37943 JSBytecodeVarDef *vd = &b->vardefs[i]; 37944 bc_put_atom(s, vd->var_name); 37945 bc_put_leb128(s, vd->scope_next + 1); 37946 bc_put_leb128(s, vd->var_ref_idx); 37947 flags = idx = 0; 37948 bc_set_flags(&flags, &idx, vd->var_kind, 4); 37949 bc_set_flags(&flags, &idx, vd->is_const, 1); 37950 bc_set_flags(&flags, &idx, vd->is_lexical, 1); 37951 bc_set_flags(&flags, &idx, vd->is_captured, 1); 37952 bc_set_flags(&flags, &idx, vd->has_scope, 1); 37953 assert(idx <= 8); 37954 bc_put_u8(s, flags); 37955 } 37956 } else { 37957 bc_put_leb128(s, 0); 37958 } 37959 37960 for(i = 0; i < b->closure_var_count; i++) { 37961 JSClosureVar *cv = &b->closure_var[i]; 37962 bc_put_atom(s, cv->var_name); 37963 bc_put_leb128(s, cv->var_idx); 37964 flags = idx = 0; 37965 bc_set_flags(&flags, &idx, cv->closure_type, 3); 37966 bc_set_flags(&flags, &idx, cv->is_const, 1); 37967 bc_set_flags(&flags, &idx, cv->is_lexical, 1); 37968 bc_set_flags(&flags, &idx, cv->var_kind, 4); 37969 assert(idx <= 16); 37970 bc_put_u16(s, flags); 37971 } 37972 37973 if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) 37974 goto fail; 37975 37976 if (b->has_debug) { 37977 bc_put_atom(s, b->debug.filename); 37978 bc_put_leb128(s, b->debug.pc2line_len); 37979 dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); 37980 if (b->debug.source) { 37981 bc_put_leb128(s, b->debug.source_len); 37982 dbuf_put(&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len); 37983 } else { 37984 bc_put_leb128(s, 0); 37985 } 37986 } 37987 37988 for(i = 0; i < b->cpool_count; i++) { 37989 if (JS_WriteObjectRec(s, b->cpool[i])) 37990 goto fail; 37991 } 37992 return 0; 37993 fail: 37994 return -1; 37995 } 37996 37997 static int JS_WriteModule(BCWriterState *s, JSValueConst obj) 37998 { 37999 JSModuleDef *m = JS_VALUE_GET_PTR(obj); 38000 int i; 38001 38002 bc_put_u8(s, BC_TAG_MODULE); 38003 bc_put_atom(s, m->module_name); 38004 38005 bc_put_leb128(s, m->req_module_entries_count); 38006 for(i = 0; i < m->req_module_entries_count; i++) { 38007 JSReqModuleEntry *rme = &m->req_module_entries[i]; 38008 bc_put_atom(s, rme->module_name); 38009 if (JS_WriteObjectRec(s, rme->attributes)) 38010 goto fail; 38011 } 38012 38013 bc_put_leb128(s, m->export_entries_count); 38014 for(i = 0; i < m->export_entries_count; i++) { 38015 JSExportEntry *me = &m->export_entries[i]; 38016 bc_put_u8(s, me->export_type); 38017 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 38018 bc_put_leb128(s, me->u.local.var_idx); 38019 } else { 38020 bc_put_leb128(s, me->u.req_module_idx); 38021 bc_put_atom(s, me->local_name); 38022 } 38023 bc_put_atom(s, me->export_name); 38024 } 38025 38026 bc_put_leb128(s, m->star_export_entries_count); 38027 for(i = 0; i < m->star_export_entries_count; i++) { 38028 JSStarExportEntry *se = &m->star_export_entries[i]; 38029 bc_put_leb128(s, se->req_module_idx); 38030 } 38031 38032 bc_put_leb128(s, m->import_entries_count); 38033 for(i = 0; i < m->import_entries_count; i++) { 38034 JSImportEntry *mi = &m->import_entries[i]; 38035 bc_put_leb128(s, mi->var_idx); 38036 bc_put_u8(s, mi->is_star); 38037 bc_put_atom(s, mi->import_name); 38038 bc_put_leb128(s, mi->req_module_idx); 38039 } 38040 38041 bc_put_u8(s, m->has_tla); 38042 38043 if (JS_WriteObjectRec(s, m->func_obj)) 38044 goto fail; 38045 return 0; 38046 fail: 38047 return -1; 38048 } 38049 38050 /* XXX: be compatible with the structured clone algorithm */ 38051 static int JS_WriteArray(BCWriterState *s, JSValueConst obj) 38052 { 38053 JSContext *ctx = s->ctx; 38054 JSObject *p = JS_VALUE_GET_OBJ(obj); 38055 uint32_t i, len; 38056 int ret; 38057 BOOL is_template; 38058 JSShapeProperty *prs; 38059 JSProperty *pr; 38060 38061 if (s->allow_bytecode && !p->extensible) { 38062 /* not extensible array: we consider it is a 38063 template when we are saving bytecode */ 38064 bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); 38065 is_template = TRUE; 38066 } else { 38067 bc_put_u8(s, BC_TAG_ARRAY); 38068 is_template = FALSE; 38069 } 38070 if (js_get_length32(ctx, &len, obj)) /* no side effect */ 38071 goto fail; 38072 bc_put_leb128(s, len); 38073 if (p->fast_array) { 38074 for(i = 0; i < p->u.array.count; i++) { 38075 ret = JS_WriteObjectRec(s, p->u.array.u.values[i]); 38076 if (ret) 38077 goto fail; 38078 } 38079 for(i = p->u.array.count; i < len; i++) { 38080 ret = JS_WriteObjectRec(s, JS_UNDEFINED); 38081 if (ret) 38082 goto fail; 38083 } 38084 } else { 38085 for(i = 0; i < len; i++) { 38086 JSAtom atom; 38087 atom = JS_NewAtomUInt32(ctx, i); 38088 if (atom == JS_ATOM_NULL) 38089 goto fail; 38090 prs = find_own_property(&pr, p, atom); 38091 JS_FreeAtom(ctx, atom); 38092 if (prs && (prs->flags & JS_PROP_ENUMERABLE)) { 38093 if (prs->flags & JS_PROP_TMASK) { 38094 JS_ThrowTypeError(ctx, "only value properties are supported"); 38095 goto fail; 38096 } 38097 ret = JS_WriteObjectRec(s, pr->u.value); 38098 if (ret) 38099 goto fail; 38100 } else { 38101 ret = JS_WriteObjectRec(s, JS_UNDEFINED); 38102 if (ret) 38103 goto fail; 38104 } 38105 } 38106 } 38107 if (is_template) { 38108 /* the 'raw' property is not enumerable */ 38109 prs = find_own_property(&pr, p, JS_ATOM_raw); 38110 if (prs) { 38111 if (prs->flags & JS_PROP_TMASK) { 38112 JS_ThrowTypeError(ctx, "only value properties are supported"); 38113 goto fail; 38114 } 38115 ret = JS_WriteObjectRec(s, pr->u.value); 38116 if (ret) 38117 goto fail; 38118 } else { 38119 ret = JS_WriteObjectRec(s, JS_UNDEFINED); 38120 if (ret) 38121 goto fail; 38122 } 38123 } 38124 return 0; 38125 fail: 38126 return -1; 38127 } 38128 38129 static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) 38130 { 38131 JSObject *p = JS_VALUE_GET_OBJ(obj); 38132 uint32_t i, prop_count; 38133 JSShape *sh; 38134 JSShapeProperty *pr; 38135 int pass; 38136 JSAtom atom; 38137 38138 bc_put_u8(s, BC_TAG_OBJECT); 38139 prop_count = 0; 38140 sh = p->shape; 38141 for(pass = 0; pass < 2; pass++) { 38142 if (pass == 1) 38143 bc_put_leb128(s, prop_count); 38144 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { 38145 atom = pr->atom; 38146 if (atom != JS_ATOM_NULL && 38147 JS_AtomIsString(s->ctx, atom) && 38148 (pr->flags & JS_PROP_ENUMERABLE)) { 38149 if (pr->flags & JS_PROP_TMASK) { 38150 JS_ThrowTypeError(s->ctx, "only value properties are supported"); 38151 goto fail; 38152 } 38153 if (pass == 0) { 38154 prop_count++; 38155 } else { 38156 bc_put_atom(s, atom); 38157 if (JS_WriteObjectRec(s, p->prop[i].u.value)) 38158 goto fail; 38159 } 38160 } 38161 } 38162 } 38163 return 0; 38164 fail: 38165 return -1; 38166 } 38167 38168 static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) 38169 { 38170 JSObject *p = JS_VALUE_GET_OBJ(obj); 38171 JSTypedArray *ta = p->u.typed_array; 38172 38173 bc_put_u8(s, BC_TAG_TYPED_ARRAY); 38174 bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); 38175 bc_put_leb128(s, p->u.array.count); 38176 bc_put_leb128(s, ta->offset); 38177 if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) 38178 return -1; 38179 return 0; 38180 } 38181 38182 static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) 38183 { 38184 JSObject *p = JS_VALUE_GET_OBJ(obj); 38185 JSArrayBuffer *abuf = p->u.array_buffer; 38186 if (abuf->detached) { 38187 JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); 38188 return -1; 38189 } 38190 bc_put_u8(s, BC_TAG_ARRAY_BUFFER); 38191 bc_put_leb128(s, abuf->byte_length); 38192 bc_put_leb128(s, abuf->max_byte_length); 38193 dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); 38194 return 0; 38195 } 38196 38197 static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) 38198 { 38199 JSObject *p = JS_VALUE_GET_OBJ(obj); 38200 JSArrayBuffer *abuf = p->u.array_buffer; 38201 assert(!abuf->detached); /* SharedArrayBuffer are never detached */ 38202 bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); 38203 bc_put_leb128(s, abuf->byte_length); 38204 bc_put_leb128(s, abuf->max_byte_length); 38205 bc_put_u64(s, (uintptr_t)abuf->data); 38206 if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), 38207 &s->sab_tab_size, s->sab_tab_len + 1)) 38208 return -1; 38209 /* keep the SAB pointer so that the user can clone it or free it */ 38210 s->sab_tab[s->sab_tab_len++] = abuf->data; 38211 return 0; 38212 } 38213 38214 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) 38215 { 38216 uint32_t tag; 38217 38218 if (js_check_stack_overflow(s->ctx->rt, 0)) { 38219 JS_ThrowStackOverflow(s->ctx); 38220 return -1; 38221 } 38222 38223 tag = JS_VALUE_GET_NORM_TAG(obj); 38224 switch(tag) { 38225 case JS_TAG_NULL: 38226 bc_put_u8(s, BC_TAG_NULL); 38227 break; 38228 case JS_TAG_UNDEFINED: 38229 bc_put_u8(s, BC_TAG_UNDEFINED); 38230 break; 38231 case JS_TAG_BOOL: 38232 bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); 38233 break; 38234 case JS_TAG_INT: 38235 bc_put_u8(s, BC_TAG_INT32); 38236 bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); 38237 break; 38238 case JS_TAG_FLOAT64: 38239 { 38240 JSFloat64Union u; 38241 bc_put_u8(s, BC_TAG_FLOAT64); 38242 u.d = JS_VALUE_GET_FLOAT64(obj); 38243 bc_put_u64(s, u.u64); 38244 } 38245 break; 38246 case JS_TAG_STRING: 38247 { 38248 JSString *p = JS_VALUE_GET_STRING(obj); 38249 bc_put_u8(s, BC_TAG_STRING); 38250 JS_WriteString(s, p); 38251 } 38252 break; 38253 case JS_TAG_STRING_ROPE: 38254 { 38255 JSValue str; 38256 int ret; 38257 str = JS_ToString(s->ctx, obj); 38258 if (JS_IsException(str)) 38259 goto fail; 38260 ret = JS_WriteObjectRec(s, str); 38261 JS_FreeValue(s->ctx, str); 38262 if (ret) 38263 goto fail; 38264 } 38265 break; 38266 case JS_TAG_FUNCTION_BYTECODE: 38267 if (!s->allow_bytecode) 38268 goto invalid_tag; 38269 if (JS_WriteFunctionTag(s, obj)) 38270 goto fail; 38271 break; 38272 case JS_TAG_MODULE: 38273 if (!s->allow_bytecode) 38274 goto invalid_tag; 38275 if (JS_WriteModule(s, obj)) 38276 goto fail; 38277 break; 38278 case JS_TAG_OBJECT: 38279 { 38280 JSObject *p = JS_VALUE_GET_OBJ(obj); 38281 int ret, idx; 38282 38283 if (s->allow_reference) { 38284 idx = js_object_list_find(s->ctx, &s->object_list, p); 38285 if (idx >= 0) { 38286 bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); 38287 bc_put_leb128(s, idx); 38288 break; 38289 } else { 38290 if (js_object_list_add(s->ctx, &s->object_list, p)) 38291 goto fail; 38292 } 38293 } else { 38294 if (p->tmp_mark) { 38295 JS_ThrowTypeError(s->ctx, "circular reference"); 38296 goto fail; 38297 } 38298 p->tmp_mark = 1; 38299 } 38300 switch(p->class_id) { 38301 case JS_CLASS_ARRAY: 38302 ret = JS_WriteArray(s, obj); 38303 break; 38304 case JS_CLASS_OBJECT: 38305 ret = JS_WriteObjectTag(s, obj); 38306 break; 38307 case JS_CLASS_ARRAY_BUFFER: 38308 ret = JS_WriteArrayBuffer(s, obj); 38309 break; 38310 case JS_CLASS_SHARED_ARRAY_BUFFER: 38311 if (!s->allow_sab) 38312 goto invalid_tag; 38313 ret = JS_WriteSharedArrayBuffer(s, obj); 38314 break; 38315 case JS_CLASS_DATE: 38316 bc_put_u8(s, BC_TAG_DATE); 38317 ret = JS_WriteObjectRec(s, p->u.object_data); 38318 break; 38319 case JS_CLASS_NUMBER: 38320 case JS_CLASS_STRING: 38321 case JS_CLASS_BOOLEAN: 38322 case JS_CLASS_BIG_INT: 38323 bc_put_u8(s, BC_TAG_OBJECT_VALUE); 38324 ret = JS_WriteObjectRec(s, p->u.object_data); 38325 break; 38326 default: 38327 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 38328 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 38329 ret = JS_WriteTypedArray(s, obj); 38330 } else { 38331 JS_ThrowTypeError(s->ctx, "unsupported object class"); 38332 ret = -1; 38333 } 38334 break; 38335 } 38336 p->tmp_mark = 0; 38337 if (ret) 38338 goto fail; 38339 } 38340 break; 38341 case JS_TAG_SHORT_BIG_INT: 38342 case JS_TAG_BIG_INT: 38343 if (JS_WriteBigInt(s, obj)) 38344 goto fail; 38345 break; 38346 default: 38347 invalid_tag: 38348 JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); 38349 goto fail; 38350 } 38351 return 0; 38352 38353 fail: 38354 return -1; 38355 } 38356 38357 /* create the atom table */ 38358 static int JS_WriteObjectAtoms(BCWriterState *s) 38359 { 38360 JSRuntime *rt = s->ctx->rt; 38361 DynBuf dbuf1; 38362 int i, atoms_size; 38363 38364 dbuf1 = s->dbuf; 38365 js_dbuf_init(s->ctx, &s->dbuf); 38366 bc_put_u8(s, BC_VERSION); 38367 38368 bc_put_leb128(s, s->idx_to_atom_count); 38369 for(i = 0; i < s->idx_to_atom_count; i++) { 38370 JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]]; 38371 JS_WriteString(s, p); 38372 } 38373 /* XXX: should check for OOM in above phase */ 38374 38375 /* move the atoms at the start */ 38376 /* XXX: could just append dbuf1 data, but it uses more memory if 38377 dbuf1 is larger than dbuf */ 38378 atoms_size = s->dbuf.size; 38379 if (dbuf_claim(&dbuf1, atoms_size)) 38380 goto fail; 38381 memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); 38382 memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); 38383 dbuf1.size += atoms_size; 38384 dbuf_free(&s->dbuf); 38385 s->dbuf = dbuf1; 38386 return 0; 38387 fail: 38388 dbuf_free(&dbuf1); 38389 return -1; 38390 } 38391 38392 uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, 38393 int flags, uint8_t ***psab_tab, size_t *psab_tab_len) 38394 { 38395 BCWriterState ss, *s = &ss; 38396 38397 memset(s, 0, sizeof(*s)); 38398 s->ctx = ctx; 38399 s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); 38400 s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); 38401 s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); 38402 /* XXX: could use a different version when bytecode is included */ 38403 if (s->allow_bytecode) 38404 s->first_atom = JS_ATOM_END; 38405 else 38406 s->first_atom = 1; 38407 js_dbuf_init(ctx, &s->dbuf); 38408 js_object_list_init(&s->object_list); 38409 38410 if (JS_WriteObjectRec(s, obj)) 38411 goto fail; 38412 if (JS_WriteObjectAtoms(s)) 38413 goto fail; 38414 js_object_list_end(ctx, &s->object_list); 38415 js_free(ctx, s->atom_to_idx); 38416 js_free(ctx, s->idx_to_atom); 38417 *psize = s->dbuf.size; 38418 if (psab_tab) 38419 *psab_tab = s->sab_tab; 38420 if (psab_tab_len) 38421 *psab_tab_len = s->sab_tab_len; 38422 return s->dbuf.buf; 38423 fail: 38424 js_object_list_end(ctx, &s->object_list); 38425 js_free(ctx, s->atom_to_idx); 38426 js_free(ctx, s->idx_to_atom); 38427 dbuf_free(&s->dbuf); 38428 *psize = 0; 38429 if (psab_tab) 38430 *psab_tab = NULL; 38431 if (psab_tab_len) 38432 *psab_tab_len = 0; 38433 return NULL; 38434 } 38435 38436 uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, 38437 int flags) 38438 { 38439 return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); 38440 } 38441 38442 typedef struct BCReaderState { 38443 JSContext *ctx; 38444 const uint8_t *buf_start, *ptr, *buf_end; 38445 uint32_t first_atom; 38446 uint32_t idx_to_atom_count; 38447 JSAtom *idx_to_atom; 38448 int error_state; 38449 BOOL allow_sab : 8; 38450 BOOL allow_bytecode : 8; 38451 BOOL is_rom_data : 8; 38452 BOOL allow_reference : 8; 38453 /* object references */ 38454 JSObject **objects; 38455 int objects_count; 38456 int objects_size; 38457 38458 #ifdef DUMP_READ_OBJECT 38459 const uint8_t *ptr_last; 38460 int level; 38461 #endif 38462 } BCReaderState; 38463 38464 #ifdef DUMP_READ_OBJECT 38465 static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) { 38466 va_list ap; 38467 int i, n, n0; 38468 38469 if (!s->ptr_last) 38470 s->ptr_last = s->buf_start; 38471 38472 n = n0 = 0; 38473 if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { 38474 n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); 38475 n += n0; 38476 } 38477 for (i = 0; s->ptr_last < s->ptr; i++) { 38478 if ((i & 7) == 0 && i > 0) { 38479 printf("\n%*s", n0, ""); 38480 n = n0; 38481 } 38482 n += printf(" %02x", *s->ptr_last++); 38483 } 38484 if (*fmt == '}') 38485 s->level--; 38486 if (n < 32 + s->level * 2) { 38487 printf("%*s", 32 + s->level * 2 - n, ""); 38488 } 38489 va_start(ap, fmt); 38490 vfprintf(stdout, fmt, ap); 38491 va_end(ap); 38492 if (strchr(fmt, '{')) 38493 s->level++; 38494 } 38495 #else 38496 #define bc_read_trace(...) 38497 #endif 38498 38499 static int bc_read_error_end(BCReaderState *s) 38500 { 38501 if (!s->error_state) { 38502 JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); 38503 } 38504 return s->error_state = -1; 38505 } 38506 38507 static int bc_get_u8(BCReaderState *s, uint8_t *pval) 38508 { 38509 if (unlikely(s->buf_end - s->ptr < 1)) { 38510 *pval = 0; /* avoid warning */ 38511 return bc_read_error_end(s); 38512 } 38513 *pval = *s->ptr++; 38514 return 0; 38515 } 38516 38517 static int bc_get_u16(BCReaderState *s, uint16_t *pval) 38518 { 38519 uint16_t v; 38520 if (unlikely(s->buf_end - s->ptr < 2)) { 38521 *pval = 0; /* avoid warning */ 38522 return bc_read_error_end(s); 38523 } 38524 v = get_u16(s->ptr); 38525 if (is_be()) 38526 v = bswap16(v); 38527 *pval = v; 38528 s->ptr += 2; 38529 return 0; 38530 } 38531 38532 static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) 38533 { 38534 uint32_t v; 38535 if (unlikely(s->buf_end - s->ptr < 4)) { 38536 *pval = 0; /* avoid warning */ 38537 return bc_read_error_end(s); 38538 } 38539 v = get_u32(s->ptr); 38540 if (is_be()) 38541 v = bswap32(v); 38542 *pval = v; 38543 s->ptr += 4; 38544 return 0; 38545 } 38546 38547 static int bc_get_u64(BCReaderState *s, uint64_t *pval) 38548 { 38549 uint64_t v; 38550 if (unlikely(s->buf_end - s->ptr < 8)) { 38551 *pval = 0; /* avoid warning */ 38552 return bc_read_error_end(s); 38553 } 38554 v = get_u64(s->ptr); 38555 if (is_be()) 38556 v = bswap64(v); 38557 *pval = v; 38558 s->ptr += 8; 38559 return 0; 38560 } 38561 38562 static int bc_get_leb128(BCReaderState *s, uint32_t *pval) 38563 { 38564 int ret; 38565 ret = get_leb128(pval, s->ptr, s->buf_end); 38566 if (unlikely(ret < 0)) 38567 return bc_read_error_end(s); 38568 s->ptr += ret; 38569 return 0; 38570 } 38571 38572 static int bc_get_sleb128(BCReaderState *s, int32_t *pval) 38573 { 38574 int ret; 38575 ret = get_sleb128(pval, s->ptr, s->buf_end); 38576 if (unlikely(ret < 0)) 38577 return bc_read_error_end(s); 38578 s->ptr += ret; 38579 return 0; 38580 } 38581 38582 /* XXX: used to read an `int` with a positive value */ 38583 static int bc_get_leb128_int(BCReaderState *s, int *pval) 38584 { 38585 return bc_get_leb128(s, (uint32_t *)pval); 38586 } 38587 38588 static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) 38589 { 38590 uint32_t val; 38591 if (bc_get_leb128(s, &val)) { 38592 *pval = 0; 38593 return -1; 38594 } 38595 *pval = val; 38596 return 0; 38597 } 38598 38599 static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len) 38600 { 38601 if (buf_len != 0) { 38602 if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) 38603 return bc_read_error_end(s); 38604 memcpy(buf, s->ptr, buf_len); 38605 s->ptr += buf_len; 38606 } 38607 return 0; 38608 } 38609 38610 static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) 38611 { 38612 JSAtom atom; 38613 38614 if (__JS_AtomIsTaggedInt(idx)) { 38615 atom = idx; 38616 } else if (idx < s->first_atom) { 38617 atom = JS_DupAtom(s->ctx, idx); 38618 } else { 38619 idx -= s->first_atom; 38620 if (idx >= s->idx_to_atom_count) { 38621 JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", 38622 (unsigned int)(s->ptr - s->buf_start)); 38623 *patom = JS_ATOM_NULL; 38624 return s->error_state = -1; 38625 } 38626 atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); 38627 } 38628 *patom = atom; 38629 return 0; 38630 } 38631 38632 static int bc_get_atom(BCReaderState *s, JSAtom *patom) 38633 { 38634 uint32_t v; 38635 if (bc_get_leb128(s, &v)) 38636 return -1; 38637 if (v & 1) { 38638 *patom = __JS_AtomFromUInt32(v >> 1); 38639 return 0; 38640 } else { 38641 return bc_idx_to_atom(s, patom, v >> 1); 38642 } 38643 } 38644 38645 static JSString *JS_ReadString(BCReaderState *s) 38646 { 38647 uint32_t len; 38648 size_t size; 38649 BOOL is_wide_char; 38650 JSString *p; 38651 38652 if (bc_get_leb128(s, &len)) 38653 return NULL; 38654 is_wide_char = len & 1; 38655 len >>= 1; 38656 if (len > JS_STRING_LEN_MAX) { 38657 JS_ThrowInternalError(s->ctx, "string too long"); 38658 return NULL; 38659 } 38660 p = js_alloc_string(s->ctx, len, is_wide_char); 38661 if (!p) { 38662 s->error_state = -1; 38663 return NULL; 38664 } 38665 size = (size_t)len << is_wide_char; 38666 if ((s->buf_end - s->ptr) < size) { 38667 bc_read_error_end(s); 38668 js_free_string(s->ctx->rt, p); 38669 return NULL; 38670 } 38671 memcpy(p->u.str8, s->ptr, size); 38672 s->ptr += size; 38673 if (is_wide_char) { 38674 if (is_be()) { 38675 uint32_t i; 38676 for (i = 0; i < len; i++) 38677 p->u.str16[i] = bswap16(p->u.str16[i]); 38678 } 38679 } else { 38680 p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ 38681 } 38682 #ifdef DUMP_READ_OBJECT 38683 JS_DumpString(s->ctx->rt, p); printf("\n"); 38684 #endif 38685 return p; 38686 } 38687 38688 static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n) 38689 { 38690 uint32_t val; 38691 /* XXX: this does not work for n == 32 */ 38692 val = (flags >> *pidx) & ((1U << n) - 1); 38693 *pidx += n; 38694 return val; 38695 } 38696 38697 static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, 38698 int byte_code_offset, uint32_t bc_len) 38699 { 38700 uint8_t *bc_buf; 38701 int pos, len, op; 38702 JSAtom atom; 38703 uint32_t idx; 38704 38705 if (s->is_rom_data) { 38706 /* directly use the input buffer */ 38707 if (unlikely(s->buf_end - s->ptr < bc_len)) 38708 return bc_read_error_end(s); 38709 bc_buf = (uint8_t *)s->ptr; 38710 s->ptr += bc_len; 38711 } else { 38712 bc_buf = (void *)((uint8_t*)b + byte_code_offset); 38713 if (bc_get_buf(s, bc_buf, bc_len)) 38714 return -1; 38715 } 38716 b->byte_code_buf = bc_buf; 38717 38718 if (is_be()) 38719 bc_byte_swap(bc_buf, bc_len); 38720 38721 pos = 0; 38722 while (pos < bc_len) { 38723 op = bc_buf[pos]; 38724 len = short_opcode_info(op).size; 38725 switch(short_opcode_info(op).fmt) { 38726 case OP_FMT_atom: 38727 case OP_FMT_atom_u8: 38728 case OP_FMT_atom_u16: 38729 case OP_FMT_atom_label_u8: 38730 case OP_FMT_atom_label_u16: 38731 idx = get_u32(bc_buf + pos + 1); 38732 if (s->is_rom_data) { 38733 /* just increment the reference count of the atom */ 38734 JS_DupAtom(s->ctx, (JSAtom)idx); 38735 } else { 38736 if (bc_idx_to_atom(s, &atom, idx)) { 38737 /* Note: the atoms will be freed up to this position */ 38738 b->byte_code_len = pos; 38739 return -1; 38740 } 38741 put_u32(bc_buf + pos + 1, atom); 38742 #ifdef DUMP_READ_OBJECT 38743 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n"); 38744 #endif 38745 } 38746 break; 38747 default: 38748 break; 38749 } 38750 pos += len; 38751 } 38752 return 0; 38753 } 38754 38755 static JSValue JS_ReadBigInt(BCReaderState *s) 38756 { 38757 JSValue obj = JS_UNDEFINED; 38758 uint32_t len, i, n; 38759 JSBigInt *p; 38760 js_limb_t v; 38761 uint8_t v8; 38762 38763 if (bc_get_leb128(s, &len)) 38764 goto fail; 38765 bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); 38766 if (len == 0) { 38767 /* zero case */ 38768 bc_read_trace(s, "}\n"); 38769 return __JS_NewShortBigInt(s->ctx, 0); 38770 } 38771 p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1); 38772 if (!p) 38773 goto fail; 38774 for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) { 38775 #if JS_LIMB_BITS == 32 38776 if (bc_get_u32(s, &v)) 38777 goto fail; 38778 #else 38779 if (bc_get_u64(s, &v)) 38780 goto fail; 38781 #endif 38782 p->tab[i] = v; 38783 } 38784 n = len % (JS_LIMB_BITS / 8); 38785 if (n != 0) { 38786 int shift; 38787 v = 0; 38788 for(i = 0; i < n; i++) { 38789 if (bc_get_u8(s, &v8)) 38790 goto fail; 38791 v |= (js_limb_t)v8 << (i * 8); 38792 } 38793 shift = JS_LIMB_BITS - n * 8; 38794 /* extend the sign */ 38795 if (shift != 0) { 38796 v = (js_slimb_t)(v << shift) >> shift; 38797 } 38798 p->tab[p->len - 1] = v; 38799 } 38800 bc_read_trace(s, "}\n"); 38801 return JS_CompactBigInt(s->ctx, p); 38802 fail: 38803 JS_FreeValue(s->ctx, obj); 38804 return JS_EXCEPTION; 38805 } 38806 38807 static JSValue JS_ReadObjectRec(BCReaderState *s); 38808 38809 static int BC_add_object_ref1(BCReaderState *s, JSObject *p) 38810 { 38811 if (s->allow_reference) { 38812 if (js_resize_array(s->ctx, (void *)&s->objects, 38813 sizeof(s->objects[0]), 38814 &s->objects_size, s->objects_count + 1)) 38815 return -1; 38816 s->objects[s->objects_count++] = p; 38817 } 38818 return 0; 38819 } 38820 38821 static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) 38822 { 38823 return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); 38824 } 38825 38826 static JSValue JS_ReadFunctionTag(BCReaderState *s) 38827 { 38828 JSContext *ctx = s->ctx; 38829 JSFunctionBytecode bc, *b; 38830 JSValue obj = JS_UNDEFINED; 38831 uint16_t v16; 38832 uint8_t v8; 38833 int idx, i, local_count; 38834 int cpool_offset, byte_code_offset; 38835 int closure_var_offset, vardefs_offset; 38836 uint64_t function_size; 38837 38838 memset(&bc, 0, sizeof(bc)); 38839 38840 if (bc_get_u16(s, &v16)) 38841 goto fail; 38842 idx = 0; 38843 bc.has_prototype = bc_get_flags(v16, &idx, 1); 38844 bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); 38845 bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); 38846 bc.need_home_object = bc_get_flags(v16, &idx, 1); 38847 bc.func_kind = bc_get_flags(v16, &idx, 2); 38848 bc.new_target_allowed = bc_get_flags(v16, &idx, 1); 38849 bc.super_call_allowed = bc_get_flags(v16, &idx, 1); 38850 bc.super_allowed = bc_get_flags(v16, &idx, 1); 38851 bc.arguments_allowed = bc_get_flags(v16, &idx, 1); 38852 bc.has_debug = bc_get_flags(v16, &idx, 1); 38853 bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1); 38854 bc.read_only_bytecode = s->is_rom_data; 38855 if (bc_get_u8(s, &v8)) 38856 goto fail; 38857 bc.js_mode = v8; 38858 if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure 38859 goto fail; 38860 if (bc_get_leb128_u16(s, &bc.arg_count)) 38861 goto fail; 38862 if (bc_get_leb128_u16(s, &bc.var_count)) 38863 goto fail; 38864 if (bc_get_leb128_u16(s, &bc.defined_arg_count)) 38865 goto fail; 38866 if (bc_get_leb128_u16(s, &bc.stack_size)) 38867 goto fail; 38868 if (bc_get_leb128_u16(s, &bc.var_ref_count)) 38869 goto fail; 38870 if (bc_get_leb128_int(s, &bc.closure_var_count)) 38871 goto fail; 38872 if (bc_get_leb128_int(s, &bc.cpool_count)) 38873 goto fail; 38874 if (bc_get_leb128_int(s, &bc.byte_code_len)) 38875 goto fail; 38876 if (bc_get_leb128_int(s, &local_count)) 38877 goto fail; 38878 38879 if (bc.has_debug) { 38880 function_size = sizeof(*b); 38881 } else { 38882 function_size = offsetof(JSFunctionBytecode, debug); 38883 } 38884 cpool_offset = function_size; 38885 function_size += (uint64_t)bc.cpool_count * sizeof(*bc.cpool); 38886 vardefs_offset = function_size; 38887 function_size += (uint64_t)local_count * sizeof(*bc.vardefs); 38888 closure_var_offset = function_size; 38889 function_size += (uint64_t)bc.closure_var_count * sizeof(*bc.closure_var); 38890 byte_code_offset = function_size; 38891 if (!bc.read_only_bytecode) { 38892 function_size += bc.byte_code_len; 38893 } 38894 38895 if (function_size > INT32_MAX) 38896 return JS_ThrowOutOfMemory(ctx); 38897 38898 b = js_mallocz(ctx, function_size); 38899 if (!b) 38900 return JS_EXCEPTION; 38901 38902 memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); 38903 if (local_count != 0) { 38904 b->vardefs = (void *)((uint8_t*)b + vardefs_offset); 38905 } 38906 if (b->closure_var_count != 0) { 38907 b->closure_var = (void *)((uint8_t*)b + closure_var_offset); 38908 } 38909 if (b->cpool_count != 0) { 38910 b->cpool = (void *)((uint8_t*)b + cpool_offset); 38911 } 38912 38913 js_rc(b)->ref_count = 1; 38914 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); 38915 38916 obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); 38917 38918 #ifdef DUMP_READ_OBJECT 38919 bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); 38920 #endif 38921 bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", 38922 b->arg_count, b->var_count, b->defined_arg_count, 38923 b->closure_var_count, b->cpool_count); 38924 bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", 38925 b->stack_size, b->byte_code_len, local_count); 38926 38927 if (local_count != 0) { 38928 bc_read_trace(s, "vars {\n"); 38929 for(i = 0; i < local_count; i++) { 38930 JSBytecodeVarDef *vd = &b->vardefs[i]; 38931 if (bc_get_atom(s, &vd->var_name)) 38932 goto fail; 38933 if (bc_get_leb128_int(s, &vd->scope_next)) 38934 goto fail; 38935 vd->scope_next--; 38936 if (bc_get_leb128_u16(s, &vd->var_ref_idx)) 38937 goto fail; 38938 if (bc_get_u8(s, &v8)) 38939 goto fail; 38940 idx = 0; 38941 vd->var_kind = bc_get_flags(v8, &idx, 4); 38942 vd->is_const = bc_get_flags(v8, &idx, 1); 38943 vd->is_lexical = bc_get_flags(v8, &idx, 1); 38944 vd->is_captured = bc_get_flags(v8, &idx, 1); 38945 vd->has_scope = bc_get_flags(v8, &idx, 1); 38946 #ifdef DUMP_READ_OBJECT 38947 bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); 38948 #endif 38949 } 38950 bc_read_trace(s, "}\n"); 38951 } 38952 if (b->closure_var_count != 0) { 38953 bc_read_trace(s, "closure vars {\n"); 38954 for(i = 0; i < b->closure_var_count; i++) { 38955 JSClosureVar *cv = &b->closure_var[i]; 38956 int var_idx; 38957 if (bc_get_atom(s, &cv->var_name)) 38958 goto fail; 38959 if (bc_get_leb128_int(s, &var_idx)) 38960 goto fail; 38961 cv->var_idx = var_idx; 38962 if (bc_get_u16(s, &v16)) 38963 goto fail; 38964 idx = 0; 38965 cv->closure_type = bc_get_flags(v16, &idx, 3); 38966 cv->is_const = bc_get_flags(v16, &idx, 1); 38967 cv->is_lexical = bc_get_flags(v16, &idx, 1); 38968 cv->var_kind = bc_get_flags(v16, &idx, 4); 38969 #ifdef DUMP_READ_OBJECT 38970 bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); 38971 #endif 38972 } 38973 bc_read_trace(s, "}\n"); 38974 } 38975 { 38976 bc_read_trace(s, "bytecode {\n"); 38977 if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) 38978 goto fail; 38979 bc_read_trace(s, "}\n"); 38980 } 38981 if (b->has_debug) { 38982 /* read optional debug information */ 38983 bc_read_trace(s, "debug {\n"); 38984 if (bc_get_atom(s, &b->debug.filename)) 38985 goto fail; 38986 #ifdef DUMP_READ_OBJECT 38987 bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); 38988 #endif 38989 if (bc_get_leb128_int(s, &b->debug.pc2line_len)) 38990 goto fail; 38991 if (b->debug.pc2line_len) { 38992 b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); 38993 if (!b->debug.pc2line_buf) 38994 goto fail; 38995 if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) 38996 goto fail; 38997 } 38998 if (bc_get_leb128_int(s, &b->debug.source_len)) 38999 goto fail; 39000 if (b->debug.source_len) { 39001 bc_read_trace(s, "source: %d bytes\n", b->source_len); 39002 b->debug.source = js_mallocz(ctx, b->debug.source_len); 39003 if (!b->debug.source) 39004 goto fail; 39005 if (bc_get_buf(s, (uint8_t *)b->debug.source, b->debug.source_len)) 39006 goto fail; 39007 } 39008 bc_read_trace(s, "}\n"); 39009 } 39010 if (b->cpool_count != 0) { 39011 bc_read_trace(s, "cpool {\n"); 39012 for(i = 0; i < b->cpool_count; i++) { 39013 JSValue val; 39014 val = JS_ReadObjectRec(s); 39015 if (JS_IsException(val)) 39016 goto fail; 39017 b->cpool[i] = val; 39018 } 39019 bc_read_trace(s, "}\n"); 39020 } 39021 b->realm = JS_DupContext(ctx); 39022 return obj; 39023 fail: 39024 JS_FreeValue(ctx, obj); 39025 return JS_EXCEPTION; 39026 } 39027 39028 static JSValue JS_ReadModule(BCReaderState *s) 39029 { 39030 JSContext *ctx = s->ctx; 39031 JSValue obj; 39032 JSModuleDef *m = NULL; 39033 JSAtom module_name; 39034 int i; 39035 uint8_t v8; 39036 39037 if (bc_get_atom(s, &module_name)) 39038 goto fail; 39039 #ifdef DUMP_READ_OBJECT 39040 bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); 39041 #endif 39042 m = js_new_module_def(ctx, module_name); 39043 if (!m) 39044 goto fail; 39045 obj = JS_NewModuleValue(ctx, m); 39046 if (bc_get_leb128_int(s, &m->req_module_entries_count)) 39047 goto fail; 39048 if (m->req_module_entries_count != 0) { 39049 m->req_module_entries_size = m->req_module_entries_count; 39050 m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); 39051 if (!m->req_module_entries) 39052 goto fail; 39053 for(i = 0; i < m->req_module_entries_count; i++) { 39054 JSReqModuleEntry *rme = &m->req_module_entries[i]; 39055 JSValue val; 39056 if (bc_get_atom(s, &rme->module_name)) 39057 goto fail; 39058 val = JS_ReadObjectRec(s); 39059 if (JS_IsException(val)) 39060 goto fail; 39061 rme->attributes = val; 39062 } 39063 } 39064 39065 if (bc_get_leb128_int(s, &m->export_entries_count)) 39066 goto fail; 39067 if (m->export_entries_count != 0) { 39068 m->export_entries_size = m->export_entries_count; 39069 m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); 39070 if (!m->export_entries) 39071 goto fail; 39072 for(i = 0; i < m->export_entries_count; i++) { 39073 JSExportEntry *me = &m->export_entries[i]; 39074 if (bc_get_u8(s, &v8)) 39075 goto fail; 39076 me->export_type = v8; 39077 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 39078 if (bc_get_leb128_int(s, &me->u.local.var_idx)) 39079 goto fail; 39080 } else { 39081 if (bc_get_leb128_int(s, &me->u.req_module_idx)) 39082 goto fail; 39083 if (bc_get_atom(s, &me->local_name)) 39084 goto fail; 39085 } 39086 if (bc_get_atom(s, &me->export_name)) 39087 goto fail; 39088 } 39089 } 39090 39091 if (bc_get_leb128_int(s, &m->star_export_entries_count)) 39092 goto fail; 39093 if (m->star_export_entries_count != 0) { 39094 m->star_export_entries_size = m->star_export_entries_count; 39095 m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); 39096 if (!m->star_export_entries) 39097 goto fail; 39098 for(i = 0; i < m->star_export_entries_count; i++) { 39099 JSStarExportEntry *se = &m->star_export_entries[i]; 39100 if (bc_get_leb128_int(s, &se->req_module_idx)) 39101 goto fail; 39102 } 39103 } 39104 39105 if (bc_get_leb128_int(s, &m->import_entries_count)) 39106 goto fail; 39107 if (m->import_entries_count != 0) { 39108 m->import_entries_size = m->import_entries_count; 39109 m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); 39110 if (!m->import_entries) 39111 goto fail; 39112 for(i = 0; i < m->import_entries_count; i++) { 39113 JSImportEntry *mi = &m->import_entries[i]; 39114 uint8_t v8; 39115 if (bc_get_leb128_int(s, &mi->var_idx)) 39116 goto fail; 39117 if (bc_get_u8(s, &v8)) 39118 goto fail; 39119 mi->is_star = (v8 != 0); 39120 if (bc_get_atom(s, &mi->import_name)) 39121 goto fail; 39122 if (bc_get_leb128_int(s, &mi->req_module_idx)) 39123 goto fail; 39124 } 39125 } 39126 39127 if (bc_get_u8(s, &v8)) 39128 goto fail; 39129 m->has_tla = (v8 != 0); 39130 39131 m->func_obj = JS_ReadObjectRec(s); 39132 if (JS_IsException(m->func_obj)) 39133 goto fail; 39134 return obj; 39135 fail: 39136 if (m) { 39137 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); 39138 } 39139 return JS_EXCEPTION; 39140 } 39141 39142 static JSValue JS_ReadObjectTag(BCReaderState *s) 39143 { 39144 JSContext *ctx = s->ctx; 39145 JSValue obj; 39146 uint32_t prop_count, i; 39147 JSAtom atom; 39148 JSValue val; 39149 int ret; 39150 39151 obj = JS_NewObject(ctx); 39152 if (BC_add_object_ref(s, obj)) 39153 goto fail; 39154 if (bc_get_leb128(s, &prop_count)) 39155 goto fail; 39156 for(i = 0; i < prop_count; i++) { 39157 if (bc_get_atom(s, &atom)) 39158 goto fail; 39159 #ifdef DUMP_READ_OBJECT 39160 bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); 39161 #endif 39162 val = JS_ReadObjectRec(s); 39163 if (JS_IsException(val)) { 39164 JS_FreeAtom(ctx, atom); 39165 goto fail; 39166 } 39167 ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); 39168 JS_FreeAtom(ctx, atom); 39169 if (ret < 0) 39170 goto fail; 39171 } 39172 return obj; 39173 fail: 39174 JS_FreeValue(ctx, obj); 39175 return JS_EXCEPTION; 39176 } 39177 39178 static JSValue JS_ReadArray(BCReaderState *s, int tag) 39179 { 39180 JSContext *ctx = s->ctx; 39181 JSValue obj; 39182 uint32_t len, i; 39183 JSValue val; 39184 int ret, prop_flags; 39185 BOOL is_template; 39186 39187 obj = JS_NewArray(ctx); 39188 if (BC_add_object_ref(s, obj)) 39189 goto fail; 39190 is_template = (tag == BC_TAG_TEMPLATE_OBJECT); 39191 if (bc_get_leb128(s, &len)) 39192 goto fail; 39193 for(i = 0; i < len; i++) { 39194 val = JS_ReadObjectRec(s); 39195 if (JS_IsException(val)) 39196 goto fail; 39197 if (is_template) 39198 prop_flags = JS_PROP_ENUMERABLE; 39199 else 39200 prop_flags = JS_PROP_C_W_E; 39201 ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, 39202 prop_flags); 39203 if (ret < 0) 39204 goto fail; 39205 } 39206 if (is_template) { 39207 val = JS_ReadObjectRec(s); 39208 if (JS_IsException(val)) 39209 goto fail; 39210 if (!JS_IsUndefined(val)) { 39211 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); 39212 if (ret < 0) 39213 goto fail; 39214 } 39215 JS_PreventExtensions(ctx, obj); 39216 } 39217 return obj; 39218 fail: 39219 JS_FreeValue(ctx, obj); 39220 return JS_EXCEPTION; 39221 } 39222 39223 static JSValue JS_ReadTypedArray(BCReaderState *s) 39224 { 39225 JSContext *ctx = s->ctx; 39226 JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; 39227 uint8_t array_tag; 39228 JSValueConst args[3]; 39229 uint32_t offset, len, idx; 39230 39231 if (bc_get_u8(s, &array_tag)) 39232 return JS_EXCEPTION; 39233 if (array_tag >= JS_TYPED_ARRAY_COUNT) 39234 return JS_ThrowTypeError(ctx, "invalid typed array"); 39235 if (bc_get_leb128(s, &len)) 39236 return JS_EXCEPTION; 39237 if (bc_get_leb128(s, &offset)) 39238 return JS_EXCEPTION; 39239 /* XXX: this hack could be avoided if the typed array could be 39240 created before the array buffer */ 39241 idx = s->objects_count; 39242 if (BC_add_object_ref1(s, NULL)) 39243 goto fail; 39244 array_buffer = JS_ReadObjectRec(s); 39245 if (JS_IsException(array_buffer)) 39246 return JS_EXCEPTION; 39247 if (!js_get_array_buffer(ctx, array_buffer)) { 39248 JS_FreeValue(ctx, array_buffer); 39249 return JS_EXCEPTION; 39250 } 39251 args[0] = array_buffer; 39252 args[1] = JS_NewInt64(ctx, offset); 39253 args[2] = JS_NewInt64(ctx, len); 39254 obj = js_typed_array_constructor(ctx, JS_UNDEFINED, 39255 3, args, 39256 JS_CLASS_UINT8C_ARRAY + array_tag); 39257 if (JS_IsException(obj)) 39258 goto fail; 39259 if (s->allow_reference) { 39260 s->objects[idx] = JS_VALUE_GET_OBJ(obj); 39261 } 39262 JS_FreeValue(ctx, array_buffer); 39263 return obj; 39264 fail: 39265 JS_FreeValue(ctx, array_buffer); 39266 JS_FreeValue(ctx, obj); 39267 return JS_EXCEPTION; 39268 } 39269 39270 static JSValue JS_ReadArrayBuffer(BCReaderState *s) 39271 { 39272 JSContext *ctx = s->ctx; 39273 uint32_t byte_length, max_byte_length; 39274 uint64_t max_byte_length_u64, *pmax_byte_length = NULL; 39275 JSValue obj; 39276 39277 if (bc_get_leb128(s, &byte_length)) 39278 return JS_EXCEPTION; 39279 if (bc_get_leb128(s, &max_byte_length)) 39280 return JS_EXCEPTION; 39281 if (max_byte_length < byte_length) 39282 return JS_ThrowTypeError(ctx, "invalid array buffer"); 39283 if (max_byte_length != UINT32_MAX) { 39284 max_byte_length_u64 = max_byte_length; 39285 pmax_byte_length = &max_byte_length_u64; 39286 } 39287 if (unlikely(s->buf_end - s->ptr < byte_length)) { 39288 bc_read_error_end(s); 39289 return JS_EXCEPTION; 39290 } 39291 // makes a copy of the input 39292 obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, 39293 byte_length, pmax_byte_length, 39294 JS_CLASS_ARRAY_BUFFER, 39295 (uint8_t*)s->ptr, 39296 js_array_buffer_free, NULL, 39297 /*alloc_flag*/TRUE); 39298 if (JS_IsException(obj)) 39299 goto fail; 39300 if (BC_add_object_ref(s, obj)) 39301 goto fail; 39302 s->ptr += byte_length; 39303 return obj; 39304 fail: 39305 JS_FreeValue(ctx, obj); 39306 return JS_EXCEPTION; 39307 } 39308 39309 static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) 39310 { 39311 JSContext *ctx = s->ctx; 39312 uint32_t byte_length, max_byte_length; 39313 uint64_t max_byte_length_u64, *pmax_byte_length = NULL; 39314 uint8_t *data_ptr; 39315 JSValue obj; 39316 uint64_t u64; 39317 39318 if (bc_get_leb128(s, &byte_length)) 39319 return JS_EXCEPTION; 39320 if (bc_get_leb128(s, &max_byte_length)) 39321 return JS_EXCEPTION; 39322 if (max_byte_length < byte_length) 39323 return JS_ThrowTypeError(ctx, "invalid array buffer"); 39324 if (max_byte_length != UINT32_MAX) { 39325 max_byte_length_u64 = max_byte_length; 39326 pmax_byte_length = &max_byte_length_u64; 39327 } 39328 if (bc_get_u64(s, &u64)) 39329 return JS_EXCEPTION; 39330 data_ptr = (uint8_t *)(uintptr_t)u64; 39331 /* the SharedArrayBuffer is cloned */ 39332 obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, 39333 byte_length, pmax_byte_length, 39334 JS_CLASS_SHARED_ARRAY_BUFFER, 39335 data_ptr, 39336 NULL, NULL, FALSE); 39337 if (JS_IsException(obj)) 39338 goto fail; 39339 if (BC_add_object_ref(s, obj)) 39340 goto fail; 39341 return obj; 39342 fail: 39343 JS_FreeValue(ctx, obj); 39344 return JS_EXCEPTION; 39345 } 39346 39347 static JSValue JS_ReadDate(BCReaderState *s) 39348 { 39349 JSContext *ctx = s->ctx; 39350 JSValue val, obj = JS_UNDEFINED; 39351 39352 val = JS_ReadObjectRec(s); 39353 if (JS_IsException(val)) 39354 goto fail; 39355 if (!JS_IsNumber(val)) { 39356 JS_ThrowTypeError(ctx, "Number tag expected for date"); 39357 goto fail; 39358 } 39359 obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], 39360 JS_CLASS_DATE); 39361 if (JS_IsException(obj)) 39362 goto fail; 39363 if (BC_add_object_ref(s, obj)) 39364 goto fail; 39365 JS_SetObjectData(ctx, obj, val); 39366 return obj; 39367 fail: 39368 JS_FreeValue(ctx, val); 39369 JS_FreeValue(ctx, obj); 39370 return JS_EXCEPTION; 39371 } 39372 39373 static JSValue JS_ReadObjectValue(BCReaderState *s) 39374 { 39375 JSContext *ctx = s->ctx; 39376 JSValue val, obj = JS_UNDEFINED; 39377 39378 val = JS_ReadObjectRec(s); 39379 if (JS_IsException(val)) 39380 goto fail; 39381 obj = JS_ToObject(ctx, val); 39382 if (JS_IsException(obj)) 39383 goto fail; 39384 if (BC_add_object_ref(s, obj)) 39385 goto fail; 39386 JS_FreeValue(ctx, val); 39387 return obj; 39388 fail: 39389 JS_FreeValue(ctx, val); 39390 JS_FreeValue(ctx, obj); 39391 return JS_EXCEPTION; 39392 } 39393 39394 static JSValue JS_ReadObjectRec(BCReaderState *s) 39395 { 39396 JSContext *ctx = s->ctx; 39397 uint8_t tag; 39398 JSValue obj = JS_UNDEFINED; 39399 39400 if (js_check_stack_overflow(ctx->rt, 0)) 39401 return JS_ThrowStackOverflow(ctx); 39402 39403 if (bc_get_u8(s, &tag)) 39404 return JS_EXCEPTION; 39405 39406 bc_read_trace(s, "%s {\n", bc_tag_str[tag]); 39407 39408 switch(tag) { 39409 case BC_TAG_NULL: 39410 obj = JS_NULL; 39411 break; 39412 case BC_TAG_UNDEFINED: 39413 obj = JS_UNDEFINED; 39414 break; 39415 case BC_TAG_BOOL_FALSE: 39416 case BC_TAG_BOOL_TRUE: 39417 obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); 39418 break; 39419 case BC_TAG_INT32: 39420 { 39421 int32_t val; 39422 if (bc_get_sleb128(s, &val)) 39423 return JS_EXCEPTION; 39424 bc_read_trace(s, "%d\n", val); 39425 obj = JS_NewInt32(ctx, val); 39426 } 39427 break; 39428 case BC_TAG_FLOAT64: 39429 { 39430 JSFloat64Union u; 39431 if (bc_get_u64(s, &u.u64)) 39432 return JS_EXCEPTION; 39433 bc_read_trace(s, "%g\n", u.d); 39434 obj = __JS_NewFloat64(ctx, u.d); 39435 } 39436 break; 39437 case BC_TAG_STRING: 39438 { 39439 JSString *p; 39440 p = JS_ReadString(s); 39441 if (!p) 39442 return JS_EXCEPTION; 39443 obj = JS_MKPTR(JS_TAG_STRING, p); 39444 } 39445 break; 39446 case BC_TAG_FUNCTION_BYTECODE: 39447 if (!s->allow_bytecode) 39448 goto invalid_tag; 39449 obj = JS_ReadFunctionTag(s); 39450 break; 39451 case BC_TAG_MODULE: 39452 if (!s->allow_bytecode) 39453 goto invalid_tag; 39454 obj = JS_ReadModule(s); 39455 break; 39456 case BC_TAG_OBJECT: 39457 obj = JS_ReadObjectTag(s); 39458 break; 39459 case BC_TAG_ARRAY: 39460 case BC_TAG_TEMPLATE_OBJECT: 39461 obj = JS_ReadArray(s, tag); 39462 break; 39463 case BC_TAG_TYPED_ARRAY: 39464 obj = JS_ReadTypedArray(s); 39465 break; 39466 case BC_TAG_ARRAY_BUFFER: 39467 obj = JS_ReadArrayBuffer(s); 39468 break; 39469 case BC_TAG_SHARED_ARRAY_BUFFER: 39470 if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) 39471 goto invalid_tag; 39472 obj = JS_ReadSharedArrayBuffer(s); 39473 break; 39474 case BC_TAG_DATE: 39475 obj = JS_ReadDate(s); 39476 break; 39477 case BC_TAG_OBJECT_VALUE: 39478 obj = JS_ReadObjectValue(s); 39479 break; 39480 case BC_TAG_BIG_INT: 39481 obj = JS_ReadBigInt(s); 39482 break; 39483 case BC_TAG_OBJECT_REFERENCE: 39484 { 39485 uint32_t val; 39486 if (!s->allow_reference) 39487 return JS_ThrowSyntaxError(ctx, "object references are not allowed"); 39488 if (bc_get_leb128(s, &val)) 39489 return JS_EXCEPTION; 39490 bc_read_trace(s, "%u\n", val); 39491 if (val >= s->objects_count) { 39492 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", 39493 val, s->objects_count); 39494 } 39495 obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); 39496 } 39497 break; 39498 default: 39499 invalid_tag: 39500 return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", 39501 tag, (unsigned int)(s->ptr - s->buf_start)); 39502 } 39503 bc_read_trace(s, "}\n"); 39504 return obj; 39505 } 39506 39507 static int JS_ReadObjectAtoms(BCReaderState *s) 39508 { 39509 uint8_t v8; 39510 JSString *p; 39511 int i; 39512 JSAtom atom; 39513 39514 if (bc_get_u8(s, &v8)) 39515 return -1; 39516 if (v8 != BC_VERSION) { 39517 JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", 39518 v8, BC_VERSION); 39519 return -1; 39520 } 39521 if (bc_get_leb128(s, &s->idx_to_atom_count)) 39522 return -1; 39523 39524 bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); 39525 39526 if (s->idx_to_atom_count != 0) { 39527 s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * 39528 sizeof(s->idx_to_atom[0])); 39529 if (!s->idx_to_atom) 39530 return s->error_state = -1; 39531 } 39532 for(i = 0; i < s->idx_to_atom_count; i++) { 39533 p = JS_ReadString(s); 39534 if (!p) 39535 return -1; 39536 atom = JS_NewAtomStr(s->ctx, p); 39537 if (atom == JS_ATOM_NULL) 39538 return s->error_state = -1; 39539 s->idx_to_atom[i] = atom; 39540 if (s->is_rom_data && (atom != (i + s->first_atom))) 39541 s->is_rom_data = FALSE; /* atoms must be relocated */ 39542 } 39543 bc_read_trace(s, "}\n"); 39544 return 0; 39545 } 39546 39547 static void bc_reader_free(BCReaderState *s) 39548 { 39549 int i; 39550 if (s->idx_to_atom) { 39551 for(i = 0; i < s->idx_to_atom_count; i++) { 39552 JS_FreeAtom(s->ctx, s->idx_to_atom[i]); 39553 } 39554 js_free(s->ctx, s->idx_to_atom); 39555 } 39556 js_free(s->ctx, s->objects); 39557 } 39558 39559 JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, 39560 int flags) 39561 { 39562 BCReaderState ss, *s = &ss; 39563 JSValue obj; 39564 39565 ctx->binary_object_count += 1; 39566 ctx->binary_object_size += buf_len; 39567 39568 memset(s, 0, sizeof(*s)); 39569 s->ctx = ctx; 39570 s->buf_start = buf; 39571 s->buf_end = buf + buf_len; 39572 s->ptr = buf; 39573 s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); 39574 s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); 39575 s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); 39576 s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); 39577 if (s->allow_bytecode) 39578 s->first_atom = JS_ATOM_END; 39579 else 39580 s->first_atom = 1; 39581 if (JS_ReadObjectAtoms(s)) { 39582 obj = JS_EXCEPTION; 39583 } else { 39584 obj = JS_ReadObjectRec(s); 39585 } 39586 bc_reader_free(s); 39587 return obj; 39588 } 39589 39590 /*******************************************************************/ 39591 /* runtime functions & objects */ 39592 39593 static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val, 39594 int argc, JSValueConst *argv); 39595 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val, 39596 int argc, JSValueConst *argv); 39597 static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val, 39598 int argc, JSValueConst *argv); 39599 39600 static int check_function(JSContext *ctx, JSValueConst obj) 39601 { 39602 if (likely(JS_IsFunction(ctx, obj))) 39603 return 0; 39604 JS_ThrowTypeError(ctx, "not a function"); 39605 return -1; 39606 } 39607 39608 static int check_exception_free(JSContext *ctx, JSValue obj) 39609 { 39610 JS_FreeValue(ctx, obj); 39611 return JS_IsException(obj); 39612 } 39613 39614 static JSAtom find_atom(JSContext *ctx, const char *name) 39615 { 39616 JSAtom atom; 39617 int len; 39618 39619 if (*name == '[') { 39620 name++; 39621 len = strlen(name) - 1; 39622 /* We assume 8 bit non null strings, which is the case for these 39623 symbols */ 39624 for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { 39625 JSAtomStruct *p = ctx->rt->atom_array[atom]; 39626 JSString *str = p; 39627 if (str->len == len && !memcmp(str->u.str8, name, len)) 39628 return JS_DupAtom(ctx, atom); 39629 } 39630 abort(); 39631 } else { 39632 atom = JS_NewAtom(ctx, name); 39633 } 39634 return atom; 39635 } 39636 39637 static JSValue JS_NewObjectProtoList(JSContext *ctx, JSValueConst proto, 39638 const JSCFunctionListEntry *fields, int n_fields) 39639 { 39640 JSValue obj; 39641 obj = JS_NewObjectProtoClassAlloc(ctx, proto, JS_CLASS_OBJECT, n_fields); 39642 if (JS_IsException(obj)) 39643 return obj; 39644 if (JS_SetPropertyFunctionList(ctx, obj, fields, n_fields)) { 39645 JS_FreeValue(ctx, obj); 39646 return JS_EXCEPTION; 39647 } 39648 return obj; 39649 } 39650 39651 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, 39652 JSAtom atom, void *opaque) 39653 { 39654 const JSCFunctionListEntry *e = opaque; 39655 JSValue val, proto; 39656 39657 switch(e->def_type) { 39658 case JS_DEF_CFUNC: 39659 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, 39660 e->name, e->u.func.length, e->u.func.cproto, e->magic); 39661 break; 39662 case JS_DEF_PROP_STRING: 39663 val = JS_NewAtomString(ctx, e->u.str); 39664 break; 39665 case JS_DEF_OBJECT: 39666 /* XXX: could add a flag */ 39667 if (atom == JS_ATOM_Symbol_unscopables) 39668 proto = JS_NULL; 39669 else 39670 proto = ctx->class_proto[JS_CLASS_OBJECT]; 39671 val = JS_NewObjectProtoList(ctx, proto, 39672 e->u.prop_list.tab, e->u.prop_list.len); 39673 break; 39674 default: 39675 abort(); 39676 } 39677 return val; 39678 } 39679 39680 static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, 39681 JSAtom atom, 39682 const JSCFunctionListEntry *e) 39683 { 39684 JSValue val; 39685 int prop_flags = e->prop_flags; 39686 39687 switch(e->def_type) { 39688 case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ 39689 { 39690 JSAtom atom1 = find_atom(ctx, e->u.alias.name); 39691 switch (e->u.alias.base) { 39692 case -1: 39693 val = JS_GetProperty(ctx, obj, atom1); 39694 break; 39695 case 0: 39696 val = JS_GetProperty(ctx, ctx->global_obj, atom1); 39697 break; 39698 case 1: 39699 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1); 39700 break; 39701 default: 39702 abort(); 39703 } 39704 JS_FreeAtom(ctx, atom1); 39705 if (JS_IsException(val)) 39706 return -1; 39707 if (atom == JS_ATOM_Symbol_toPrimitive) { 39708 /* Symbol.toPrimitive functions are not writable */ 39709 prop_flags = JS_PROP_CONFIGURABLE; 39710 } else if (atom == JS_ATOM_Symbol_hasInstance) { 39711 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ 39712 prop_flags = 0; 39713 } 39714 } 39715 break; 39716 case JS_DEF_CFUNC: 39717 if (atom == JS_ATOM_Symbol_toPrimitive) { 39718 /* Symbol.toPrimitive functions are not writable */ 39719 prop_flags = JS_PROP_CONFIGURABLE; 39720 } else if (atom == JS_ATOM_Symbol_hasInstance) { 39721 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ 39722 prop_flags = 0; 39723 } 39724 if (JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, 39725 (void *)e, prop_flags) < 0) 39726 return -1; 39727 return 0; 39728 case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ 39729 case JS_DEF_CGETSET_MAGIC: 39730 { 39731 JSValue getter, setter; 39732 char buf[64]; 39733 39734 getter = JS_UNDEFINED; 39735 if (e->u.getset.get.generic) { 39736 snprintf(buf, sizeof(buf), "get %s", e->name); 39737 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic, 39738 buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter, 39739 e->magic); 39740 if (JS_IsException(getter)) 39741 return -1; 39742 } 39743 setter = JS_UNDEFINED; 39744 if (e->u.getset.set.generic) { 39745 snprintf(buf, sizeof(buf), "set %s", e->name); 39746 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic, 39747 buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter, 39748 e->magic); 39749 if (JS_IsException(setter)) { 39750 JS_FreeValue(ctx, getter); 39751 return -1; 39752 } 39753 } 39754 if (JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags) < 0) 39755 return -1; 39756 return 0; 39757 } 39758 break; 39759 case JS_DEF_PROP_INT32: 39760 val = JS_NewInt32(ctx, e->u.i32); 39761 break; 39762 case JS_DEF_PROP_INT64: 39763 val = JS_NewInt64(ctx, e->u.i64); 39764 break; 39765 case JS_DEF_PROP_DOUBLE: 39766 val = __JS_NewFloat64(ctx, e->u.f64); 39767 break; 39768 case JS_DEF_PROP_UNDEFINED: 39769 val = JS_UNDEFINED; 39770 break; 39771 case JS_DEF_PROP_ATOM: 39772 val = JS_AtomToValue(ctx, e->u.i32); 39773 break; 39774 case JS_DEF_PROP_BOOL: 39775 val = JS_NewBool(ctx, e->u.i32); 39776 break; 39777 case JS_DEF_PROP_STRING: 39778 case JS_DEF_OBJECT: 39779 if (JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, 39780 (void *)e, prop_flags) < 0) 39781 return -1; 39782 return 0; 39783 default: 39784 abort(); 39785 } 39786 if (JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags) < 0) 39787 return -1; 39788 return 0; 39789 } 39790 39791 int JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, 39792 const JSCFunctionListEntry *tab, int len) 39793 { 39794 int i, ret; 39795 39796 for (i = 0; i < len; i++) { 39797 const JSCFunctionListEntry *e = &tab[i]; 39798 JSAtom atom = find_atom(ctx, e->name); 39799 if (atom == JS_ATOM_NULL) 39800 return -1; 39801 ret = JS_InstantiateFunctionListItem(ctx, obj, atom, e); 39802 JS_FreeAtom(ctx, atom); 39803 if (ret) 39804 return -1; 39805 } 39806 return 0; 39807 } 39808 39809 int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, 39810 const JSCFunctionListEntry *tab, int len) 39811 { 39812 int i; 39813 for(i = 0; i < len; i++) { 39814 if (JS_AddModuleExport(ctx, m, tab[i].name)) 39815 return -1; 39816 } 39817 return 0; 39818 } 39819 39820 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, 39821 const JSCFunctionListEntry *tab, int len) 39822 { 39823 int i; 39824 JSValue val; 39825 39826 for(i = 0; i < len; i++) { 39827 const JSCFunctionListEntry *e = &tab[i]; 39828 switch(e->def_type) { 39829 case JS_DEF_CFUNC: 39830 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, 39831 e->name, e->u.func.length, e->u.func.cproto, e->magic); 39832 break; 39833 case JS_DEF_PROP_STRING: 39834 val = JS_NewString(ctx, e->u.str); 39835 break; 39836 case JS_DEF_PROP_INT32: 39837 val = JS_NewInt32(ctx, e->u.i32); 39838 break; 39839 case JS_DEF_PROP_INT64: 39840 val = JS_NewInt64(ctx, e->u.i64); 39841 break; 39842 case JS_DEF_PROP_DOUBLE: 39843 val = __JS_NewFloat64(ctx, e->u.f64); 39844 break; 39845 case JS_DEF_OBJECT: 39846 val = JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_OBJECT], 39847 e->u.prop_list.tab, e->u.prop_list.len); 39848 break; 39849 default: 39850 abort(); 39851 } 39852 if (JS_SetModuleExport(ctx, m, e->name, val)) 39853 return -1; 39854 } 39855 return 0; 39856 } 39857 39858 /* Note: 'func_obj' is not necessarily a constructor */ 39859 static int JS_SetConstructor2(JSContext *ctx, 39860 JSValueConst func_obj, 39861 JSValueConst proto, 39862 int proto_flags, int ctor_flags) 39863 { 39864 if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, 39865 JS_DupValue(ctx, proto), proto_flags) < 0) 39866 return -1; 39867 if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, 39868 JS_DupValue(ctx, func_obj), 39869 ctor_flags) < 0) 39870 return -1; 39871 set_cycle_flag(ctx, func_obj); 39872 set_cycle_flag(ctx, proto); 39873 return 0; 39874 } 39875 39876 /* return 0 if OK, -1 if exception */ 39877 int JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, 39878 JSValueConst proto) 39879 { 39880 return JS_SetConstructor2(ctx, func_obj, proto, 39881 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 39882 } 39883 39884 #define JS_NEW_CTOR_NO_GLOBAL (1 << 0) /* don't create a global binding */ 39885 #define JS_NEW_CTOR_PROTO_CLASS (1 << 1) /* the prototype class is 'class_id' instead of JS_CLASS_OBJECT */ 39886 #define JS_NEW_CTOR_PROTO_EXIST (1 << 2) /* the prototype is already defined */ 39887 #define JS_NEW_CTOR_READONLY (1 << 3) /* read-only constructor field */ 39888 39889 /* Return the constructor and. Define it as a global variable unless 39890 JS_NEW_CTOR_NO_GLOBAL is set. The new class inherit from 39891 parent_ctor if it is not JS_UNDEFINED. if class_id is != -1, 39892 class_proto[class_id] is set. */ 39893 static JSValue JS_NewCConstructor(JSContext *ctx, int class_id, const char *name, 39894 JSCFunction *func, int length, JSCFunctionEnum cproto, int magic, 39895 JSValueConst parent_ctor, 39896 const JSCFunctionListEntry *ctor_fields, int n_ctor_fields, 39897 const JSCFunctionListEntry *proto_fields, int n_proto_fields, 39898 int flags) 39899 { 39900 JSValue ctor = JS_UNDEFINED, proto, parent_proto; 39901 int proto_class_id, proto_flags, ctor_flags; 39902 39903 proto_flags = 0; 39904 if (flags & JS_NEW_CTOR_READONLY) { 39905 ctor_flags = JS_PROP_CONFIGURABLE; 39906 } else { 39907 ctor_flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE; 39908 } 39909 39910 if (JS_IsUndefined(parent_ctor)) { 39911 parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); 39912 parent_ctor = ctx->function_proto; 39913 } else { 39914 parent_proto = JS_GetProperty(ctx, parent_ctor, JS_ATOM_prototype); 39915 if (JS_IsException(parent_proto)) 39916 return JS_EXCEPTION; 39917 } 39918 39919 if (flags & JS_NEW_CTOR_PROTO_EXIST) { 39920 proto = JS_DupValue(ctx, ctx->class_proto[class_id]); 39921 } else { 39922 if (flags & JS_NEW_CTOR_PROTO_CLASS) 39923 proto_class_id = class_id; 39924 else 39925 proto_class_id = JS_CLASS_OBJECT; 39926 /* one additional field: constructor */ 39927 proto = JS_NewObjectProtoClassAlloc(ctx, parent_proto, proto_class_id, 39928 n_proto_fields + 1); 39929 if (JS_IsException(proto)) 39930 goto fail; 39931 if (class_id >= 0) 39932 ctx->class_proto[class_id] = JS_DupValue(ctx, proto); 39933 } 39934 if (JS_SetPropertyFunctionList(ctx, proto, proto_fields, n_proto_fields)) 39935 goto fail; 39936 39937 /* additional fields: name, length, prototype */ 39938 ctor = JS_NewCFunction3(ctx, func, name, length, cproto, magic, parent_ctor, 39939 n_ctor_fields + 3); 39940 if (JS_IsException(ctor)) 39941 goto fail; 39942 if (JS_SetPropertyFunctionList(ctx, ctor, ctor_fields, n_ctor_fields)) 39943 goto fail; 39944 if (!(flags & JS_NEW_CTOR_NO_GLOBAL)) { 39945 if (JS_DefinePropertyValueStr(ctx, ctx->global_obj, name, 39946 JS_DupValue(ctx, ctor), 39947 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) 39948 goto fail; 39949 } 39950 JS_SetConstructor2(ctx, ctor, proto, proto_flags, ctor_flags); 39951 39952 JS_FreeValue(ctx, proto); 39953 JS_FreeValue(ctx, parent_proto); 39954 return ctor; 39955 fail: 39956 JS_FreeValue(ctx, proto); 39957 JS_FreeValue(ctx, parent_proto); 39958 JS_FreeValue(ctx, ctor); 39959 return JS_EXCEPTION; 39960 } 39961 39962 static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val, 39963 int argc, JSValueConst *argv) 39964 { 39965 return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1); 39966 } 39967 39968 static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, 39969 int argc, JSValueConst *argv) 39970 { 39971 double d; 39972 39973 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 39974 return JS_EXCEPTION; 39975 return JS_NewBool(ctx, isnan(d)); 39976 } 39977 39978 static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, 39979 int argc, JSValueConst *argv) 39980 { 39981 double d; 39982 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 39983 return JS_EXCEPTION; 39984 return JS_NewBool(ctx, isfinite(d)); 39985 } 39986 39987 /* Object class */ 39988 39989 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) 39990 { 39991 int tag = JS_VALUE_GET_NORM_TAG(val); 39992 JSValue obj; 39993 39994 switch(tag) { 39995 default: 39996 case JS_TAG_NULL: 39997 case JS_TAG_UNDEFINED: 39998 return JS_ThrowTypeError(ctx, "cannot convert to object"); 39999 case JS_TAG_OBJECT: 40000 case JS_TAG_EXCEPTION: 40001 return JS_DupValue(ctx, val); 40002 case JS_TAG_SHORT_BIG_INT: 40003 case JS_TAG_BIG_INT: 40004 obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); 40005 goto set_value; 40006 case JS_TAG_INT: 40007 case JS_TAG_FLOAT64: 40008 obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); 40009 goto set_value; 40010 case JS_TAG_STRING: 40011 case JS_TAG_STRING_ROPE: 40012 /* XXX: should call the string constructor */ 40013 { 40014 JSValue str; 40015 str = JS_ToString(ctx, val); /* ensure that we never store a rope */ 40016 if (JS_IsException(str)) 40017 return JS_EXCEPTION; 40018 obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); 40019 if (!JS_IsException(obj)) { 40020 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, 40021 JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0); 40022 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str)); 40023 } 40024 JS_FreeValue(ctx, str); 40025 return obj; 40026 } 40027 case JS_TAG_BOOL: 40028 obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); 40029 goto set_value; 40030 case JS_TAG_SYMBOL: 40031 obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); 40032 set_value: 40033 if (!JS_IsException(obj)) 40034 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); 40035 return obj; 40036 } 40037 } 40038 40039 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) 40040 { 40041 JSValue obj = JS_ToObject(ctx, val); 40042 JS_FreeValue(ctx, val); 40043 return obj; 40044 } 40045 40046 static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, 40047 JSValueConst desc) 40048 { 40049 JSValue val, getter, setter; 40050 int flags; 40051 40052 if (!JS_IsObject(desc)) { 40053 JS_ThrowTypeErrorNotAnObject(ctx); 40054 return -1; 40055 } 40056 flags = 0; 40057 val = JS_UNDEFINED; 40058 getter = JS_UNDEFINED; 40059 setter = JS_UNDEFINED; 40060 if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { 40061 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); 40062 if (JS_IsException(prop)) 40063 goto fail; 40064 flags |= JS_PROP_HAS_ENUMERABLE; 40065 if (JS_ToBoolFree(ctx, prop)) 40066 flags |= JS_PROP_ENUMERABLE; 40067 } 40068 if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { 40069 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); 40070 if (JS_IsException(prop)) 40071 goto fail; 40072 flags |= JS_PROP_HAS_CONFIGURABLE; 40073 if (JS_ToBoolFree(ctx, prop)) 40074 flags |= JS_PROP_CONFIGURABLE; 40075 } 40076 if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { 40077 flags |= JS_PROP_HAS_VALUE; 40078 val = JS_GetProperty(ctx, desc, JS_ATOM_value); 40079 if (JS_IsException(val)) 40080 goto fail; 40081 } 40082 if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { 40083 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); 40084 if (JS_IsException(prop)) 40085 goto fail; 40086 flags |= JS_PROP_HAS_WRITABLE; 40087 if (JS_ToBoolFree(ctx, prop)) 40088 flags |= JS_PROP_WRITABLE; 40089 } 40090 if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { 40091 flags |= JS_PROP_HAS_GET; 40092 getter = JS_GetProperty(ctx, desc, JS_ATOM_get); 40093 if (JS_IsException(getter) || 40094 !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { 40095 JS_ThrowTypeError(ctx, "invalid getter"); 40096 goto fail; 40097 } 40098 } 40099 if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { 40100 flags |= JS_PROP_HAS_SET; 40101 setter = JS_GetProperty(ctx, desc, JS_ATOM_set); 40102 if (JS_IsException(setter) || 40103 !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { 40104 JS_ThrowTypeError(ctx, "invalid setter"); 40105 goto fail; 40106 } 40107 } 40108 if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && 40109 (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { 40110 JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); 40111 goto fail; 40112 } 40113 d->flags = flags; 40114 d->value = val; 40115 d->getter = getter; 40116 d->setter = setter; 40117 return 0; 40118 fail: 40119 JS_FreeValue(ctx, val); 40120 JS_FreeValue(ctx, getter); 40121 JS_FreeValue(ctx, setter); 40122 return -1; 40123 } 40124 40125 static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, 40126 JSAtom prop, JSValueConst desc, 40127 int flags) 40128 { 40129 JSPropertyDescriptor d; 40130 int ret; 40131 40132 if (js_obj_to_desc(ctx, &d, desc) < 0) 40133 return -1; 40134 40135 ret = JS_DefineProperty(ctx, obj, prop, 40136 d.value, d.getter, d.setter, d.flags | flags); 40137 js_free_desc(ctx, &d); 40138 return ret; 40139 } 40140 40141 static __exception int JS_ObjectDefineProperties(JSContext *ctx, 40142 JSValueConst obj, 40143 JSValueConst properties) 40144 { 40145 JSValue props, desc; 40146 JSObject *p; 40147 JSPropertyEnum *atoms; 40148 uint32_t len, i; 40149 int ret = -1; 40150 40151 if (!JS_IsObject(obj)) { 40152 JS_ThrowTypeErrorNotAnObject(ctx); 40153 return -1; 40154 } 40155 desc = JS_UNDEFINED; 40156 props = JS_ToObject(ctx, properties); 40157 if (JS_IsException(props)) 40158 return -1; 40159 p = JS_VALUE_GET_OBJ(props); 40160 /* XXX: not done in the same order as the spec */ 40161 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) 40162 goto exception; 40163 for(i = 0; i < len; i++) { 40164 JS_FreeValue(ctx, desc); 40165 desc = JS_GetProperty(ctx, props, atoms[i].atom); 40166 if (JS_IsException(desc)) 40167 goto exception; 40168 if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) 40169 goto exception; 40170 } 40171 ret = 0; 40172 40173 exception: 40174 JS_FreePropertyEnum(ctx, atoms, len); 40175 JS_FreeValue(ctx, props); 40176 JS_FreeValue(ctx, desc); 40177 return ret; 40178 } 40179 40180 static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target, 40181 int argc, JSValueConst *argv) 40182 { 40183 JSValue ret; 40184 if (!JS_IsUndefined(new_target) && 40185 JS_VALUE_GET_OBJ(new_target) != 40186 JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { 40187 ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); 40188 } else { 40189 int tag = JS_VALUE_GET_NORM_TAG(argv[0]); 40190 switch(tag) { 40191 case JS_TAG_NULL: 40192 case JS_TAG_UNDEFINED: 40193 ret = JS_NewObject(ctx); 40194 break; 40195 default: 40196 ret = JS_ToObject(ctx, argv[0]); 40197 break; 40198 } 40199 } 40200 return ret; 40201 } 40202 40203 static JSValue js_object_create(JSContext *ctx, JSValueConst this_val, 40204 int argc, JSValueConst *argv) 40205 { 40206 JSValueConst proto, props; 40207 JSValue obj; 40208 40209 proto = argv[0]; 40210 if (!JS_IsObject(proto) && !JS_IsNull(proto)) 40211 return JS_ThrowTypeError(ctx, "not a prototype"); 40212 obj = JS_NewObjectProto(ctx, proto); 40213 if (JS_IsException(obj)) 40214 return JS_EXCEPTION; 40215 props = argv[1]; 40216 if (!JS_IsUndefined(props)) { 40217 if (JS_ObjectDefineProperties(ctx, obj, props)) { 40218 JS_FreeValue(ctx, obj); 40219 return JS_EXCEPTION; 40220 } 40221 } 40222 return obj; 40223 } 40224 40225 static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, 40226 int argc, JSValueConst *argv, int magic) 40227 { 40228 JSValueConst val; 40229 40230 val = argv[0]; 40231 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { 40232 /* ES6 feature non compatible with ES5.1: primitive types are 40233 accepted */ 40234 if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || 40235 JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) 40236 return JS_ThrowTypeErrorNotAnObject(ctx); 40237 } 40238 return JS_GetPrototype(ctx, val); 40239 } 40240 40241 static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, 40242 int argc, JSValueConst *argv) 40243 { 40244 JSValueConst obj; 40245 obj = argv[0]; 40246 if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) 40247 return JS_EXCEPTION; 40248 return JS_DupValue(ctx, obj); 40249 } 40250 40251 /* magic = 1 if called as Reflect.defineProperty */ 40252 static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, 40253 int argc, JSValueConst *argv, int magic) 40254 { 40255 JSValueConst obj, prop, desc; 40256 int ret, flags; 40257 JSAtom atom; 40258 40259 obj = argv[0]; 40260 prop = argv[1]; 40261 desc = argv[2]; 40262 40263 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 40264 return JS_ThrowTypeErrorNotAnObject(ctx); 40265 atom = JS_ValueToAtom(ctx, prop); 40266 if (unlikely(atom == JS_ATOM_NULL)) 40267 return JS_EXCEPTION; 40268 flags = 0; 40269 if (!magic) 40270 flags |= JS_PROP_THROW; 40271 ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); 40272 JS_FreeAtom(ctx, atom); 40273 if (ret < 0) { 40274 return JS_EXCEPTION; 40275 } else if (magic) { 40276 return JS_NewBool(ctx, ret); 40277 } else { 40278 return JS_DupValue(ctx, obj); 40279 } 40280 } 40281 40282 static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val, 40283 int argc, JSValueConst *argv) 40284 { 40285 // defineProperties(obj, properties) 40286 JSValueConst obj = argv[0]; 40287 40288 if (JS_ObjectDefineProperties(ctx, obj, argv[1])) 40289 return JS_EXCEPTION; 40290 else 40291 return JS_DupValue(ctx, obj); 40292 } 40293 40294 /* magic = 1 if called as __defineSetter__ */ 40295 static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val, 40296 int argc, JSValueConst *argv, int magic) 40297 { 40298 JSValue obj; 40299 JSValueConst prop, value, get, set; 40300 int ret, flags; 40301 JSAtom atom; 40302 40303 prop = argv[0]; 40304 value = argv[1]; 40305 40306 obj = JS_ToObject(ctx, this_val); 40307 if (JS_IsException(obj)) 40308 return JS_EXCEPTION; 40309 40310 if (check_function(ctx, value)) { 40311 JS_FreeValue(ctx, obj); 40312 return JS_EXCEPTION; 40313 } 40314 atom = JS_ValueToAtom(ctx, prop); 40315 if (unlikely(atom == JS_ATOM_NULL)) { 40316 JS_FreeValue(ctx, obj); 40317 return JS_EXCEPTION; 40318 } 40319 flags = JS_PROP_THROW | 40320 JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | 40321 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; 40322 if (magic) { 40323 get = JS_UNDEFINED; 40324 set = value; 40325 flags |= JS_PROP_HAS_SET; 40326 } else { 40327 get = value; 40328 set = JS_UNDEFINED; 40329 flags |= JS_PROP_HAS_GET; 40330 } 40331 ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); 40332 JS_FreeValue(ctx, obj); 40333 JS_FreeAtom(ctx, atom); 40334 if (ret < 0) { 40335 return JS_EXCEPTION; 40336 } else { 40337 return JS_UNDEFINED; 40338 } 40339 } 40340 40341 static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val, 40342 int argc, JSValueConst *argv, int magic) 40343 { 40344 JSValueConst prop; 40345 JSAtom atom; 40346 JSValue ret, obj; 40347 JSPropertyDescriptor desc; 40348 int res, flags; 40349 40350 if (magic) { 40351 /* Reflect.getOwnPropertyDescriptor case */ 40352 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) 40353 return JS_ThrowTypeErrorNotAnObject(ctx); 40354 obj = JS_DupValue(ctx, argv[0]); 40355 } else { 40356 obj = JS_ToObject(ctx, argv[0]); 40357 if (JS_IsException(obj)) 40358 return obj; 40359 } 40360 prop = argv[1]; 40361 atom = JS_ValueToAtom(ctx, prop); 40362 if (unlikely(atom == JS_ATOM_NULL)) 40363 goto exception; 40364 ret = JS_UNDEFINED; 40365 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 40366 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); 40367 if (res < 0) 40368 goto exception; 40369 if (res) { 40370 ret = JS_NewObject(ctx); 40371 if (JS_IsException(ret)) 40372 goto exception1; 40373 flags = JS_PROP_C_W_E | JS_PROP_THROW; 40374 if (desc.flags & JS_PROP_GETSET) { 40375 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 40376 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) 40377 goto exception1; 40378 } else { 40379 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 40380 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, 40381 JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0) 40382 goto exception1; 40383 } 40384 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, 40385 JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0 40386 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, 40387 JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0) 40388 goto exception1; 40389 js_free_desc(ctx, &desc); 40390 } 40391 } 40392 JS_FreeAtom(ctx, atom); 40393 JS_FreeValue(ctx, obj); 40394 return ret; 40395 40396 exception1: 40397 js_free_desc(ctx, &desc); 40398 JS_FreeValue(ctx, ret); 40399 exception: 40400 JS_FreeAtom(ctx, atom); 40401 JS_FreeValue(ctx, obj); 40402 return JS_EXCEPTION; 40403 } 40404 40405 static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val, 40406 int argc, JSValueConst *argv) 40407 { 40408 //getOwnPropertyDescriptors(obj) 40409 JSValue obj, r; 40410 JSObject *p; 40411 JSPropertyEnum *props; 40412 uint32_t len, i; 40413 40414 r = JS_UNDEFINED; 40415 obj = JS_ToObject(ctx, argv[0]); 40416 if (JS_IsException(obj)) 40417 return JS_EXCEPTION; 40418 40419 p = JS_VALUE_GET_OBJ(obj); 40420 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, 40421 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) 40422 goto exception; 40423 r = JS_NewObject(ctx); 40424 if (JS_IsException(r)) 40425 goto exception; 40426 for(i = 0; i < len; i++) { 40427 JSValue atomValue, desc; 40428 JSValueConst args[2]; 40429 40430 atomValue = JS_AtomToValue(ctx, props[i].atom); 40431 if (JS_IsException(atomValue)) 40432 goto exception; 40433 args[0] = obj; 40434 args[1] = atomValue; 40435 desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); 40436 JS_FreeValue(ctx, atomValue); 40437 if (JS_IsException(desc)) 40438 goto exception; 40439 if (!JS_IsUndefined(desc)) { 40440 if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, 40441 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 40442 goto exception; 40443 } 40444 } 40445 JS_FreePropertyEnum(ctx, props, len); 40446 JS_FreeValue(ctx, obj); 40447 return r; 40448 40449 exception: 40450 JS_FreePropertyEnum(ctx, props, len); 40451 JS_FreeValue(ctx, obj); 40452 JS_FreeValue(ctx, r); 40453 return JS_EXCEPTION; 40454 } 40455 40456 static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, 40457 int flags, int kind) 40458 { 40459 JSValue obj, r, val, key, value; 40460 JSObject *p; 40461 JSPropertyEnum *atoms; 40462 uint32_t len, i, j; 40463 40464 r = JS_UNDEFINED; 40465 val = JS_UNDEFINED; 40466 obj = JS_ToObject(ctx, obj1); 40467 if (JS_IsException(obj)) 40468 return JS_EXCEPTION; 40469 p = JS_VALUE_GET_OBJ(obj); 40470 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) 40471 goto exception; 40472 r = JS_NewArray(ctx); 40473 if (JS_IsException(r)) 40474 goto exception; 40475 for(j = i = 0; i < len; i++) { 40476 JSAtom atom = atoms[i].atom; 40477 if (flags & JS_GPN_ENUM_ONLY) { 40478 JSPropertyDescriptor desc; 40479 int res; 40480 40481 /* Check if property is still enumerable */ 40482 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 40483 if (res < 0) 40484 goto exception; 40485 if (!res) 40486 continue; 40487 js_free_desc(ctx, &desc); 40488 if (!(desc.flags & JS_PROP_ENUMERABLE)) 40489 continue; 40490 } 40491 switch(kind) { 40492 default: 40493 case JS_ITERATOR_KIND_KEY: 40494 val = JS_AtomToValue(ctx, atom); 40495 if (JS_IsException(val)) 40496 goto exception; 40497 break; 40498 case JS_ITERATOR_KIND_VALUE: 40499 val = JS_GetProperty(ctx, obj, atom); 40500 if (JS_IsException(val)) 40501 goto exception; 40502 break; 40503 case JS_ITERATOR_KIND_KEY_AND_VALUE: 40504 val = JS_NewArray(ctx); 40505 if (JS_IsException(val)) 40506 goto exception; 40507 key = JS_AtomToValue(ctx, atom); 40508 if (JS_IsException(key)) 40509 goto exception1; 40510 if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) 40511 goto exception1; 40512 value = JS_GetProperty(ctx, obj, atom); 40513 if (JS_IsException(value)) 40514 goto exception1; 40515 if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) 40516 goto exception1; 40517 break; 40518 } 40519 if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) 40520 goto exception; 40521 } 40522 goto done; 40523 40524 exception1: 40525 JS_FreeValue(ctx, val); 40526 exception: 40527 JS_FreeValue(ctx, r); 40528 r = JS_EXCEPTION; 40529 done: 40530 JS_FreePropertyEnum(ctx, atoms, len); 40531 JS_FreeValue(ctx, obj); 40532 return r; 40533 } 40534 40535 static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val, 40536 int argc, JSValueConst *argv) 40537 { 40538 return JS_GetOwnPropertyNames2(ctx, argv[0], 40539 JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); 40540 } 40541 40542 static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val, 40543 int argc, JSValueConst *argv) 40544 { 40545 return JS_GetOwnPropertyNames2(ctx, argv[0], 40546 JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); 40547 } 40548 40549 static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, 40550 int argc, JSValueConst *argv, int kind) 40551 { 40552 return JS_GetOwnPropertyNames2(ctx, argv[0], 40553 JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); 40554 } 40555 40556 static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val, 40557 int argc, JSValueConst *argv, int reflect) 40558 { 40559 JSValueConst obj; 40560 int ret; 40561 40562 obj = argv[0]; 40563 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 40564 if (reflect) 40565 return JS_ThrowTypeErrorNotAnObject(ctx); 40566 else 40567 return JS_FALSE; 40568 } 40569 ret = JS_IsExtensible(ctx, obj); 40570 if (ret < 0) 40571 return JS_EXCEPTION; 40572 else 40573 return JS_NewBool(ctx, ret); 40574 } 40575 40576 static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val, 40577 int argc, JSValueConst *argv, int reflect) 40578 { 40579 JSValueConst obj; 40580 int ret; 40581 40582 obj = argv[0]; 40583 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 40584 if (reflect) 40585 return JS_ThrowTypeErrorNotAnObject(ctx); 40586 else 40587 return JS_DupValue(ctx, obj); 40588 } 40589 ret = JS_PreventExtensions(ctx, obj); 40590 if (ret < 0) 40591 return JS_EXCEPTION; 40592 if (reflect) { 40593 return JS_NewBool(ctx, ret); 40594 } else { 40595 if (!ret) 40596 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); 40597 return JS_DupValue(ctx, obj); 40598 } 40599 } 40600 40601 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, 40602 int argc, JSValueConst *argv) 40603 { 40604 JSValue obj; 40605 JSAtom atom; 40606 JSObject *p; 40607 BOOL ret; 40608 40609 atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ 40610 if (unlikely(atom == JS_ATOM_NULL)) 40611 return JS_EXCEPTION; 40612 obj = JS_ToObject(ctx, this_val); 40613 if (JS_IsException(obj)) { 40614 JS_FreeAtom(ctx, atom); 40615 return obj; 40616 } 40617 p = JS_VALUE_GET_OBJ(obj); 40618 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); 40619 JS_FreeAtom(ctx, atom); 40620 JS_FreeValue(ctx, obj); 40621 if (ret < 0) 40622 return JS_EXCEPTION; 40623 else 40624 return JS_NewBool(ctx, ret); 40625 } 40626 40627 static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, 40628 int argc, JSValueConst *argv) 40629 { 40630 JSValue obj; 40631 JSAtom atom; 40632 JSObject *p; 40633 BOOL ret; 40634 40635 obj = JS_ToObject(ctx, argv[0]); 40636 if (JS_IsException(obj)) 40637 return obj; 40638 atom = JS_ValueToAtom(ctx, argv[1]); 40639 if (unlikely(atom == JS_ATOM_NULL)) { 40640 JS_FreeValue(ctx, obj); 40641 return JS_EXCEPTION; 40642 } 40643 p = JS_VALUE_GET_OBJ(obj); 40644 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); 40645 JS_FreeAtom(ctx, atom); 40646 JS_FreeValue(ctx, obj); 40647 if (ret < 0) 40648 return JS_EXCEPTION; 40649 else 40650 return JS_NewBool(ctx, ret); 40651 } 40652 40653 static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, 40654 int argc, JSValueConst *argv) 40655 { 40656 return JS_ToObject(ctx, this_val); 40657 } 40658 40659 static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, 40660 int argc, JSValueConst *argv) 40661 { 40662 JSValue obj, tag; 40663 int is_array; 40664 JSAtom atom; 40665 JSObject *p; 40666 40667 if (JS_IsNull(this_val)) { 40668 tag = js_new_string8(ctx, "Null"); 40669 } else if (JS_IsUndefined(this_val)) { 40670 tag = js_new_string8(ctx, "Undefined"); 40671 } else { 40672 obj = JS_ToObject(ctx, this_val); 40673 if (JS_IsException(obj)) 40674 return obj; 40675 is_array = JS_IsArray(ctx, obj); 40676 if (is_array < 0) { 40677 JS_FreeValue(ctx, obj); 40678 return JS_EXCEPTION; 40679 } 40680 if (is_array) { 40681 atom = JS_ATOM_Array; 40682 } else if (JS_IsFunction(ctx, obj)) { 40683 atom = JS_ATOM_Function; 40684 } else { 40685 p = JS_VALUE_GET_OBJ(obj); 40686 switch(p->class_id) { 40687 case JS_CLASS_STRING: 40688 case JS_CLASS_ARGUMENTS: 40689 case JS_CLASS_MAPPED_ARGUMENTS: 40690 case JS_CLASS_ERROR: 40691 case JS_CLASS_BOOLEAN: 40692 case JS_CLASS_NUMBER: 40693 case JS_CLASS_DATE: 40694 case JS_CLASS_REGEXP: 40695 atom = ctx->rt->class_array[p->class_id].class_name; 40696 break; 40697 default: 40698 atom = JS_ATOM_Object; 40699 break; 40700 } 40701 } 40702 tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); 40703 JS_FreeValue(ctx, obj); 40704 if (JS_IsException(tag)) 40705 return JS_EXCEPTION; 40706 if (!JS_IsString(tag)) { 40707 JS_FreeValue(ctx, tag); 40708 tag = JS_AtomToString(ctx, atom); 40709 } 40710 } 40711 return JS_ConcatString3(ctx, "[object ", tag, "]"); 40712 } 40713 40714 static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val, 40715 int argc, JSValueConst *argv) 40716 { 40717 return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); 40718 } 40719 40720 static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val, 40721 int argc, JSValueConst *argv) 40722 { 40723 // Object.assign(obj, source1) 40724 JSValue obj, s; 40725 int i; 40726 40727 s = JS_UNDEFINED; 40728 obj = JS_ToObject(ctx, argv[0]); 40729 if (JS_IsException(obj)) 40730 goto exception; 40731 for (i = 1; i < argc; i++) { 40732 if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { 40733 s = JS_ToObject(ctx, argv[i]); 40734 if (JS_IsException(s)) 40735 goto exception; 40736 if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) 40737 goto exception; 40738 JS_FreeValue(ctx, s); 40739 } 40740 } 40741 return obj; 40742 exception: 40743 JS_FreeValue(ctx, obj); 40744 JS_FreeValue(ctx, s); 40745 return JS_EXCEPTION; 40746 } 40747 40748 static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, 40749 int argc, JSValueConst *argv, int freeze_flag) 40750 { 40751 JSValueConst obj = argv[0]; 40752 JSObject *p; 40753 JSPropertyEnum *props; 40754 uint32_t len, i; 40755 int flags, desc_flags, res; 40756 40757 if (!JS_IsObject(obj)) 40758 return JS_DupValue(ctx, obj); 40759 40760 res = JS_PreventExtensions(ctx, obj); 40761 if (res < 0) 40762 return JS_EXCEPTION; 40763 if (!res) { 40764 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); 40765 } 40766 40767 p = JS_VALUE_GET_OBJ(obj); 40768 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; 40769 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) 40770 return JS_EXCEPTION; 40771 40772 for(i = 0; i < len; i++) { 40773 JSPropertyDescriptor desc; 40774 JSAtom prop = props[i].atom; 40775 40776 desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; 40777 if (freeze_flag) { 40778 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 40779 if (res < 0) 40780 goto exception; 40781 if (res) { 40782 if (desc.flags & JS_PROP_WRITABLE) 40783 desc_flags |= JS_PROP_HAS_WRITABLE; 40784 js_free_desc(ctx, &desc); 40785 } 40786 } 40787 if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, 40788 JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) 40789 goto exception; 40790 } 40791 JS_FreePropertyEnum(ctx, props, len); 40792 return JS_DupValue(ctx, obj); 40793 40794 exception: 40795 JS_FreePropertyEnum(ctx, props, len); 40796 return JS_EXCEPTION; 40797 } 40798 40799 static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, 40800 int argc, JSValueConst *argv, int is_frozen) 40801 { 40802 JSValueConst obj = argv[0]; 40803 JSObject *p; 40804 JSPropertyEnum *props; 40805 uint32_t len, i; 40806 int flags, res; 40807 40808 if (!JS_IsObject(obj)) 40809 return JS_TRUE; 40810 40811 p = JS_VALUE_GET_OBJ(obj); 40812 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; 40813 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) 40814 return JS_EXCEPTION; 40815 40816 for(i = 0; i < len; i++) { 40817 JSPropertyDescriptor desc; 40818 JSAtom prop = props[i].atom; 40819 40820 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 40821 if (res < 0) 40822 goto exception; 40823 if (res) { 40824 js_free_desc(ctx, &desc); 40825 if ((desc.flags & JS_PROP_CONFIGURABLE) 40826 || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { 40827 res = FALSE; 40828 goto done; 40829 } 40830 } 40831 } 40832 res = JS_IsExtensible(ctx, obj); 40833 if (res < 0) 40834 return JS_EXCEPTION; 40835 res ^= 1; 40836 done: 40837 JS_FreePropertyEnum(ctx, props, len); 40838 return JS_NewBool(ctx, res); 40839 40840 exception: 40841 JS_FreePropertyEnum(ctx, props, len); 40842 return JS_EXCEPTION; 40843 } 40844 40845 static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, 40846 int argc, JSValueConst *argv) 40847 { 40848 JSValue obj, iter, next_method = JS_UNDEFINED; 40849 JSValueConst iterable; 40850 BOOL done; 40851 40852 /* RequireObjectCoercible() not necessary because it is tested in 40853 JS_GetIterator() by JS_GetProperty() */ 40854 iterable = argv[0]; 40855 40856 obj = JS_NewObject(ctx); 40857 if (JS_IsException(obj)) 40858 return obj; 40859 40860 iter = JS_GetIterator(ctx, iterable, FALSE); 40861 if (JS_IsException(iter)) 40862 goto fail; 40863 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 40864 if (JS_IsException(next_method)) 40865 goto fail; 40866 40867 for(;;) { 40868 JSValue key, value, item; 40869 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 40870 if (JS_IsException(item)) 40871 goto fail; 40872 if (done) 40873 break; 40874 40875 key = JS_UNDEFINED; 40876 value = JS_UNDEFINED; 40877 if (!JS_IsObject(item)) { 40878 JS_ThrowTypeErrorNotAnObject(ctx); 40879 goto fail1; 40880 } 40881 key = JS_GetPropertyUint32(ctx, item, 0); 40882 if (JS_IsException(key)) 40883 goto fail1; 40884 value = JS_GetPropertyUint32(ctx, item, 1); 40885 if (JS_IsException(value)) { 40886 JS_FreeValue(ctx, key); 40887 goto fail1; 40888 } 40889 if (JS_DefinePropertyValueValue(ctx, obj, key, value, 40890 JS_PROP_C_W_E | JS_PROP_THROW) < 0) { 40891 fail1: 40892 JS_FreeValue(ctx, item); 40893 goto fail; 40894 } 40895 JS_FreeValue(ctx, item); 40896 } 40897 JS_FreeValue(ctx, next_method); 40898 JS_FreeValue(ctx, iter); 40899 return obj; 40900 fail: 40901 if (JS_IsObject(iter)) { 40902 /* close the iterator object, preserving pending exception */ 40903 JS_IteratorClose(ctx, iter, TRUE); 40904 } 40905 JS_FreeValue(ctx, next_method); 40906 JS_FreeValue(ctx, iter); 40907 JS_FreeValue(ctx, obj); 40908 return JS_EXCEPTION; 40909 } 40910 40911 static JSValue js_object_is(JSContext *ctx, JSValueConst this_val, 40912 int argc, JSValueConst *argv) 40913 { 40914 return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); 40915 } 40916 40917 static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, 40918 JSValueConst defaultConstructor) 40919 { 40920 JSValue ctor, species; 40921 40922 if (!JS_IsObject(obj)) 40923 return JS_ThrowTypeErrorNotAnObject(ctx); 40924 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); 40925 if (JS_IsException(ctor)) 40926 return ctor; 40927 if (JS_IsUndefined(ctor)) 40928 return JS_DupValue(ctx, defaultConstructor); 40929 if (!JS_IsObject(ctor)) { 40930 JS_FreeValue(ctx, ctor); 40931 return JS_ThrowTypeErrorNotAnObject(ctx); 40932 } 40933 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); 40934 JS_FreeValue(ctx, ctor); 40935 if (JS_IsException(species)) 40936 return species; 40937 if (JS_IsUndefined(species) || JS_IsNull(species)) 40938 return JS_DupValue(ctx, defaultConstructor); 40939 if (!JS_IsConstructor(ctx, species)) { 40940 JS_ThrowTypeErrorNotAConstructor(ctx, species); 40941 JS_FreeValue(ctx, species); 40942 return JS_EXCEPTION; 40943 } 40944 return species; 40945 } 40946 40947 static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) 40948 { 40949 JSValue val, ret; 40950 40951 val = JS_ToObject(ctx, this_val); 40952 if (JS_IsException(val)) 40953 return val; 40954 ret = JS_GetPrototype(ctx, val); 40955 JS_FreeValue(ctx, val); 40956 return ret; 40957 } 40958 40959 static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, 40960 JSValueConst proto) 40961 { 40962 if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) 40963 return JS_ThrowTypeErrorNotAnObject(ctx); 40964 if (!JS_IsObject(proto) && !JS_IsNull(proto)) 40965 return JS_UNDEFINED; 40966 if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) 40967 return JS_EXCEPTION; 40968 else 40969 return JS_UNDEFINED; 40970 } 40971 40972 static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, 40973 int argc, JSValueConst *argv) 40974 { 40975 JSValue obj, v1; 40976 JSValueConst v; 40977 int res; 40978 40979 v = argv[0]; 40980 if (!JS_IsObject(v)) 40981 return JS_FALSE; 40982 obj = JS_ToObject(ctx, this_val); 40983 if (JS_IsException(obj)) 40984 return JS_EXCEPTION; 40985 v1 = JS_DupValue(ctx, v); 40986 for(;;) { 40987 v1 = JS_GetPrototypeFree(ctx, v1); 40988 if (JS_IsException(v1)) 40989 goto exception; 40990 if (JS_IsNull(v1)) { 40991 res = FALSE; 40992 break; 40993 } 40994 if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { 40995 res = TRUE; 40996 break; 40997 } 40998 /* avoid infinite loop (possible with proxies) */ 40999 if (js_poll_interrupts(ctx)) 41000 goto exception; 41001 } 41002 JS_FreeValue(ctx, v1); 41003 JS_FreeValue(ctx, obj); 41004 return JS_NewBool(ctx, res); 41005 41006 exception: 41007 JS_FreeValue(ctx, v1); 41008 JS_FreeValue(ctx, obj); 41009 return JS_EXCEPTION; 41010 } 41011 41012 static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, 41013 int argc, JSValueConst *argv) 41014 { 41015 JSValue obj = JS_UNDEFINED, res = JS_EXCEPTION; 41016 JSAtom prop; 41017 JSPropertyDescriptor desc; 41018 int has_prop; 41019 41020 prop = JS_ValueToAtom(ctx, argv[0]); 41021 if (unlikely(prop == JS_ATOM_NULL)) 41022 goto exception; 41023 obj = JS_ToObject(ctx, this_val); 41024 if (JS_IsException(obj)) 41025 goto exception; 41026 41027 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); 41028 if (has_prop < 0) 41029 goto exception; 41030 if (has_prop) { 41031 res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE); 41032 js_free_desc(ctx, &desc); 41033 } else { 41034 res = JS_FALSE; 41035 } 41036 41037 exception: 41038 JS_FreeAtom(ctx, prop); 41039 JS_FreeValue(ctx, obj); 41040 return res; 41041 } 41042 41043 static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, 41044 int argc, JSValueConst *argv, int setter) 41045 { 41046 JSValue obj, res = JS_EXCEPTION; 41047 JSAtom prop = JS_ATOM_NULL; 41048 JSPropertyDescriptor desc; 41049 int has_prop; 41050 41051 obj = JS_ToObject(ctx, this_val); 41052 if (JS_IsException(obj)) 41053 goto exception; 41054 prop = JS_ValueToAtom(ctx, argv[0]); 41055 if (unlikely(prop == JS_ATOM_NULL)) 41056 goto exception; 41057 41058 for (;;) { 41059 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); 41060 if (has_prop < 0) 41061 goto exception; 41062 if (has_prop) { 41063 if (desc.flags & JS_PROP_GETSET) 41064 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); 41065 else 41066 res = JS_UNDEFINED; 41067 js_free_desc(ctx, &desc); 41068 break; 41069 } 41070 obj = JS_GetPrototypeFree(ctx, obj); 41071 if (JS_IsException(obj)) 41072 goto exception; 41073 if (JS_IsNull(obj)) { 41074 res = JS_UNDEFINED; 41075 break; 41076 } 41077 /* avoid infinite loop (possible with proxies) */ 41078 if (js_poll_interrupts(ctx)) 41079 goto exception; 41080 } 41081 41082 exception: 41083 JS_FreeAtom(ctx, prop); 41084 JS_FreeValue(ctx, obj); 41085 return res; 41086 } 41087 41088 static const JSCFunctionListEntry js_object_funcs[] = { 41089 JS_CFUNC_DEF("create", 2, js_object_create ), 41090 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), 41091 JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ), 41092 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), 41093 JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), 41094 JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), 41095 JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), 41096 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ), 41097 JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), 41098 JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), 41099 JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), 41100 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), 41101 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), 41102 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), 41103 JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ), 41104 JS_CFUNC_DEF("is", 2, js_object_is ), 41105 JS_CFUNC_DEF("assign", 2, js_object_assign ), 41106 JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ), 41107 JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ), 41108 JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ), 41109 JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ), 41110 JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ), 41111 JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ), 41112 }; 41113 41114 static const JSCFunctionListEntry js_object_proto_funcs[] = { 41115 JS_CFUNC_DEF("toString", 0, js_object_toString ), 41116 JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ), 41117 JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ), 41118 JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ), 41119 JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ), 41120 JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ), 41121 JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ), 41122 JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ), 41123 JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ), 41124 JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ), 41125 JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ), 41126 }; 41127 41128 /* Function class */ 41129 41130 static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, 41131 int argc, JSValueConst *argv) 41132 { 41133 return JS_UNDEFINED; 41134 } 41135 41136 /* XXX: add a specific eval mode so that Function("}), ({") is rejected */ 41137 static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, 41138 int argc, JSValueConst *argv, int magic) 41139 { 41140 JSFunctionKindEnum func_kind = magic; 41141 int i, n, ret; 41142 JSValue s, proto, obj = JS_UNDEFINED; 41143 StringBuffer b_s, *b = &b_s; 41144 41145 string_buffer_init(ctx, b, 0); 41146 string_buffer_putc8(b, '('); 41147 41148 if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { 41149 string_buffer_puts8(b, "async "); 41150 } 41151 string_buffer_puts8(b, "function"); 41152 41153 if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { 41154 string_buffer_putc8(b, '*'); 41155 } 41156 string_buffer_puts8(b, " anonymous("); 41157 41158 n = argc - 1; 41159 for(i = 0; i < n; i++) { 41160 if (i != 0) { 41161 string_buffer_putc8(b, ','); 41162 } 41163 if (string_buffer_concat_value(b, argv[i])) 41164 goto fail; 41165 } 41166 string_buffer_puts8(b, "\n) {\n"); 41167 if (n >= 0) { 41168 if (string_buffer_concat_value(b, argv[n])) 41169 goto fail; 41170 } 41171 string_buffer_puts8(b, "\n})"); 41172 s = string_buffer_end(b); 41173 if (JS_IsException(s)) 41174 goto fail1; 41175 41176 obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); 41177 JS_FreeValue(ctx, s); 41178 if (JS_IsException(obj)) 41179 goto fail1; 41180 if (!JS_IsUndefined(new_target)) { 41181 /* set the prototype */ 41182 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); 41183 if (JS_IsException(proto)) 41184 goto fail1; 41185 if (!JS_IsObject(proto)) { 41186 JSContext *realm; 41187 JS_FreeValue(ctx, proto); 41188 realm = JS_GetFunctionRealm(ctx, new_target); 41189 if (!realm) 41190 goto fail1; 41191 proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); 41192 } 41193 ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); 41194 JS_FreeValue(ctx, proto); 41195 if (ret < 0) 41196 goto fail1; 41197 } 41198 return obj; 41199 41200 fail: 41201 string_buffer_free(b); 41202 fail1: 41203 JS_FreeValue(ctx, obj); 41204 return JS_EXCEPTION; 41205 } 41206 41207 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, 41208 JSValueConst obj) 41209 { 41210 JSValue len_val; 41211 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); 41212 if (JS_IsException(len_val)) { 41213 *pres = 0; 41214 return -1; 41215 } 41216 return JS_ToUint32Free(ctx, pres, len_val); 41217 } 41218 41219 static __exception int js_get_length64(JSContext *ctx, int64_t *pres, 41220 JSValueConst obj) 41221 { 41222 JSValue len_val; 41223 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); 41224 if (JS_IsException(len_val)) { 41225 *pres = 0; 41226 return -1; 41227 } 41228 return JS_ToLengthFree(ctx, pres, len_val); 41229 } 41230 41231 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) 41232 { 41233 uint32_t i; 41234 for(i = 0; i < len; i++) { 41235 JS_FreeValue(ctx, tab[i]); 41236 } 41237 js_free(ctx, tab); 41238 } 41239 41240 /* XXX: should use ValueArray */ 41241 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, 41242 JSValueConst array_arg) 41243 { 41244 uint32_t len, i; 41245 int64_t len64; 41246 JSValue *tab, ret; 41247 JSObject *p; 41248 41249 if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { 41250 JS_ThrowTypeError(ctx, "not a object"); 41251 return NULL; 41252 } 41253 if (js_get_length64(ctx, &len64, array_arg)) 41254 return NULL; 41255 if (len64 > JS_MAX_LOCAL_VARS) { 41256 // XXX: check for stack overflow? 41257 JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", 41258 JS_MAX_LOCAL_VARS); 41259 return NULL; 41260 } 41261 len = len64; 41262 /* avoid allocating 0 bytes */ 41263 tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); 41264 if (!tab) 41265 return NULL; 41266 p = JS_VALUE_GET_OBJ(array_arg); 41267 if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS || p->class_id == JS_CLASS_MAPPED_ARGUMENTS) && 41268 p->fast_array && 41269 len == p->u.array.count) { 41270 if (p->class_id == JS_CLASS_MAPPED_ARGUMENTS) { 41271 for(i = 0; i < len; i++) { 41272 tab[i] = JS_DupValue(ctx, *p->u.array.u.var_refs[i]->pvalue); 41273 } 41274 } else { 41275 for(i = 0; i < len; i++) { 41276 tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); 41277 } 41278 } 41279 } else { 41280 for(i = 0; i < len; i++) { 41281 ret = JS_GetPropertyUint32(ctx, array_arg, i); 41282 if (JS_IsException(ret)) { 41283 free_arg_list(ctx, tab, i); 41284 return NULL; 41285 } 41286 tab[i] = ret; 41287 } 41288 } 41289 *plen = len; 41290 return tab; 41291 } 41292 41293 /* magic value: 0 = normal apply, 1 = apply for constructor, 2 = 41294 Reflect.apply */ 41295 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, 41296 int argc, JSValueConst *argv, int magic) 41297 { 41298 JSValueConst this_arg, array_arg; 41299 uint32_t len; 41300 JSValue *tab, ret; 41301 41302 if (check_function(ctx, this_val)) 41303 return JS_EXCEPTION; 41304 this_arg = argv[0]; 41305 array_arg = argv[1]; 41306 if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || 41307 JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { 41308 return JS_Call(ctx, this_val, this_arg, 0, NULL); 41309 } 41310 tab = build_arg_list(ctx, &len, array_arg); 41311 if (!tab) 41312 return JS_EXCEPTION; 41313 if (magic & 1) { 41314 ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); 41315 } else { 41316 ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); 41317 } 41318 free_arg_list(ctx, tab, len); 41319 return ret; 41320 } 41321 41322 static JSValue js_function_call(JSContext *ctx, JSValueConst this_val, 41323 int argc, JSValueConst *argv) 41324 { 41325 if (argc <= 0) { 41326 return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); 41327 } else { 41328 return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); 41329 } 41330 } 41331 41332 static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, 41333 int argc, JSValueConst *argv) 41334 { 41335 JSBoundFunction *bf; 41336 JSValue func_obj, name1, len_val; 41337 JSObject *p; 41338 int arg_count, i, ret; 41339 41340 if (check_function(ctx, this_val)) 41341 return JS_EXCEPTION; 41342 41343 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 41344 JS_CLASS_BOUND_FUNCTION); 41345 if (JS_IsException(func_obj)) 41346 return JS_EXCEPTION; 41347 p = JS_VALUE_GET_OBJ(func_obj); 41348 p->is_constructor = JS_IsConstructor(ctx, this_val); 41349 arg_count = max_int(0, argc - 1); 41350 bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); 41351 if (!bf) 41352 goto exception; 41353 bf->func_obj = JS_DupValue(ctx, this_val); 41354 bf->this_val = JS_DupValue(ctx, argv[0]); 41355 bf->argc = arg_count; 41356 for(i = 0; i < arg_count; i++) { 41357 bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); 41358 } 41359 p->u.bound_function = bf; 41360 41361 /* XXX: the spec could be simpler by only using GetOwnProperty */ 41362 ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); 41363 if (ret < 0) 41364 goto exception; 41365 if (!ret) { 41366 len_val = JS_NewInt32(ctx, 0); 41367 } else { 41368 len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); 41369 if (JS_IsException(len_val)) 41370 goto exception; 41371 if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { 41372 /* most common case */ 41373 int len1 = JS_VALUE_GET_INT(len_val); 41374 if (len1 <= arg_count) 41375 len1 = 0; 41376 else 41377 len1 -= arg_count; 41378 len_val = JS_NewInt32(ctx, len1); 41379 } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { 41380 double d = JS_VALUE_GET_FLOAT64(len_val); 41381 if (isnan(d)) { 41382 d = 0.0; 41383 } else { 41384 d = trunc(d); 41385 if (d <= (double)arg_count) 41386 d = 0.0; 41387 else 41388 d -= (double)arg_count; /* also converts -0 to +0 */ 41389 } 41390 len_val = JS_NewFloat64(ctx, d); 41391 } else { 41392 JS_FreeValue(ctx, len_val); 41393 len_val = JS_NewInt32(ctx, 0); 41394 } 41395 } 41396 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, 41397 len_val, JS_PROP_CONFIGURABLE); 41398 41399 name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); 41400 if (JS_IsException(name1)) 41401 goto exception; 41402 if (!JS_IsString(name1)) { 41403 JS_FreeValue(ctx, name1); 41404 name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); 41405 } 41406 name1 = JS_ConcatString3(ctx, "bound ", name1, ""); 41407 if (JS_IsException(name1)) 41408 goto exception; 41409 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, 41410 JS_PROP_CONFIGURABLE); 41411 return func_obj; 41412 exception: 41413 JS_FreeValue(ctx, func_obj); 41414 return JS_EXCEPTION; 41415 } 41416 41417 static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, 41418 int argc, JSValueConst *argv) 41419 { 41420 JSObject *p; 41421 JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; 41422 41423 if (check_function(ctx, this_val)) 41424 return JS_EXCEPTION; 41425 41426 p = JS_VALUE_GET_OBJ(this_val); 41427 if (js_class_has_bytecode(p->class_id)) { 41428 JSFunctionBytecode *b = p->u.func.function_bytecode; 41429 if (b->has_debug && b->debug.source) { 41430 return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); 41431 } 41432 func_kind = b->func_kind; 41433 } 41434 { 41435 JSValue name; 41436 const char *pref, *suff; 41437 41438 switch(func_kind) { 41439 default: 41440 case JS_FUNC_NORMAL: 41441 pref = "function "; 41442 break; 41443 case JS_FUNC_GENERATOR: 41444 pref = "function *"; 41445 break; 41446 case JS_FUNC_ASYNC: 41447 pref = "async function "; 41448 break; 41449 case JS_FUNC_ASYNC_GENERATOR: 41450 pref = "async function *"; 41451 break; 41452 } 41453 suff = "() {\n [native code]\n}"; 41454 name = JS_GetProperty(ctx, this_val, JS_ATOM_name); 41455 if (JS_IsUndefined(name)) 41456 name = JS_AtomToString(ctx, JS_ATOM_empty_string); 41457 return JS_ConcatString3(ctx, pref, name, suff); 41458 } 41459 } 41460 41461 static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val, 41462 int argc, JSValueConst *argv) 41463 { 41464 int ret; 41465 ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); 41466 if (ret < 0) 41467 return JS_EXCEPTION; 41468 else 41469 return JS_NewBool(ctx, ret); 41470 } 41471 41472 static const JSCFunctionListEntry js_function_proto_funcs[] = { 41473 JS_CFUNC_DEF("call", 1, js_function_call ), 41474 JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ), 41475 JS_CFUNC_DEF("bind", 1, js_function_bind ), 41476 JS_CFUNC_DEF("toString", 0, js_function_toString ), 41477 JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ), 41478 JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ), 41479 JS_CGETSET_MAGIC_DEF("lineNumber", js_function_proto_lineNumber, NULL, 0 ), 41480 JS_CGETSET_MAGIC_DEF("columnNumber", js_function_proto_lineNumber, NULL, 1 ), 41481 }; 41482 41483 /* Error class */ 41484 41485 static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) 41486 { 41487 JSValue iter, next_method = JS_UNDEFINED; 41488 JSValue v, r = JS_UNDEFINED; 41489 int64_t k; 41490 BOOL done; 41491 41492 iter = JS_GetIterator(ctx, items, FALSE); 41493 if (JS_IsException(iter)) 41494 goto exception; 41495 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 41496 if (JS_IsException(next_method)) 41497 goto exception; 41498 r = JS_NewArray(ctx); 41499 if (JS_IsException(r)) 41500 goto exception; 41501 for (k = 0;; k++) { 41502 v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 41503 if (JS_IsException(v)) 41504 goto exception_close; 41505 if (done) 41506 break; 41507 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 41508 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 41509 goto exception_close; 41510 } 41511 done: 41512 JS_FreeValue(ctx, next_method); 41513 JS_FreeValue(ctx, iter); 41514 return r; 41515 exception_close: 41516 JS_IteratorClose(ctx, iter, TRUE); 41517 exception: 41518 JS_FreeValue(ctx, r); 41519 r = JS_EXCEPTION; 41520 goto done; 41521 } 41522 41523 static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, 41524 int argc, JSValueConst *argv, int magic) 41525 { 41526 JSValue obj, msg, proto; 41527 JSValueConst message, options; 41528 int arg_index; 41529 41530 if (JS_IsUndefined(new_target)) 41531 new_target = JS_GetActiveFunction(ctx); 41532 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); 41533 if (JS_IsException(proto)) 41534 return proto; 41535 if (!JS_IsObject(proto)) { 41536 JSContext *realm; 41537 JSValueConst proto1; 41538 41539 JS_FreeValue(ctx, proto); 41540 realm = JS_GetFunctionRealm(ctx, new_target); 41541 if (!realm) 41542 return JS_EXCEPTION; 41543 if (magic < 0) { 41544 proto1 = realm->class_proto[JS_CLASS_ERROR]; 41545 } else { 41546 proto1 = realm->native_error_proto[magic]; 41547 } 41548 proto = JS_DupValue(ctx, proto1); 41549 } 41550 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); 41551 JS_FreeValue(ctx, proto); 41552 if (JS_IsException(obj)) 41553 return obj; 41554 arg_index = (magic == JS_AGGREGATE_ERROR); 41555 41556 message = argv[arg_index++]; 41557 if (!JS_IsUndefined(message)) { 41558 msg = JS_ToString(ctx, message); 41559 if (unlikely(JS_IsException(msg))) 41560 goto exception; 41561 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, 41562 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 41563 } 41564 41565 if (arg_index < argc) { 41566 options = argv[arg_index]; 41567 if (JS_IsObject(options)) { 41568 int present = JS_HasProperty(ctx, options, JS_ATOM_cause); 41569 if (present < 0) 41570 goto exception; 41571 if (present) { 41572 JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause); 41573 if (JS_IsException(cause)) 41574 goto exception; 41575 JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause, 41576 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 41577 } 41578 } 41579 } 41580 41581 if (magic == JS_AGGREGATE_ERROR) { 41582 JSValue error_list = iterator_to_array(ctx, argv[0]); 41583 if (JS_IsException(error_list)) 41584 goto exception; 41585 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, 41586 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 41587 } 41588 41589 /* skip the Error() function in the backtrace */ 41590 build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); 41591 return obj; 41592 exception: 41593 JS_FreeValue(ctx, obj); 41594 return JS_EXCEPTION; 41595 } 41596 41597 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, 41598 int argc, JSValueConst *argv) 41599 { 41600 JSValue name, msg; 41601 41602 if (!JS_IsObject(this_val)) 41603 return JS_ThrowTypeErrorNotAnObject(ctx); 41604 name = JS_GetProperty(ctx, this_val, JS_ATOM_name); 41605 if (JS_IsUndefined(name)) 41606 name = JS_AtomToString(ctx, JS_ATOM_Error); 41607 else 41608 name = JS_ToStringFree(ctx, name); 41609 if (JS_IsException(name)) 41610 return JS_EXCEPTION; 41611 41612 msg = JS_GetProperty(ctx, this_val, JS_ATOM_message); 41613 if (JS_IsUndefined(msg)) 41614 msg = JS_AtomToString(ctx, JS_ATOM_empty_string); 41615 else 41616 msg = JS_ToStringFree(ctx, msg); 41617 if (JS_IsException(msg)) { 41618 JS_FreeValue(ctx, name); 41619 return JS_EXCEPTION; 41620 } 41621 if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg)) 41622 name = JS_ConcatString3(ctx, "", name, ": "); 41623 return JS_ConcatString(ctx, name, msg); 41624 } 41625 41626 static const JSCFunctionListEntry js_error_proto_funcs[] = { 41627 JS_CFUNC_DEF("toString", 0, js_error_toString ), 41628 JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 41629 JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 41630 }; 41631 41632 /* 2 entries for each native error class */ 41633 /* Note: we use an atom to avoid the autoinit definition which does 41634 not work in get_prop_string() */ 41635 static const JSCFunctionListEntry js_native_error_proto_funcs[] = { 41636 #define DEF(name) \ 41637 JS_PROP_ATOM_DEF("name", name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),\ 41638 JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 41639 41640 DEF(JS_ATOM_EvalError) 41641 DEF(JS_ATOM_RangeError) 41642 DEF(JS_ATOM_ReferenceError) 41643 DEF(JS_ATOM_SyntaxError) 41644 DEF(JS_ATOM_TypeError) 41645 DEF(JS_ATOM_URIError) 41646 DEF(JS_ATOM_InternalError) 41647 DEF(JS_ATOM_AggregateError) 41648 #undef DEF 41649 }; 41650 41651 static JSValue js_error_isError(JSContext *ctx, JSValueConst this_val, 41652 int argc, JSValueConst *argv) 41653 { 41654 return JS_NewBool(ctx, JS_IsError(ctx, argv[0])); 41655 } 41656 41657 static const JSCFunctionListEntry js_error_funcs[] = { 41658 JS_CFUNC_DEF("isError", 1, js_error_isError), 41659 }; 41660 41661 /* AggregateError */ 41662 41663 /* used by C code. */ 41664 static JSValue js_aggregate_error_constructor(JSContext *ctx, 41665 JSValueConst errors) 41666 { 41667 JSValue obj; 41668 41669 obj = JS_NewObjectProtoClass(ctx, 41670 ctx->native_error_proto[JS_AGGREGATE_ERROR], 41671 JS_CLASS_ERROR); 41672 if (JS_IsException(obj)) 41673 return obj; 41674 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), 41675 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 41676 return obj; 41677 } 41678 41679 /* Array */ 41680 41681 static int JS_CopySubArray(JSContext *ctx, 41682 JSValueConst obj, int64_t to_pos, 41683 int64_t from_pos, int64_t count, int dir) 41684 { 41685 JSObject *p; 41686 int64_t i, from, to, len; 41687 JSValue val; 41688 int fromPresent; 41689 41690 p = NULL; 41691 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 41692 p = JS_VALUE_GET_OBJ(obj); 41693 if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) { 41694 p = NULL; 41695 } 41696 } 41697 41698 for (i = 0; i < count; ) { 41699 if (dir < 0) { 41700 from = from_pos + count - i - 1; 41701 to = to_pos + count - i - 1; 41702 } else { 41703 from = from_pos + i; 41704 to = to_pos + i; 41705 } 41706 if (p && p->fast_array && 41707 from >= 0 && from < (len = p->u.array.count) && 41708 to >= 0 && to < len) { 41709 int64_t l, j; 41710 /* Fast path for fast arrays. Since we don't look at the 41711 prototype chain, we can optimize only the cases where 41712 all the elements are present in the array. */ 41713 l = count - i; 41714 if (dir < 0) { 41715 l = min_int64(l, from + 1); 41716 l = min_int64(l, to + 1); 41717 for(j = 0; j < l; j++) { 41718 set_value(ctx, &p->u.array.u.values[to - j], 41719 JS_DupValue(ctx, p->u.array.u.values[from - j])); 41720 } 41721 } else { 41722 l = min_int64(l, len - from); 41723 l = min_int64(l, len - to); 41724 for(j = 0; j < l; j++) { 41725 set_value(ctx, &p->u.array.u.values[to + j], 41726 JS_DupValue(ctx, p->u.array.u.values[from + j])); 41727 } 41728 } 41729 i += l; 41730 } else { 41731 fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); 41732 if (fromPresent < 0) 41733 goto exception; 41734 41735 if (fromPresent) { 41736 if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) 41737 goto exception; 41738 } else { 41739 if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) 41740 goto exception; 41741 } 41742 i++; 41743 } 41744 } 41745 return 0; 41746 41747 exception: 41748 return -1; 41749 } 41750 41751 static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, 41752 int argc, JSValueConst *argv) 41753 { 41754 JSValue obj; 41755 int i; 41756 41757 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); 41758 if (JS_IsException(obj)) 41759 return obj; 41760 if (argc == 1 && JS_IsNumber(argv[0])) { 41761 uint32_t len; 41762 if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) 41763 goto fail; 41764 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) 41765 goto fail; 41766 } else { 41767 for(i = 0; i < argc; i++) { 41768 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) 41769 goto fail; 41770 } 41771 } 41772 return obj; 41773 fail: 41774 JS_FreeValue(ctx, obj); 41775 return JS_EXCEPTION; 41776 } 41777 41778 static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, 41779 int argc, JSValueConst *argv) 41780 { 41781 // from(items, mapfn = void 0, this_arg = void 0) 41782 JSValueConst items = argv[0], mapfn, this_arg; 41783 JSValueConst args[2]; 41784 JSValue iter, r, v, v2, arrayLike, next_method, enum_obj; 41785 int64_t k, len; 41786 int done, mapping; 41787 41788 mapping = FALSE; 41789 mapfn = JS_UNDEFINED; 41790 this_arg = JS_UNDEFINED; 41791 r = JS_UNDEFINED; 41792 arrayLike = JS_UNDEFINED; 41793 iter = JS_UNDEFINED; 41794 enum_obj = JS_UNDEFINED; 41795 next_method = JS_UNDEFINED; 41796 41797 if (argc > 1) { 41798 mapfn = argv[1]; 41799 if (!JS_IsUndefined(mapfn)) { 41800 if (check_function(ctx, mapfn)) 41801 goto exception; 41802 mapping = 1; 41803 if (argc > 2) 41804 this_arg = argv[2]; 41805 } 41806 } 41807 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); 41808 if (JS_IsException(iter)) 41809 goto exception; 41810 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { 41811 if (!JS_IsFunction(ctx, iter)) { 41812 JS_ThrowTypeError(ctx, "value is not iterable"); 41813 goto exception; 41814 } 41815 if (JS_IsConstructor(ctx, this_val)) 41816 r = JS_CallConstructor(ctx, this_val, 0, NULL); 41817 else 41818 r = JS_NewArray(ctx); 41819 if (JS_IsException(r)) 41820 goto exception; 41821 enum_obj = JS_GetIterator2(ctx, items, iter); 41822 if (JS_IsException(enum_obj)) 41823 goto exception; 41824 next_method = JS_GetProperty(ctx, enum_obj, JS_ATOM_next); 41825 if (JS_IsException(next_method)) 41826 goto exception; 41827 for (k = 0;; k++) { 41828 v = JS_IteratorNext(ctx, enum_obj, next_method, 0, NULL, &done); 41829 if (JS_IsException(v)) 41830 goto exception; 41831 if (done) 41832 break; 41833 if (mapping) { 41834 args[0] = v; 41835 args[1] = JS_NewInt32(ctx, k); 41836 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 41837 JS_FreeValue(ctx, v); 41838 v = v2; 41839 if (JS_IsException(v)) 41840 goto exception_close; 41841 } 41842 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 41843 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 41844 goto exception_close; 41845 } 41846 } else { 41847 arrayLike = JS_ToObject(ctx, items); 41848 if (JS_IsException(arrayLike)) 41849 goto exception; 41850 if (js_get_length64(ctx, &len, arrayLike) < 0) 41851 goto exception; 41852 v = JS_NewInt64(ctx, len); 41853 args[0] = v; 41854 if (JS_IsConstructor(ctx, this_val)) { 41855 r = JS_CallConstructor(ctx, this_val, 1, args); 41856 } else { 41857 r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); 41858 } 41859 JS_FreeValue(ctx, v); 41860 if (JS_IsException(r)) 41861 goto exception; 41862 for(k = 0; k < len; k++) { 41863 v = JS_GetPropertyInt64(ctx, arrayLike, k); 41864 if (JS_IsException(v)) 41865 goto exception; 41866 if (mapping) { 41867 args[0] = v; 41868 args[1] = JS_NewInt32(ctx, k); 41869 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 41870 JS_FreeValue(ctx, v); 41871 v = v2; 41872 if (JS_IsException(v)) 41873 goto exception; 41874 } 41875 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 41876 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 41877 goto exception; 41878 } 41879 } 41880 if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) 41881 goto exception; 41882 goto done; 41883 41884 exception_close: 41885 JS_IteratorClose(ctx, enum_obj, TRUE); 41886 exception: 41887 JS_FreeValue(ctx, r); 41888 r = JS_EXCEPTION; 41889 done: 41890 JS_FreeValue(ctx, arrayLike); 41891 JS_FreeValue(ctx, iter); 41892 JS_FreeValue(ctx, enum_obj); 41893 JS_FreeValue(ctx, next_method); 41894 return r; 41895 } 41896 41897 static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, 41898 int argc, JSValueConst *argv) 41899 { 41900 JSValue obj, args[1]; 41901 int i; 41902 41903 if (JS_IsConstructor(ctx, this_val)) { 41904 args[0] = JS_NewInt32(ctx, argc); 41905 obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args); 41906 } else { 41907 obj = JS_NewArray(ctx); 41908 } 41909 if (JS_IsException(obj)) 41910 return JS_EXCEPTION; 41911 for(i = 0; i < argc; i++) { 41912 if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), 41913 JS_PROP_THROW) < 0) { 41914 goto fail; 41915 } 41916 } 41917 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { 41918 fail: 41919 JS_FreeValue(ctx, obj); 41920 return JS_EXCEPTION; 41921 } 41922 return obj; 41923 } 41924 41925 static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, 41926 int argc, JSValueConst *argv) 41927 { 41928 int ret; 41929 ret = JS_IsArray(ctx, argv[0]); 41930 if (ret < 0) 41931 return JS_EXCEPTION; 41932 else 41933 return JS_NewBool(ctx, ret); 41934 } 41935 41936 static JSValue js_get_this(JSContext *ctx, 41937 JSValueConst this_val) 41938 { 41939 return JS_DupValue(ctx, this_val); 41940 } 41941 41942 static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, 41943 JSValueConst len_val) 41944 { 41945 JSValue ctor, ret, species; 41946 int res; 41947 JSContext *realm; 41948 41949 res = JS_IsArray(ctx, obj); 41950 if (res < 0) 41951 return JS_EXCEPTION; 41952 if (!res) 41953 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); 41954 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); 41955 if (JS_IsException(ctor)) 41956 return ctor; 41957 if (JS_IsConstructor(ctx, ctor)) { 41958 /* legacy web compatibility */ 41959 realm = JS_GetFunctionRealm(ctx, ctor); 41960 if (!realm) { 41961 JS_FreeValue(ctx, ctor); 41962 return JS_EXCEPTION; 41963 } 41964 if (realm != ctx && 41965 js_same_value(ctx, ctor, realm->array_ctor)) { 41966 JS_FreeValue(ctx, ctor); 41967 ctor = JS_UNDEFINED; 41968 } 41969 } 41970 if (JS_IsObject(ctor)) { 41971 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); 41972 JS_FreeValue(ctx, ctor); 41973 if (JS_IsException(species)) 41974 return species; 41975 ctor = species; 41976 if (JS_IsNull(ctor)) 41977 ctor = JS_UNDEFINED; 41978 } 41979 if (JS_IsUndefined(ctor)) { 41980 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); 41981 } else { 41982 ret = JS_CallConstructor(ctx, ctor, 1, &len_val); 41983 JS_FreeValue(ctx, ctor); 41984 return ret; 41985 } 41986 } 41987 41988 static const JSCFunctionListEntry js_array_funcs[] = { 41989 JS_CFUNC_DEF("isArray", 1, js_array_isArray ), 41990 JS_CFUNC_DEF("from", 1, js_array_from ), 41991 JS_CFUNC_DEF("of", 0, js_array_of ), 41992 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 41993 }; 41994 41995 static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) 41996 { 41997 JSValue val; 41998 41999 if (!JS_IsObject(obj)) 42000 return FALSE; 42001 val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); 42002 if (JS_IsException(val)) 42003 return -1; 42004 if (!JS_IsUndefined(val)) 42005 return JS_ToBoolFree(ctx, val); 42006 return JS_IsArray(ctx, obj); 42007 } 42008 42009 static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, 42010 int argc, JSValueConst *argv) 42011 { 42012 JSValue obj, ret; 42013 int64_t len, idx; 42014 JSValue *arrp; 42015 uint32_t count; 42016 42017 obj = JS_ToObject(ctx, this_val); 42018 if (js_get_length64(ctx, &len, obj)) 42019 goto exception; 42020 42021 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 42022 goto exception; 42023 42024 if (idx < 0) 42025 idx = len + idx; 42026 if (idx < 0 || idx >= len) { 42027 ret = JS_UNDEFINED; 42028 } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) { 42029 ret = JS_DupValue(ctx, arrp[idx]); 42030 } else { 42031 int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret); 42032 if (present < 0) 42033 goto exception; 42034 if (!present) 42035 ret = JS_UNDEFINED; 42036 } 42037 JS_FreeValue(ctx, obj); 42038 return ret; 42039 exception: 42040 JS_FreeValue(ctx, obj); 42041 return JS_EXCEPTION; 42042 } 42043 42044 static JSValue js_array_with(JSContext *ctx, JSValueConst this_val, 42045 int argc, JSValueConst *argv) 42046 { 42047 JSValue arr, obj, ret, *arrp, *pval; 42048 JSObject *p; 42049 int64_t i, len, idx; 42050 uint32_t count32; 42051 42052 ret = JS_EXCEPTION; 42053 arr = JS_UNDEFINED; 42054 obj = JS_ToObject(ctx, this_val); 42055 if (js_get_length64(ctx, &len, obj)) 42056 goto exception; 42057 42058 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 42059 goto exception; 42060 42061 if (idx < 0) 42062 idx = len + idx; 42063 42064 if (idx < 0 || idx >= len) { 42065 JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx); 42066 goto exception; 42067 } 42068 42069 arr = js_allocate_fast_array(ctx, len); 42070 if (JS_IsException(arr)) 42071 goto exception; 42072 42073 p = JS_VALUE_GET_OBJ(arr); 42074 i = 0; 42075 pval = p->u.array.u.values; 42076 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 42077 for (; i < idx; i++, pval++) 42078 *pval = JS_DupValue(ctx, arrp[i]); 42079 *pval = JS_DupValue(ctx, argv[1]); 42080 for (i++, pval++; i < len; i++, pval++) 42081 *pval = JS_DupValue(ctx, arrp[i]); 42082 } else { 42083 for (; i < idx; i++, pval++) 42084 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 42085 goto exception; 42086 *pval = JS_DupValue(ctx, argv[1]); 42087 for (i++, pval++; i < len; i++, pval++) { 42088 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 42089 goto exception; 42090 } 42091 } 42092 42093 ret = arr; 42094 arr = JS_UNDEFINED; 42095 42096 exception: 42097 JS_FreeValue(ctx, arr); 42098 JS_FreeValue(ctx, obj); 42099 return ret; 42100 } 42101 42102 static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, 42103 int argc, JSValueConst *argv) 42104 { 42105 JSValue obj, arr, val; 42106 JSValueConst e; 42107 int64_t len, k, n; 42108 int i, res; 42109 42110 arr = JS_UNDEFINED; 42111 obj = JS_ToObject(ctx, this_val); 42112 if (JS_IsException(obj)) 42113 goto exception; 42114 42115 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 42116 if (JS_IsException(arr)) 42117 goto exception; 42118 n = 0; 42119 for (i = -1; i < argc; i++) { 42120 if (i < 0) 42121 e = obj; 42122 else 42123 e = argv[i]; 42124 42125 res = JS_isConcatSpreadable(ctx, e); 42126 if (res < 0) 42127 goto exception; 42128 if (res) { 42129 if (js_get_length64(ctx, &len, e)) 42130 goto exception; 42131 if (n + len > MAX_SAFE_INTEGER) { 42132 JS_ThrowTypeError(ctx, "Array loo long"); 42133 goto exception; 42134 } 42135 for (k = 0; k < len; k++, n++) { 42136 res = JS_TryGetPropertyInt64(ctx, e, k, &val); 42137 if (res < 0) 42138 goto exception; 42139 if (res) { 42140 if (JS_DefinePropertyValueInt64(ctx, arr, n, val, 42141 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 42142 goto exception; 42143 } 42144 } 42145 } else { 42146 if (n >= MAX_SAFE_INTEGER) { 42147 JS_ThrowTypeError(ctx, "Array loo long"); 42148 goto exception; 42149 } 42150 if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), 42151 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 42152 goto exception; 42153 n++; 42154 } 42155 } 42156 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) 42157 goto exception; 42158 42159 JS_FreeValue(ctx, obj); 42160 return arr; 42161 42162 exception: 42163 JS_FreeValue(ctx, arr); 42164 JS_FreeValue(ctx, obj); 42165 return JS_EXCEPTION; 42166 } 42167 42168 #define special_every 0 42169 #define special_some 1 42170 #define special_forEach 2 42171 #define special_map 3 42172 #define special_filter 4 42173 #define special_TA 8 42174 42175 static JSValue js_typed_array___speciesCreate(JSContext *ctx, 42176 JSValueConst this_val, 42177 int argc, JSValueConst *argv); 42178 42179 static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, 42180 int argc, JSValueConst *argv, int special) 42181 { 42182 JSValue obj, val, index_val, res, ret; 42183 JSValueConst args[3]; 42184 JSValueConst func, this_arg; 42185 int64_t len, k, n; 42186 int present; 42187 42188 ret = JS_UNDEFINED; 42189 val = JS_UNDEFINED; 42190 if (special & special_TA) { 42191 obj = JS_DupValue(ctx, this_val); 42192 len = js_typed_array_get_length_unsafe(ctx, obj); 42193 if (len < 0) 42194 goto exception; 42195 } else { 42196 obj = JS_ToObject(ctx, this_val); 42197 if (js_get_length64(ctx, &len, obj)) 42198 goto exception; 42199 } 42200 func = argv[0]; 42201 this_arg = JS_UNDEFINED; 42202 if (argc > 1) 42203 this_arg = argv[1]; 42204 42205 if (check_function(ctx, func)) 42206 goto exception; 42207 42208 switch (special) { 42209 case special_every: 42210 case special_every | special_TA: 42211 ret = JS_TRUE; 42212 break; 42213 case special_some: 42214 case special_some | special_TA: 42215 ret = JS_FALSE; 42216 break; 42217 case special_map: 42218 /* XXX: JS_ArraySpeciesCreate should take int64_t */ 42219 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); 42220 if (JS_IsException(ret)) 42221 goto exception; 42222 break; 42223 case special_filter: 42224 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 42225 if (JS_IsException(ret)) 42226 goto exception; 42227 break; 42228 case special_map | special_TA: 42229 args[0] = obj; 42230 args[1] = JS_NewInt32(ctx, len); 42231 ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 42232 if (JS_IsException(ret)) 42233 goto exception; 42234 break; 42235 case special_filter | special_TA: 42236 ret = JS_NewArray(ctx); 42237 if (JS_IsException(ret)) 42238 goto exception; 42239 break; 42240 } 42241 n = 0; 42242 42243 for(k = 0; k < len; k++) { 42244 if (special & special_TA) { 42245 val = JS_GetPropertyInt64(ctx, obj, k); 42246 if (JS_IsException(val)) 42247 goto exception; 42248 present = TRUE; 42249 } else { 42250 present = JS_TryGetPropertyInt64(ctx, obj, k, &val); 42251 if (present < 0) 42252 goto exception; 42253 } 42254 if (present) { 42255 index_val = JS_NewInt64(ctx, k); 42256 if (JS_IsException(index_val)) 42257 goto exception; 42258 args[0] = val; 42259 args[1] = index_val; 42260 args[2] = obj; 42261 res = JS_Call(ctx, func, this_arg, 3, args); 42262 JS_FreeValue(ctx, index_val); 42263 if (JS_IsException(res)) 42264 goto exception; 42265 switch (special) { 42266 case special_every: 42267 case special_every | special_TA: 42268 if (!JS_ToBoolFree(ctx, res)) { 42269 ret = JS_FALSE; 42270 goto done; 42271 } 42272 break; 42273 case special_some: 42274 case special_some | special_TA: 42275 if (JS_ToBoolFree(ctx, res)) { 42276 ret = JS_TRUE; 42277 goto done; 42278 } 42279 break; 42280 case special_map: 42281 if (JS_DefinePropertyValueInt64(ctx, ret, k, res, 42282 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 42283 goto exception; 42284 break; 42285 case special_map | special_TA: 42286 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) 42287 goto exception; 42288 break; 42289 case special_filter: 42290 case special_filter | special_TA: 42291 if (JS_ToBoolFree(ctx, res)) { 42292 if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), 42293 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 42294 goto exception; 42295 } 42296 break; 42297 default: 42298 JS_FreeValue(ctx, res); 42299 break; 42300 } 42301 JS_FreeValue(ctx, val); 42302 val = JS_UNDEFINED; 42303 } 42304 } 42305 done: 42306 if (special == (special_filter | special_TA)) { 42307 JSValue arr; 42308 args[0] = obj; 42309 args[1] = JS_NewInt32(ctx, n); 42310 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 42311 if (JS_IsException(arr)) 42312 goto exception; 42313 args[0] = ret; 42314 res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); 42315 if (check_exception_free(ctx, res)) { 42316 JS_FreeValue(ctx, arr); 42317 goto exception; 42318 } 42319 JS_FreeValue(ctx, ret); 42320 ret = arr; 42321 } 42322 JS_FreeValue(ctx, val); 42323 JS_FreeValue(ctx, obj); 42324 return ret; 42325 42326 exception: 42327 JS_FreeValue(ctx, ret); 42328 JS_FreeValue(ctx, val); 42329 JS_FreeValue(ctx, obj); 42330 return JS_EXCEPTION; 42331 } 42332 42333 #define special_reduce 0 42334 #define special_reduceRight 1 42335 42336 static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, 42337 int argc, JSValueConst *argv, int special) 42338 { 42339 JSValue obj, val, index_val, acc, acc1; 42340 JSValueConst args[4]; 42341 JSValueConst func; 42342 int64_t len, k, k1; 42343 int present; 42344 42345 acc = JS_UNDEFINED; 42346 val = JS_UNDEFINED; 42347 if (special & special_TA) { 42348 obj = JS_DupValue(ctx, this_val); 42349 len = js_typed_array_get_length_unsafe(ctx, obj); 42350 if (len < 0) 42351 goto exception; 42352 } else { 42353 obj = JS_ToObject(ctx, this_val); 42354 if (js_get_length64(ctx, &len, obj)) 42355 goto exception; 42356 } 42357 func = argv[0]; 42358 42359 if (check_function(ctx, func)) 42360 goto exception; 42361 42362 k = 0; 42363 if (argc > 1) { 42364 acc = JS_DupValue(ctx, argv[1]); 42365 } else { 42366 for(;;) { 42367 if (k >= len) { 42368 JS_ThrowTypeError(ctx, "empty array"); 42369 goto exception; 42370 } 42371 k1 = (special & special_reduceRight) ? len - k - 1 : k; 42372 k++; 42373 if (special & special_TA) { 42374 acc = JS_GetPropertyInt64(ctx, obj, k1); 42375 if (JS_IsException(acc)) 42376 goto exception; 42377 break; 42378 } else { 42379 present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); 42380 if (present < 0) 42381 goto exception; 42382 if (present) 42383 break; 42384 } 42385 } 42386 } 42387 for (; k < len; k++) { 42388 k1 = (special & special_reduceRight) ? len - k - 1 : k; 42389 if (special & special_TA) { 42390 val = JS_GetPropertyInt64(ctx, obj, k1); 42391 if (JS_IsException(val)) 42392 goto exception; 42393 present = TRUE; 42394 } else { 42395 present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); 42396 if (present < 0) 42397 goto exception; 42398 } 42399 if (present) { 42400 index_val = JS_NewInt64(ctx, k1); 42401 if (JS_IsException(index_val)) 42402 goto exception; 42403 args[0] = acc; 42404 args[1] = val; 42405 args[2] = index_val; 42406 args[3] = obj; 42407 acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); 42408 JS_FreeValue(ctx, index_val); 42409 JS_FreeValue(ctx, val); 42410 val = JS_UNDEFINED; 42411 if (JS_IsException(acc1)) 42412 goto exception; 42413 JS_FreeValue(ctx, acc); 42414 acc = acc1; 42415 } 42416 } 42417 JS_FreeValue(ctx, obj); 42418 return acc; 42419 42420 exception: 42421 JS_FreeValue(ctx, acc); 42422 JS_FreeValue(ctx, val); 42423 JS_FreeValue(ctx, obj); 42424 return JS_EXCEPTION; 42425 } 42426 42427 static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, 42428 int argc, JSValueConst *argv) 42429 { 42430 JSValue obj; 42431 int64_t len, start, end; 42432 42433 obj = JS_ToObject(ctx, this_val); 42434 if (js_get_length64(ctx, &len, obj)) 42435 goto exception; 42436 42437 start = 0; 42438 if (argc > 1 && !JS_IsUndefined(argv[1])) { 42439 if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) 42440 goto exception; 42441 } 42442 42443 end = len; 42444 if (argc > 2 && !JS_IsUndefined(argv[2])) { 42445 if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) 42446 goto exception; 42447 } 42448 42449 /* XXX: should special case fast arrays */ 42450 while (start < end) { 42451 if (JS_SetPropertyInt64(ctx, obj, start, 42452 JS_DupValue(ctx, argv[0])) < 0) 42453 goto exception; 42454 start++; 42455 } 42456 return obj; 42457 42458 exception: 42459 JS_FreeValue(ctx, obj); 42460 return JS_EXCEPTION; 42461 } 42462 42463 static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, 42464 int argc, JSValueConst *argv) 42465 { 42466 JSValue obj, val; 42467 int64_t len, n; 42468 JSValue *arrp; 42469 uint32_t count; 42470 int res; 42471 42472 obj = JS_ToObject(ctx, this_val); 42473 if (js_get_length64(ctx, &len, obj)) 42474 goto exception; 42475 42476 res = FALSE; 42477 if (len > 0) { 42478 n = 0; 42479 if (argc > 1) { 42480 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) 42481 goto exception; 42482 } 42483 if (js_get_fast_array(ctx, obj, &arrp, &count)) { 42484 for (; n < count; n++) { 42485 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), 42486 JS_DupValue(ctx, arrp[n]), 42487 JS_EQ_SAME_VALUE_ZERO)) { 42488 res = TRUE; 42489 goto done; 42490 } 42491 } 42492 } 42493 for (; n < len; n++) { 42494 val = JS_GetPropertyInt64(ctx, obj, n); 42495 if (JS_IsException(val)) 42496 goto exception; 42497 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, 42498 JS_EQ_SAME_VALUE_ZERO)) { 42499 res = TRUE; 42500 break; 42501 } 42502 } 42503 } 42504 done: 42505 JS_FreeValue(ctx, obj); 42506 return JS_NewBool(ctx, res); 42507 42508 exception: 42509 JS_FreeValue(ctx, obj); 42510 return JS_EXCEPTION; 42511 } 42512 42513 static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, 42514 int argc, JSValueConst *argv) 42515 { 42516 JSValue obj, val; 42517 int64_t len, n, res; 42518 JSValue *arrp; 42519 uint32_t count; 42520 42521 obj = JS_ToObject(ctx, this_val); 42522 if (js_get_length64(ctx, &len, obj)) 42523 goto exception; 42524 42525 res = -1; 42526 if (len > 0) { 42527 n = 0; 42528 if (argc > 1) { 42529 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) 42530 goto exception; 42531 } 42532 if (js_get_fast_array(ctx, obj, &arrp, &count)) { 42533 for (; n < count; n++) { 42534 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), 42535 JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { 42536 res = n; 42537 goto done; 42538 } 42539 } 42540 } 42541 for (; n < len; n++) { 42542 int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); 42543 if (present < 0) 42544 goto exception; 42545 if (present) { 42546 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { 42547 res = n; 42548 break; 42549 } 42550 } 42551 } 42552 } 42553 done: 42554 JS_FreeValue(ctx, obj); 42555 return JS_NewInt64(ctx, res); 42556 42557 exception: 42558 JS_FreeValue(ctx, obj); 42559 return JS_EXCEPTION; 42560 } 42561 42562 static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, 42563 int argc, JSValueConst *argv) 42564 { 42565 JSValue obj, val; 42566 int64_t len, n, res; 42567 int present; 42568 42569 obj = JS_ToObject(ctx, this_val); 42570 if (js_get_length64(ctx, &len, obj)) 42571 goto exception; 42572 42573 res = -1; 42574 if (len > 0) { 42575 n = len - 1; 42576 if (argc > 1) { 42577 if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) 42578 goto exception; 42579 } 42580 /* XXX: should special case fast arrays */ 42581 for (; n >= 0; n--) { 42582 present = JS_TryGetPropertyInt64(ctx, obj, n, &val); 42583 if (present < 0) 42584 goto exception; 42585 if (present) { 42586 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { 42587 res = n; 42588 break; 42589 } 42590 } 42591 } 42592 } 42593 JS_FreeValue(ctx, obj); 42594 return JS_NewInt64(ctx, res); 42595 42596 exception: 42597 JS_FreeValue(ctx, obj); 42598 return JS_EXCEPTION; 42599 } 42600 42601 enum { 42602 ArrayFind, 42603 ArrayFindIndex, 42604 ArrayFindLast, 42605 ArrayFindLastIndex, 42606 }; 42607 42608 static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, 42609 int argc, JSValueConst *argv, int mode) 42610 { 42611 JSValueConst func, this_arg; 42612 JSValueConst args[3]; 42613 JSValue obj, val, index_val, res; 42614 int64_t len, k, end; 42615 int dir; 42616 42617 index_val = JS_UNDEFINED; 42618 val = JS_UNDEFINED; 42619 obj = JS_ToObject(ctx, this_val); 42620 if (js_get_length64(ctx, &len, obj)) 42621 goto exception; 42622 42623 func = argv[0]; 42624 if (check_function(ctx, func)) 42625 goto exception; 42626 42627 this_arg = JS_UNDEFINED; 42628 if (argc > 1) 42629 this_arg = argv[1]; 42630 42631 k = 0; 42632 dir = 1; 42633 end = len; 42634 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { 42635 k = len - 1; 42636 dir = -1; 42637 end = -1; 42638 } 42639 42640 // TODO(bnoordhuis) add fast path for fast arrays 42641 for(; k != end; k += dir) { 42642 index_val = JS_NewInt64(ctx, k); 42643 if (JS_IsException(index_val)) 42644 goto exception; 42645 val = JS_GetPropertyValue(ctx, obj, index_val); 42646 if (JS_IsException(val)) 42647 goto exception; 42648 args[0] = val; 42649 args[1] = index_val; 42650 args[2] = this_val; 42651 res = JS_Call(ctx, func, this_arg, 3, args); 42652 if (JS_IsException(res)) 42653 goto exception; 42654 if (JS_ToBoolFree(ctx, res)) { 42655 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { 42656 JS_FreeValue(ctx, val); 42657 JS_FreeValue(ctx, obj); 42658 return index_val; 42659 } else { 42660 JS_FreeValue(ctx, index_val); 42661 JS_FreeValue(ctx, obj); 42662 return val; 42663 } 42664 } 42665 JS_FreeValue(ctx, val); 42666 JS_FreeValue(ctx, index_val); 42667 } 42668 JS_FreeValue(ctx, obj); 42669 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) 42670 return JS_NewInt32(ctx, -1); 42671 else 42672 return JS_UNDEFINED; 42673 42674 exception: 42675 JS_FreeValue(ctx, index_val); 42676 JS_FreeValue(ctx, val); 42677 JS_FreeValue(ctx, obj); 42678 return JS_EXCEPTION; 42679 } 42680 42681 static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, 42682 int argc, JSValueConst *argv) 42683 { 42684 JSValue obj, method, ret; 42685 42686 obj = JS_ToObject(ctx, this_val); 42687 if (JS_IsException(obj)) 42688 return JS_EXCEPTION; 42689 method = JS_GetProperty(ctx, obj, JS_ATOM_join); 42690 if (JS_IsException(method)) { 42691 ret = JS_EXCEPTION; 42692 } else 42693 if (!JS_IsFunction(ctx, method)) { 42694 /* Use intrinsic Object.prototype.toString */ 42695 JS_FreeValue(ctx, method); 42696 ret = js_object_toString(ctx, obj, 0, NULL); 42697 } else { 42698 ret = JS_CallFree(ctx, method, obj, 0, NULL); 42699 } 42700 JS_FreeValue(ctx, obj); 42701 return ret; 42702 } 42703 42704 static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, 42705 int argc, JSValueConst *argv, int toLocaleString) 42706 { 42707 JSValue obj, sep = JS_UNDEFINED, el; 42708 StringBuffer b_s, *b = &b_s; 42709 JSString *p = NULL; 42710 int64_t i, n; 42711 int c; 42712 42713 obj = JS_ToObject(ctx, this_val); 42714 if (js_get_length64(ctx, &n, obj)) 42715 goto exception; 42716 42717 c = ','; /* default separator */ 42718 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { 42719 sep = JS_ToString(ctx, argv[0]); 42720 if (JS_IsException(sep)) 42721 goto exception; 42722 p = JS_VALUE_GET_STRING(sep); 42723 if (p->len == 1 && !p->is_wide_char) 42724 c = p->u.str8[0]; 42725 else 42726 c = -1; 42727 } 42728 string_buffer_init(ctx, b, 0); 42729 42730 for(i = 0; i < n; i++) { 42731 if (i > 0) { 42732 if (c >= 0) { 42733 string_buffer_putc8(b, c); 42734 } else { 42735 string_buffer_concat(b, p, 0, p->len); 42736 } 42737 } 42738 el = JS_GetPropertyUint32(ctx, obj, i); 42739 if (JS_IsException(el)) 42740 goto fail; 42741 if (!JS_IsNull(el) && !JS_IsUndefined(el)) { 42742 if (toLocaleString) { 42743 el = JS_ToLocaleStringFree(ctx, el); 42744 } 42745 if (string_buffer_concat_value_free(b, el)) 42746 goto fail; 42747 } 42748 } 42749 JS_FreeValue(ctx, sep); 42750 JS_FreeValue(ctx, obj); 42751 return string_buffer_end(b); 42752 42753 fail: 42754 string_buffer_free(b); 42755 JS_FreeValue(ctx, sep); 42756 exception: 42757 JS_FreeValue(ctx, obj); 42758 return JS_EXCEPTION; 42759 } 42760 42761 static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, 42762 int argc, JSValueConst *argv, int shift) 42763 { 42764 JSValue obj, res = JS_UNDEFINED; 42765 int64_t len, newLen; 42766 JSValue *arrp; 42767 uint32_t count32; 42768 42769 obj = JS_ToObject(ctx, this_val); 42770 if (js_get_length64(ctx, &len, obj)) 42771 goto exception; 42772 newLen = 0; 42773 if (len > 0) { 42774 newLen = len - 1; 42775 /* Special case fast arrays */ 42776 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 42777 JSObject *p = JS_VALUE_GET_OBJ(obj); 42778 if (shift) { 42779 res = arrp[0]; 42780 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); 42781 p->u.array.count--; 42782 } else { 42783 res = arrp[count32 - 1]; 42784 p->u.array.count--; 42785 } 42786 } else { 42787 if (shift) { 42788 res = JS_GetPropertyInt64(ctx, obj, 0); 42789 if (JS_IsException(res)) 42790 goto exception; 42791 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) 42792 goto exception; 42793 } else { 42794 res = JS_GetPropertyInt64(ctx, obj, newLen); 42795 if (JS_IsException(res)) 42796 goto exception; 42797 } 42798 if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) 42799 goto exception; 42800 } 42801 } 42802 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 42803 goto exception; 42804 42805 JS_FreeValue(ctx, obj); 42806 return res; 42807 42808 exception: 42809 JS_FreeValue(ctx, res); 42810 JS_FreeValue(ctx, obj); 42811 return JS_EXCEPTION; 42812 } 42813 42814 int qjs_array_append_new(JSContext *ctx, JSValue this_val, JSValue item) 42815 { 42816 JSValue obj; 42817 int64_t len, from, newLen; 42818 42819 obj = JS_ToObject(ctx, this_val); 42820 if (JS_IsException(obj)) { 42821 JS_FreeValue(ctx, item); 42822 return -1; 42823 } 42824 if (js_get_length64(ctx, &len, obj)) 42825 goto exception; 42826 newLen = len + 1; 42827 if (newLen > MAX_SAFE_INTEGER) { 42828 JS_ThrowTypeError(ctx, "Array loo long"); 42829 goto exception; 42830 } 42831 from = len; 42832 if (JS_SetPropertyInt64(ctx, obj, from, item) < 0) 42833 goto exception; 42834 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 42835 goto exception; 42836 42837 JS_FreeValue(ctx, obj); 42838 return 0; 42839 42840 exception: 42841 JS_FreeValue(ctx, obj); 42842 return -1; 42843 } 42844 42845 static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, 42846 int argc, JSValueConst *argv, int unshift) 42847 { 42848 JSValue obj; 42849 int i; 42850 int64_t len, from, newLen; 42851 42852 if (likely(JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT && !unshift)) { 42853 JSObject *p = JS_VALUE_GET_OBJ(this_val); 42854 if (likely(p->class_id == JS_CLASS_ARRAY && p->fast_array && 42855 can_extend_fast_array(p) && 42856 JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT && 42857 JS_VALUE_GET_INT(p->prop[0].u.value) == p->u.array.count && 42858 (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE) != 0)) { 42859 /* fast case */ 42860 uint32_t new_len; 42861 new_len = p->u.array.count + argc; 42862 if (likely(new_len <= INT32_MAX)) { 42863 if (unlikely(new_len > p->u.array.u1.size)) { 42864 if (expand_fast_array(ctx, p, new_len)) 42865 return JS_EXCEPTION; 42866 } 42867 for(i = 0; i < argc; i++) 42868 p->u.array.u.values[p->u.array.count + i] = JS_DupValue(ctx, argv[i]); 42869 p->prop[0].u.value = JS_NewInt32(ctx, new_len); 42870 p->u.array.count = new_len; 42871 return JS_NewInt32(ctx, new_len); 42872 } 42873 } 42874 } 42875 obj = JS_ToObject(ctx, this_val); 42876 if (js_get_length64(ctx, &len, obj)) 42877 goto exception; 42878 newLen = len + argc; 42879 if (newLen > MAX_SAFE_INTEGER) { 42880 JS_ThrowTypeError(ctx, "Array loo long"); 42881 goto exception; 42882 } 42883 from = len; 42884 if (unshift && argc > 0) { 42885 if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) 42886 goto exception; 42887 from = 0; 42888 } 42889 for(i = 0; i < argc; i++) { 42890 if (JS_SetPropertyInt64(ctx, obj, from + i, 42891 JS_DupValue(ctx, argv[i])) < 0) 42892 goto exception; 42893 } 42894 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 42895 goto exception; 42896 42897 JS_FreeValue(ctx, obj); 42898 return JS_NewInt64(ctx, newLen); 42899 42900 exception: 42901 JS_FreeValue(ctx, obj); 42902 return JS_EXCEPTION; 42903 } 42904 42905 static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, 42906 int argc, JSValueConst *argv) 42907 { 42908 JSValue obj, lval, hval; 42909 JSValue *arrp; 42910 int64_t len, l, h; 42911 int l_present, h_present; 42912 uint32_t count32; 42913 42914 lval = JS_UNDEFINED; 42915 obj = JS_ToObject(ctx, this_val); 42916 if (js_get_length64(ctx, &len, obj)) 42917 goto exception; 42918 42919 /* Special case fast arrays */ 42920 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 42921 uint32_t ll, hh; 42922 42923 if (count32 > 1) { 42924 for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { 42925 lval = arrp[ll]; 42926 arrp[ll] = arrp[hh]; 42927 arrp[hh] = lval; 42928 } 42929 } 42930 return obj; 42931 } 42932 42933 for (l = 0, h = len - 1; l < h; l++, h--) { 42934 l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); 42935 if (l_present < 0) 42936 goto exception; 42937 h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); 42938 if (h_present < 0) 42939 goto exception; 42940 if (h_present) { 42941 if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) 42942 goto exception; 42943 42944 if (l_present) { 42945 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { 42946 lval = JS_UNDEFINED; 42947 goto exception; 42948 } 42949 lval = JS_UNDEFINED; 42950 } else { 42951 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) 42952 goto exception; 42953 } 42954 } else { 42955 if (l_present) { 42956 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) 42957 goto exception; 42958 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { 42959 lval = JS_UNDEFINED; 42960 goto exception; 42961 } 42962 lval = JS_UNDEFINED; 42963 } 42964 } 42965 } 42966 return obj; 42967 42968 exception: 42969 JS_FreeValue(ctx, lval); 42970 JS_FreeValue(ctx, obj); 42971 return JS_EXCEPTION; 42972 } 42973 42974 // Note: a.toReversed() is a.slice().reverse() with the twist that a.slice() 42975 // leaves holes in sparse arrays intact whereas a.toReversed() replaces them 42976 // with undefined, thus in effect creating a dense array. 42977 // Does not use Array[@@species], always returns a base Array. 42978 static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val, 42979 int argc, JSValueConst *argv) 42980 { 42981 JSValue arr, obj, ret, *arrp, *pval; 42982 JSObject *p; 42983 int64_t i, len; 42984 uint32_t count32; 42985 42986 ret = JS_EXCEPTION; 42987 arr = JS_UNDEFINED; 42988 obj = JS_ToObject(ctx, this_val); 42989 if (js_get_length64(ctx, &len, obj)) 42990 goto exception; 42991 42992 arr = js_allocate_fast_array(ctx, len); 42993 if (JS_IsException(arr)) 42994 goto exception; 42995 42996 if (len > 0) { 42997 p = JS_VALUE_GET_OBJ(arr); 42998 42999 i = len - 1; 43000 pval = p->u.array.u.values; 43001 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 43002 for (; i >= 0; i--, pval++) 43003 *pval = JS_DupValue(ctx, arrp[i]); 43004 } else { 43005 // Query order is observable; test262 expects descending order. 43006 for (; i >= 0; i--, pval++) { 43007 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 43008 goto exception; 43009 } 43010 } 43011 } 43012 43013 ret = arr; 43014 arr = JS_UNDEFINED; 43015 43016 exception: 43017 JS_FreeValue(ctx, arr); 43018 JS_FreeValue(ctx, obj); 43019 return ret; 43020 } 43021 43022 static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, 43023 int argc, JSValueConst *argv, int splice) 43024 { 43025 JSValue obj, arr, val, len_val; 43026 int64_t len, start, k, final, n, count, del_count, new_len; 43027 int kPresent; 43028 JSValue *arrp; 43029 uint32_t count32, i, item_count; 43030 43031 arr = JS_UNDEFINED; 43032 obj = JS_ToObject(ctx, this_val); 43033 if (js_get_length64(ctx, &len, obj)) 43034 goto exception; 43035 43036 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 43037 goto exception; 43038 43039 if (splice) { 43040 if (argc == 0) { 43041 item_count = 0; 43042 del_count = 0; 43043 } else 43044 if (argc == 1) { 43045 item_count = 0; 43046 del_count = len - start; 43047 } else { 43048 item_count = argc - 2; 43049 if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) 43050 goto exception; 43051 } 43052 if (len + item_count - del_count > MAX_SAFE_INTEGER) { 43053 JS_ThrowTypeError(ctx, "Array loo long"); 43054 goto exception; 43055 } 43056 count = del_count; 43057 } else { 43058 item_count = 0; /* avoid warning */ 43059 final = len; 43060 if (!JS_IsUndefined(argv[1])) { 43061 if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) 43062 goto exception; 43063 } 43064 count = max_int64(final - start, 0); 43065 } 43066 len_val = JS_NewInt64(ctx, count); 43067 arr = JS_ArraySpeciesCreate(ctx, obj, len_val); 43068 JS_FreeValue(ctx, len_val); 43069 if (JS_IsException(arr)) 43070 goto exception; 43071 43072 k = start; 43073 final = start + count; 43074 n = 0; 43075 /* The fast array test on arr ensures that 43076 JS_CreateDataPropertyUint32() won't modify obj in case arr is 43077 an exotic object */ 43078 /* Special case fast arrays */ 43079 if (js_get_fast_array(ctx, obj, &arrp, &count32) && 43080 js_is_fast_array(ctx, arr)) { 43081 /* XXX: should share code with fast array constructor */ 43082 for (; k < final && k < count32; k++, n++) { 43083 if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) 43084 goto exception; 43085 } 43086 } 43087 /* Copy the remaining elements if any (handle case of inherited properties) */ 43088 for (; k < final; k++, n++) { 43089 kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); 43090 if (kPresent < 0) 43091 goto exception; 43092 if (kPresent) { 43093 if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) 43094 goto exception; 43095 } 43096 } 43097 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) 43098 goto exception; 43099 43100 if (splice) { 43101 new_len = len + item_count - del_count; 43102 if (item_count != del_count) { 43103 if (JS_CopySubArray(ctx, obj, start + item_count, 43104 start + del_count, len - (start + del_count), 43105 item_count <= del_count ? +1 : -1) < 0) 43106 goto exception; 43107 43108 for (k = len; k-- > new_len; ) { 43109 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) 43110 goto exception; 43111 } 43112 } 43113 for (i = 0; i < item_count; i++) { 43114 if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) 43115 goto exception; 43116 } 43117 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) 43118 goto exception; 43119 } 43120 JS_FreeValue(ctx, obj); 43121 return arr; 43122 43123 exception: 43124 JS_FreeValue(ctx, obj); 43125 JS_FreeValue(ctx, arr); 43126 return JS_EXCEPTION; 43127 } 43128 43129 static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val, 43130 int argc, JSValueConst *argv) 43131 { 43132 JSValue arr, obj, ret, *arrp, *pval, *last; 43133 JSObject *p; 43134 int64_t i, j, len, newlen, start, add, del; 43135 uint32_t count32; 43136 43137 pval = NULL; 43138 last = NULL; 43139 ret = JS_EXCEPTION; 43140 arr = JS_UNDEFINED; 43141 43142 obj = JS_ToObject(ctx, this_val); 43143 if (js_get_length64(ctx, &len, obj)) 43144 goto exception; 43145 43146 start = 0; 43147 if (argc > 0) 43148 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 43149 goto exception; 43150 43151 del = 0; 43152 if (argc > 0) 43153 del = len - start; 43154 if (argc > 1) 43155 if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0)) 43156 goto exception; 43157 43158 add = 0; 43159 if (argc > 2) 43160 add = argc - 2; 43161 43162 newlen = len + add - del; 43163 if (newlen > MAX_SAFE_INTEGER) { 43164 JS_ThrowTypeError(ctx, "invalid array length"); 43165 goto exception; 43166 } 43167 43168 arr = js_allocate_fast_array(ctx, newlen); 43169 if (JS_IsException(arr)) 43170 goto exception; 43171 43172 if (newlen <= 0) 43173 goto done; 43174 43175 p = JS_VALUE_GET_OBJ(arr); 43176 pval = &p->u.array.u.values[0]; 43177 last = &p->u.array.u.values[newlen]; 43178 43179 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 43180 for (i = 0; i < start; i++, pval++) 43181 *pval = JS_DupValue(ctx, arrp[i]); 43182 for (j = 0; j < add; j++, pval++) 43183 *pval = JS_DupValue(ctx, argv[2 + j]); 43184 for (i += del; i < len; i++, pval++) 43185 *pval = JS_DupValue(ctx, arrp[i]); 43186 } else { 43187 for (i = 0; i < start; i++, pval++) 43188 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 43189 goto exception; 43190 for (j = 0; j < add; j++, pval++) 43191 *pval = JS_DupValue(ctx, argv[2 + j]); 43192 for (i += del; i < len; i++, pval++) 43193 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 43194 goto exception; 43195 } 43196 43197 assert(pval == last); 43198 43199 done: 43200 ret = arr; 43201 arr = JS_UNDEFINED; 43202 43203 exception: 43204 JS_FreeValue(ctx, arr); 43205 JS_FreeValue(ctx, obj); 43206 return ret; 43207 } 43208 43209 static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, 43210 int argc, JSValueConst *argv) 43211 { 43212 JSValue obj; 43213 int64_t len, from, to, final, count; 43214 43215 obj = JS_ToObject(ctx, this_val); 43216 if (js_get_length64(ctx, &len, obj)) 43217 goto exception; 43218 43219 if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) 43220 goto exception; 43221 43222 if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) 43223 goto exception; 43224 43225 final = len; 43226 if (argc > 2 && !JS_IsUndefined(argv[2])) { 43227 if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) 43228 goto exception; 43229 } 43230 43231 count = min_int64(final - from, len - to); 43232 43233 if (JS_CopySubArray(ctx, obj, to, from, count, 43234 (from < to && to < from + count) ? -1 : +1)) 43235 goto exception; 43236 43237 return obj; 43238 43239 exception: 43240 JS_FreeValue(ctx, obj); 43241 return JS_EXCEPTION; 43242 } 43243 43244 static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, 43245 JSValueConst source, int64_t sourceLen, 43246 int64_t targetIndex, int depth, 43247 JSValueConst mapperFunction, 43248 JSValueConst thisArg) 43249 { 43250 JSValue element; 43251 int64_t sourceIndex, elementLen; 43252 int present, is_array; 43253 43254 if (js_check_stack_overflow(ctx->rt, 0)) { 43255 JS_ThrowStackOverflow(ctx); 43256 return -1; 43257 } 43258 43259 for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { 43260 present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); 43261 if (present < 0) 43262 return -1; 43263 if (!present) 43264 continue; 43265 if (!JS_IsUndefined(mapperFunction)) { 43266 JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; 43267 element = JS_Call(ctx, mapperFunction, thisArg, 3, args); 43268 JS_FreeValue(ctx, (JSValue)args[0]); 43269 JS_FreeValue(ctx, (JSValue)args[1]); 43270 if (JS_IsException(element)) 43271 return -1; 43272 } 43273 if (depth > 0) { 43274 is_array = JS_IsArray(ctx, element); 43275 if (is_array < 0) 43276 goto fail; 43277 if (is_array) { 43278 if (js_get_length64(ctx, &elementLen, element) < 0) 43279 goto fail; 43280 targetIndex = JS_FlattenIntoArray(ctx, target, element, 43281 elementLen, targetIndex, 43282 depth - 1, 43283 JS_UNDEFINED, JS_UNDEFINED); 43284 if (targetIndex < 0) 43285 goto fail; 43286 JS_FreeValue(ctx, element); 43287 continue; 43288 } 43289 } 43290 if (targetIndex >= MAX_SAFE_INTEGER) { 43291 JS_ThrowTypeError(ctx, "Array too long"); 43292 goto fail; 43293 } 43294 if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, 43295 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 43296 return -1; 43297 targetIndex++; 43298 } 43299 return targetIndex; 43300 43301 fail: 43302 JS_FreeValue(ctx, element); 43303 return -1; 43304 } 43305 43306 static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, 43307 int argc, JSValueConst *argv, int map) 43308 { 43309 JSValue obj, arr; 43310 JSValueConst mapperFunction, thisArg; 43311 int64_t sourceLen; 43312 int depthNum; 43313 43314 arr = JS_UNDEFINED; 43315 obj = JS_ToObject(ctx, this_val); 43316 if (js_get_length64(ctx, &sourceLen, obj)) 43317 goto exception; 43318 43319 depthNum = 1; 43320 mapperFunction = JS_UNDEFINED; 43321 thisArg = JS_UNDEFINED; 43322 if (map) { 43323 mapperFunction = argv[0]; 43324 if (argc > 1) { 43325 thisArg = argv[1]; 43326 } 43327 if (check_function(ctx, mapperFunction)) 43328 goto exception; 43329 } else { 43330 if (argc > 0 && !JS_IsUndefined(argv[0])) { 43331 if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) 43332 goto exception; 43333 } 43334 } 43335 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 43336 if (JS_IsException(arr)) 43337 goto exception; 43338 if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, 43339 mapperFunction, thisArg) < 0) 43340 goto exception; 43341 JS_FreeValue(ctx, obj); 43342 return arr; 43343 43344 exception: 43345 JS_FreeValue(ctx, obj); 43346 JS_FreeValue(ctx, arr); 43347 return JS_EXCEPTION; 43348 } 43349 43350 /* Array sort */ 43351 43352 typedef struct ValueSlot { 43353 JSValue val; 43354 JSString *str; 43355 int64_t pos; 43356 } ValueSlot; 43357 43358 struct array_sort_context { 43359 JSContext *ctx; 43360 int exception; 43361 int has_method; 43362 JSValueConst method; 43363 }; 43364 43365 static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { 43366 struct array_sort_context *psc = opaque; 43367 JSContext *ctx = psc->ctx; 43368 JSValueConst argv[2]; 43369 JSValue res; 43370 ValueSlot *ap = (ValueSlot *)(void *)a; 43371 ValueSlot *bp = (ValueSlot *)(void *)b; 43372 int cmp; 43373 43374 if (psc->exception) 43375 return 0; 43376 43377 if (psc->has_method) { 43378 /* custom sort function is specified as returning 0 for identical 43379 * objects: avoid method call overhead. 43380 */ 43381 if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) 43382 goto cmp_same; 43383 argv[0] = ap->val; 43384 argv[1] = bp->val; 43385 res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); 43386 if (JS_IsException(res)) 43387 goto exception; 43388 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { 43389 int val = JS_VALUE_GET_INT(res); 43390 cmp = (val > 0) - (val < 0); 43391 } else { 43392 double val; 43393 if (JS_ToFloat64Free(ctx, &val, res) < 0) 43394 goto exception; 43395 cmp = (val > 0) - (val < 0); 43396 } 43397 } else { 43398 /* Not supposed to bypass ToString even for identical objects as 43399 * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js 43400 */ 43401 if (!ap->str) { 43402 JSValue str = JS_ToString(ctx, ap->val); 43403 if (JS_IsException(str)) 43404 goto exception; 43405 ap->str = JS_VALUE_GET_STRING(str); 43406 } 43407 if (!bp->str) { 43408 JSValue str = JS_ToString(ctx, bp->val); 43409 if (JS_IsException(str)) 43410 goto exception; 43411 bp->str = JS_VALUE_GET_STRING(str); 43412 } 43413 cmp = js_string_compare(ctx, ap->str, bp->str); 43414 } 43415 if (cmp != 0) 43416 return cmp; 43417 cmp_same: 43418 /* make sort stable: compare array offsets */ 43419 return (ap->pos > bp->pos) - (ap->pos < bp->pos); 43420 43421 exception: 43422 psc->exception = 1; 43423 return 0; 43424 } 43425 43426 static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, 43427 int argc, JSValueConst *argv) 43428 { 43429 struct array_sort_context asc = { ctx, 0, 0, argv[0] }; 43430 JSValue obj = JS_UNDEFINED; 43431 ValueSlot *array = NULL; 43432 size_t array_size = 0, pos = 0, n = 0; 43433 int64_t i, len, undefined_count = 0; 43434 int present; 43435 43436 if (!JS_IsUndefined(asc.method)) { 43437 if (check_function(ctx, asc.method)) 43438 goto exception; 43439 asc.has_method = 1; 43440 } 43441 obj = JS_ToObject(ctx, this_val); 43442 if (js_get_length64(ctx, &len, obj)) 43443 goto exception; 43444 43445 /* XXX: should special case fast arrays */ 43446 for (i = 0; i < len; i++) { 43447 if (pos >= array_size) { 43448 size_t new_size, slack; 43449 ValueSlot *new_array; 43450 new_size = (array_size + (array_size >> 1) + 31) & ~15; 43451 new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); 43452 if (new_array == NULL) 43453 goto exception; 43454 new_size += slack / sizeof(*new_array); 43455 array = new_array; 43456 array_size = new_size; 43457 } 43458 present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); 43459 if (present < 0) 43460 goto exception; 43461 if (present == 0) 43462 continue; 43463 if (JS_IsUndefined(array[pos].val)) { 43464 undefined_count++; 43465 continue; 43466 } 43467 array[pos].str = NULL; 43468 array[pos].pos = i; 43469 pos++; 43470 } 43471 rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); 43472 if (asc.exception) 43473 goto exception; 43474 43475 /* XXX: should special case fast arrays */ 43476 while (n < pos) { 43477 if (array[n].str) 43478 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); 43479 if (array[n].pos == n) { 43480 JS_FreeValue(ctx, array[n].val); 43481 } else { 43482 if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { 43483 n++; 43484 goto exception; 43485 } 43486 } 43487 n++; 43488 } 43489 js_free(ctx, array); 43490 for (i = n; undefined_count-- > 0; i++) { 43491 if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) 43492 goto fail; 43493 } 43494 for (; i < len; i++) { 43495 if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) 43496 goto fail; 43497 } 43498 return obj; 43499 43500 exception: 43501 for (; n < pos; n++) { 43502 JS_FreeValue(ctx, array[n].val); 43503 if (array[n].str) 43504 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); 43505 } 43506 js_free(ctx, array); 43507 fail: 43508 JS_FreeValue(ctx, obj); 43509 return JS_EXCEPTION; 43510 } 43511 43512 // Note: a.toSorted() is a.slice().sort() with the twist that a.slice() 43513 // leaves holes in sparse arrays intact whereas a.toSorted() replaces them 43514 // with undefined, thus in effect creating a dense array. 43515 // Does not use Array[@@species], always returns a base Array. 43516 static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, 43517 int argc, JSValueConst *argv) 43518 { 43519 JSValue arr, obj, ret, *arrp, *pval; 43520 JSObject *p; 43521 int64_t i, len; 43522 uint32_t count32; 43523 int ok; 43524 43525 ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); 43526 if (!ok) 43527 return JS_ThrowTypeError(ctx, "not a function"); 43528 43529 ret = JS_EXCEPTION; 43530 arr = JS_UNDEFINED; 43531 obj = JS_ToObject(ctx, this_val); 43532 if (js_get_length64(ctx, &len, obj)) 43533 goto exception; 43534 43535 arr = js_allocate_fast_array(ctx, len); 43536 if (JS_IsException(arr)) 43537 goto exception; 43538 43539 if (len > 0) { 43540 p = JS_VALUE_GET_OBJ(arr); 43541 i = 0; 43542 pval = p->u.array.u.values; 43543 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 43544 for (; i < len; i++, pval++) 43545 *pval = JS_DupValue(ctx, arrp[i]); 43546 } else { 43547 for (; i < len; i++, pval++) { 43548 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 43549 goto exception; 43550 } 43551 } 43552 } 43553 43554 ret = js_array_sort(ctx, arr, argc, argv); 43555 if (JS_IsException(ret)) 43556 goto exception; 43557 JS_FreeValue(ctx, ret); 43558 43559 ret = arr; 43560 arr = JS_UNDEFINED; 43561 43562 exception: 43563 JS_FreeValue(ctx, arr); 43564 JS_FreeValue(ctx, obj); 43565 return ret; 43566 } 43567 43568 typedef struct JSArrayIteratorData { 43569 JSValue obj; 43570 JSIteratorKindEnum kind; 43571 uint32_t idx; 43572 } JSArrayIteratorData; 43573 43574 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) 43575 { 43576 JSObject *p = JS_VALUE_GET_OBJ(val); 43577 JSArrayIteratorData *it = p->u.array_iterator_data; 43578 if (it) { 43579 JS_FreeValueRT(rt, it->obj); 43580 js_free_rt(rt, it); 43581 } 43582 } 43583 43584 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, 43585 JS_MarkFunc *mark_func) 43586 { 43587 JSObject *p = JS_VALUE_GET_OBJ(val); 43588 JSArrayIteratorData *it = p->u.array_iterator_data; 43589 if (it) { 43590 JS_MarkValue(rt, it->obj, mark_func); 43591 } 43592 } 43593 43594 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, 43595 int argc, JSValueConst *argv, int magic) 43596 { 43597 JSValue enum_obj, arr; 43598 JSArrayIteratorData *it; 43599 JSIteratorKindEnum kind; 43600 int class_id; 43601 43602 kind = magic & 3; 43603 if (magic & 4) { 43604 /* string iterator case */ 43605 arr = JS_ToStringCheckObject(ctx, this_val); 43606 class_id = JS_CLASS_STRING_ITERATOR; 43607 } else { 43608 arr = JS_ToObject(ctx, this_val); 43609 class_id = JS_CLASS_ARRAY_ITERATOR; 43610 } 43611 if (JS_IsException(arr)) 43612 goto fail; 43613 enum_obj = JS_NewObjectClass(ctx, class_id); 43614 if (JS_IsException(enum_obj)) 43615 goto fail; 43616 it = js_malloc(ctx, sizeof(*it)); 43617 if (!it) 43618 goto fail1; 43619 it->obj = arr; 43620 it->kind = kind; 43621 it->idx = 0; 43622 JS_SetOpaque(enum_obj, it); 43623 return enum_obj; 43624 fail1: 43625 JS_FreeValue(ctx, enum_obj); 43626 fail: 43627 JS_FreeValue(ctx, arr); 43628 return JS_EXCEPTION; 43629 } 43630 43631 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, 43632 int argc, JSValueConst *argv, 43633 BOOL *pdone, int magic) 43634 { 43635 JSArrayIteratorData *it; 43636 uint32_t len, idx; 43637 JSValue val, obj; 43638 JSObject *p; 43639 43640 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); 43641 if (!it) 43642 goto fail1; 43643 if (JS_IsUndefined(it->obj)) 43644 goto done; 43645 p = JS_VALUE_GET_OBJ(it->obj); 43646 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 43647 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 43648 if (typed_array_is_oob(p)) { 43649 JS_ThrowTypeErrorArrayBufferOOB(ctx); 43650 goto fail1; 43651 } 43652 len = p->u.array.count; 43653 } else { 43654 if (js_get_length32(ctx, &len, it->obj)) { 43655 fail1: 43656 *pdone = FALSE; 43657 return JS_EXCEPTION; 43658 } 43659 } 43660 idx = it->idx; 43661 if (idx >= len) { 43662 JS_FreeValue(ctx, it->obj); 43663 it->obj = JS_UNDEFINED; 43664 done: 43665 *pdone = TRUE; 43666 return JS_UNDEFINED; 43667 } 43668 it->idx = idx + 1; 43669 *pdone = FALSE; 43670 if (it->kind == JS_ITERATOR_KIND_KEY) { 43671 return JS_NewUint32(ctx, idx); 43672 } else { 43673 val = JS_GetPropertyUint32(ctx, it->obj, idx); 43674 if (JS_IsException(val)) 43675 return JS_EXCEPTION; 43676 if (it->kind == JS_ITERATOR_KIND_VALUE) { 43677 return val; 43678 } else { 43679 JSValueConst args[2]; 43680 JSValue num; 43681 num = JS_NewUint32(ctx, idx); 43682 args[0] = num; 43683 args[1] = val; 43684 obj = js_create_array(ctx, 2, args); 43685 JS_FreeValue(ctx, val); 43686 JS_FreeValue(ctx, num); 43687 return obj; 43688 } 43689 } 43690 } 43691 43692 /* Iterator Wrap */ 43693 43694 typedef struct JSIteratorWrapData { 43695 JSValue wrapped_iter; 43696 JSValue wrapped_next; 43697 } JSIteratorWrapData; 43698 43699 static void js_iterator_wrap_finalizer(JSRuntime *rt, JSValue val) 43700 { 43701 JSObject *p = JS_VALUE_GET_OBJ(val); 43702 JSIteratorWrapData *it = p->u.iterator_wrap_data; 43703 if (it) { 43704 JS_FreeValueRT(rt, it->wrapped_iter); 43705 JS_FreeValueRT(rt, it->wrapped_next); 43706 js_free_rt(rt, it); 43707 } 43708 } 43709 43710 static void js_iterator_wrap_mark(JSRuntime *rt, JSValueConst val, 43711 JS_MarkFunc *mark_func) 43712 { 43713 JSObject *p = JS_VALUE_GET_OBJ(val); 43714 JSIteratorWrapData *it = p->u.iterator_wrap_data; 43715 if (it) { 43716 JS_MarkValue(rt, it->wrapped_iter, mark_func); 43717 JS_MarkValue(rt, it->wrapped_next, mark_func); 43718 } 43719 } 43720 43721 static JSValue js_iterator_wrap_next(JSContext *ctx, JSValueConst this_val, 43722 int argc, JSValueConst *argv, 43723 int *pdone, int magic) 43724 { 43725 JSIteratorWrapData *it; 43726 JSValue method, ret; 43727 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_WRAP); 43728 if (!it) 43729 return JS_EXCEPTION; 43730 if (magic == GEN_MAGIC_NEXT) { 43731 return JS_IteratorNext(ctx, it->wrapped_iter, it->wrapped_next, 0, NULL, pdone); 43732 } else { 43733 method = JS_GetProperty(ctx, it->wrapped_iter, JS_ATOM_return); 43734 if (JS_IsException(method)) 43735 return JS_EXCEPTION; 43736 if (JS_IsNull(method) || JS_IsUndefined(method)) { 43737 *pdone = TRUE; 43738 return JS_UNDEFINED; 43739 } 43740 ret = JS_IteratorNext2(ctx, it->wrapped_iter, method, 0, NULL, pdone); 43741 JS_FreeValue(ctx, method); 43742 return ret; 43743 } 43744 } 43745 43746 static const JSCFunctionListEntry js_iterator_wrap_proto_funcs[] = { 43747 JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_wrap_next, GEN_MAGIC_NEXT ), 43748 JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_wrap_next, GEN_MAGIC_RETURN ), 43749 }; 43750 43751 /* Iterator */ 43752 43753 static JSValue js_iterator_constructor_getset(JSContext *ctx, 43754 JSValueConst this_val, 43755 int argc, JSValueConst *argv, 43756 int magic, 43757 JSValue *func_data) 43758 { 43759 int ret; 43760 43761 if (argc > 0) { // if setter 43762 if (!JS_IsObject(argv[0])) 43763 return JS_ThrowTypeErrorNotAnObject(ctx); 43764 ret = JS_DefinePropertyValue(ctx, this_val, JS_ATOM_constructor, 43765 JS_DupValue(ctx, argv[0]), 43766 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 43767 if (ret < 0) 43768 return JS_EXCEPTION; 43769 return JS_UNDEFINED; 43770 } else { 43771 return JS_DupValue(ctx, func_data[0]); 43772 } 43773 } 43774 43775 static JSValue js_iterator_constructor(JSContext *ctx, JSValueConst new_target, 43776 int argc, JSValueConst *argv) 43777 { 43778 JSObject *p; 43779 43780 if (JS_TAG_OBJECT != JS_VALUE_GET_TAG(new_target)) 43781 return JS_ThrowTypeError(ctx, "constructor requires 'new'"); 43782 p = JS_VALUE_GET_OBJ(new_target); 43783 if (p->class_id == JS_CLASS_C_FUNCTION && 43784 p->u.cfunc.c_function.generic == js_iterator_constructor) { 43785 return JS_ThrowTypeError(ctx, "abstract class not constructable"); 43786 } 43787 return js_create_from_ctor(ctx, new_target, JS_CLASS_ITERATOR); 43788 } 43789 43790 // note: deliberately doesn't use space-saving bit fields for 43791 // |index|, |count| and |running| because tcc miscompiles them 43792 typedef struct JSIteratorConcatData { 43793 int index, count; // elements (not pairs!) in values[] array 43794 BOOL running; 43795 JSValue iter, next, values[]; // array of (object, method) pairs 43796 } JSIteratorConcatData; 43797 43798 static void js_iterator_concat_finalizer(JSRuntime *rt, JSValue val) 43799 { 43800 JSObject *p = JS_VALUE_GET_OBJ(val); 43801 JSIteratorConcatData *it = p->u.iterator_concat_data; 43802 if (it) { 43803 JS_FreeValueRT(rt, it->iter); 43804 JS_FreeValueRT(rt, it->next); 43805 for (int i = it->index; i < it->count; i++) 43806 JS_FreeValueRT(rt, it->values[i]); 43807 js_free_rt(rt, it); 43808 } 43809 } 43810 43811 static void js_iterator_concat_mark(JSRuntime *rt, JSValueConst val, 43812 JS_MarkFunc *mark_func) 43813 { 43814 JSObject *p = JS_VALUE_GET_OBJ(val); 43815 JSIteratorConcatData *it = p->u.iterator_concat_data; 43816 if (it) { 43817 JS_MarkValue(rt, it->iter, mark_func); 43818 JS_MarkValue(rt, it->next, mark_func); 43819 for (int i = it->index; i < it->count; i++) 43820 JS_MarkValue(rt, it->values[i], mark_func); 43821 } 43822 } 43823 43824 static JSValue js_iterator_concat_next(JSContext *ctx, JSValueConst this_val, 43825 int argc, JSValueConst *argv, 43826 int *pdone, int magic) 43827 { 43828 JSValue iter, item, next, val, *obj, *meth, ret; 43829 JSIteratorConcatData *it; 43830 int done; 43831 43832 *pdone = FALSE; 43833 43834 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT); 43835 if (!it) 43836 return JS_EXCEPTION; 43837 if (it->running) 43838 return JS_ThrowTypeError(ctx, "already running"); 43839 43840 it->running = TRUE; 43841 for(;;) { 43842 if (it->index >= it->count) { 43843 *pdone = TRUE; 43844 ret = JS_UNDEFINED; 43845 break; 43846 } 43847 obj = &it->values[it->index + 0]; 43848 meth = &it->values[it->index + 1]; 43849 iter = it->iter; 43850 if (JS_IsUndefined(iter)) { 43851 iter = JS_GetIterator2(ctx, *obj, *meth); 43852 if (JS_IsException(iter)) 43853 goto fail; 43854 it->iter = iter; 43855 } 43856 next = it->next; 43857 if (JS_IsUndefined(next)) { 43858 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 43859 if (JS_IsException(next)) 43860 goto fail; 43861 it->next = next; 43862 } 43863 item = JS_IteratorNext2(ctx, iter, next, 0, NULL, &done); 43864 if (JS_IsException(item)) 43865 goto fail; 43866 if (done == 0) { 43867 ret = item; 43868 break; 43869 } else if (done == 2) { 43870 val = JS_GetProperty(ctx, item, JS_ATOM_done); 43871 if (JS_IsException(val)) { 43872 JS_FreeValue(ctx, item); 43873 fail: 43874 ret = JS_EXCEPTION; 43875 break; 43876 } 43877 done = JS_ToBoolFree(ctx, val); 43878 if (done) 43879 goto done_next; 43880 ret = JS_GetProperty(ctx, item, JS_ATOM_value); 43881 JS_FreeValue(ctx, item); 43882 break; 43883 } else { 43884 done_next: 43885 JS_FreeValue(ctx, item); 43886 JS_FreeValue(ctx, iter); 43887 JS_FreeValue(ctx, next); 43888 it->iter = JS_UNDEFINED; 43889 it->next = JS_UNDEFINED; 43890 JS_FreeValue(ctx, *meth); 43891 JS_FreeValue(ctx, *obj); 43892 it->index += 2; 43893 } 43894 } 43895 it->running = FALSE; 43896 return ret; 43897 } 43898 43899 static JSValue js_iterator_concat_return(JSContext *ctx, JSValueConst this_val, 43900 int argc, JSValueConst *argv) 43901 { 43902 JSIteratorConcatData *it; 43903 JSValue ret; 43904 43905 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_CONCAT); 43906 if (!it) 43907 return JS_EXCEPTION; 43908 if (it->running) 43909 return JS_ThrowTypeError(ctx, "already running"); 43910 ret = JS_UNDEFINED; 43911 if (!JS_IsUndefined(it->iter)) { 43912 it->running = TRUE; 43913 ret = JS_GetProperty(ctx, it->iter, JS_ATOM_return); 43914 if (JS_IsException(ret)) { 43915 it->running = FALSE; 43916 return JS_EXCEPTION; 43917 } 43918 ret = JS_CallFree(ctx, ret, it->iter, 0, NULL); 43919 it->running = FALSE; 43920 } 43921 while (it->index < it->count) 43922 JS_FreeValue(ctx, it->values[it->index++]); 43923 JS_FreeValue(ctx, it->iter); 43924 JS_FreeValue(ctx, it->next); 43925 it->iter = JS_UNDEFINED; 43926 it->next = JS_UNDEFINED; 43927 return ret; 43928 } 43929 43930 static const JSCFunctionListEntry js_iterator_concat_proto_funcs[] = { 43931 JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_concat_next, 0 ), 43932 JS_CFUNC_DEF("return", 0, js_iterator_concat_return ), 43933 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Concat", JS_PROP_CONFIGURABLE ), 43934 }; 43935 43936 static JSValue js_iterator_concat(JSContext *ctx, JSValueConst this_val, 43937 int argc, JSValueConst *argv) 43938 { 43939 JSIteratorConcatData *it; 43940 JSValue obj, method; 43941 43942 it = js_malloc(ctx, sizeof(*it) + 2*argc * sizeof(it->values[0])); 43943 if (!it) 43944 return JS_EXCEPTION; 43945 it->running = FALSE; 43946 it->index = 0; 43947 it->count = 0; 43948 it->iter = JS_UNDEFINED; 43949 it->next = JS_UNDEFINED; 43950 for (int i = 0; i < argc; i++) { 43951 JSValueConst obj = argv[i]; 43952 if (!JS_IsObject(obj)) { 43953 JS_ThrowTypeErrorNotAnObject(ctx); 43954 goto fail; 43955 } 43956 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 43957 if (JS_IsException(method)) 43958 goto fail; 43959 if (!JS_IsFunction(ctx, method)) { 43960 JS_ThrowTypeError(ctx, "not a function"); 43961 JS_FreeValue(ctx, method); 43962 goto fail; 43963 } 43964 it->values[it->count++] = JS_DupValue(ctx, obj); 43965 it->values[it->count++] = method; 43966 } 43967 obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_CONCAT); 43968 if (JS_IsException(obj)) 43969 goto fail; 43970 JS_SetOpaque(obj, it); 43971 return obj; 43972 fail: 43973 for (int i = 0; i < it->count; i++) 43974 JS_FreeValue(ctx, it->values[i]); 43975 js_free(ctx, it); 43976 return JS_EXCEPTION; 43977 } 43978 43979 static JSValue js_iterator_from(JSContext *ctx, JSValueConst this_val, 43980 int argc, JSValueConst *argv) 43981 { 43982 JSValueConst obj = argv[0]; 43983 JSValue method, iter, wrapper; 43984 JSIteratorWrapData *it; 43985 int ret; 43986 43987 if (!JS_IsObject(obj)) { 43988 if (!JS_IsString(obj)) 43989 return JS_ThrowTypeError(ctx, "Iterator.from called on non-object"); 43990 } 43991 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 43992 if (JS_IsException(method)) 43993 return JS_EXCEPTION; 43994 if (JS_IsNull(method) || JS_IsUndefined(method)) { 43995 iter = JS_DupValue(ctx, obj); 43996 } else { 43997 iter = JS_GetIterator2(ctx, obj, method); 43998 JS_FreeValue(ctx, method); 43999 if (JS_IsException(iter)) 44000 return JS_EXCEPTION; 44001 } 44002 44003 wrapper = JS_UNDEFINED; 44004 method = JS_GetProperty(ctx, iter, JS_ATOM_next); 44005 if (JS_IsException(method)) 44006 goto fail; 44007 44008 ret = JS_OrdinaryIsInstanceOf(ctx, iter, ctx->iterator_ctor); 44009 if (ret < 0) 44010 goto fail; 44011 if (ret) { 44012 JS_FreeValue(ctx, method); 44013 return iter; 44014 } 44015 44016 wrapper = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_WRAP); 44017 if (JS_IsException(wrapper)) 44018 goto fail; 44019 it = js_malloc(ctx, sizeof(*it)); 44020 if (!it) 44021 goto fail; 44022 it->wrapped_iter = iter; 44023 it->wrapped_next = method; 44024 JS_SetOpaque(wrapper, it); 44025 return wrapper; 44026 44027 fail: 44028 JS_FreeValue(ctx, method); 44029 JS_FreeValue(ctx, iter); 44030 JS_FreeValue(ctx, wrapper); 44031 return JS_EXCEPTION; 44032 } 44033 44034 typedef enum JSIteratorHelperKindEnum { 44035 JS_ITERATOR_HELPER_KIND_DROP, 44036 JS_ITERATOR_HELPER_KIND_EVERY, 44037 JS_ITERATOR_HELPER_KIND_FILTER, 44038 JS_ITERATOR_HELPER_KIND_FIND, 44039 JS_ITERATOR_HELPER_KIND_FLAT_MAP, 44040 JS_ITERATOR_HELPER_KIND_FOR_EACH, 44041 JS_ITERATOR_HELPER_KIND_MAP, 44042 JS_ITERATOR_HELPER_KIND_SOME, 44043 JS_ITERATOR_HELPER_KIND_TAKE, 44044 } JSIteratorHelperKindEnum; 44045 44046 typedef struct JSIteratorHelperData { 44047 JSValue obj; 44048 JSValue next; 44049 JSValue func; // predicate (filter) or mapper (flatMap, map) 44050 JSValue inner; // innerValue (flatMap) 44051 int64_t count; // limit (drop, take) or counter (filter, map, flatMap) 44052 JSIteratorHelperKindEnum kind : 8; 44053 uint8_t executing : 1; 44054 uint8_t done : 1; 44055 } JSIteratorHelperData; 44056 44057 static JSValue js_create_iterator_helper(JSContext *ctx, JSValueConst this_val, 44058 int argc, JSValueConst *argv, int magic) 44059 { 44060 JSValueConst func; 44061 JSValue obj, method; 44062 int64_t count; 44063 JSIteratorHelperData *it; 44064 44065 if (!JS_IsObject(this_val)) 44066 return JS_ThrowTypeErrorNotAnObject(ctx); 44067 func = JS_UNDEFINED; 44068 count = 0; 44069 44070 switch(magic) { 44071 case JS_ITERATOR_HELPER_KIND_DROP: 44072 case JS_ITERATOR_HELPER_KIND_TAKE: 44073 { 44074 JSValue v; 44075 double dlimit; 44076 v = JS_ToNumber(ctx, argv[0]); 44077 if (JS_IsException(v)) 44078 goto fail; 44079 // Check for Infinity. 44080 if (JS_ToFloat64(ctx, &dlimit, v)) { 44081 JS_FreeValue(ctx, v); 44082 goto fail; 44083 } 44084 if (isnan(dlimit)) { 44085 JS_FreeValue(ctx, v); 44086 goto range_error; 44087 } 44088 if (!isfinite(dlimit)) { 44089 JS_FreeValue(ctx, v); 44090 if (dlimit < 0) 44091 goto range_error; 44092 else 44093 count = MAX_SAFE_INTEGER; 44094 } else { 44095 v = JS_ToIntegerFree(ctx, v); 44096 if (JS_IsException(v)) 44097 goto fail; 44098 if (JS_ToInt64Free(ctx, &count, v)) 44099 goto fail; 44100 } 44101 if (count < 0) 44102 goto range_error; 44103 } 44104 break; 44105 case JS_ITERATOR_HELPER_KIND_FILTER: 44106 case JS_ITERATOR_HELPER_KIND_FLAT_MAP: 44107 case JS_ITERATOR_HELPER_KIND_MAP: 44108 { 44109 func = argv[0]; 44110 if (check_function(ctx, func)) 44111 goto fail; 44112 } 44113 break; 44114 default: 44115 abort(); 44116 break; 44117 } 44118 44119 method = JS_GetProperty(ctx, this_val, JS_ATOM_next); 44120 if (JS_IsException(method)) 44121 goto fail; 44122 obj = JS_NewObjectClass(ctx, JS_CLASS_ITERATOR_HELPER); 44123 if (JS_IsException(obj)) { 44124 JS_FreeValue(ctx, method); 44125 goto fail; 44126 } 44127 it = js_malloc(ctx, sizeof(*it)); 44128 if (!it) { 44129 JS_FreeValue(ctx, obj); 44130 JS_FreeValue(ctx, method); 44131 goto fail; 44132 } 44133 it->kind = magic; 44134 it->obj = JS_DupValue(ctx, this_val); 44135 it->func = JS_DupValue(ctx, func); 44136 it->next = method; 44137 it->inner = JS_UNDEFINED; 44138 it->count = count; 44139 it->executing = 0; 44140 it->done = 0; 44141 JS_SetOpaque(obj, it); 44142 return obj; 44143 range_error: 44144 JS_ThrowRangeError(ctx, "must be positive"); 44145 fail: 44146 JS_IteratorClose(ctx, this_val, TRUE); 44147 return JS_EXCEPTION; 44148 } 44149 44150 static JSValue js_iterator_proto_func(JSContext *ctx, JSValueConst this_val, 44151 int argc, JSValueConst *argv, int magic) 44152 { 44153 JSValue item, method, ret, func, index_val, r; 44154 JSValueConst args[2]; 44155 int64_t idx; 44156 int done; 44157 44158 if (!JS_IsObject(this_val)) 44159 return JS_ThrowTypeErrorNotAnObject(ctx); 44160 func = JS_UNDEFINED; 44161 method = JS_UNDEFINED; 44162 44163 if (check_function(ctx, argv[0])) 44164 goto fail; 44165 func = JS_DupValue(ctx, argv[0]); 44166 method = JS_GetProperty(ctx, this_val, JS_ATOM_next); 44167 if (JS_IsException(method)) 44168 goto fail_no_close; 44169 44170 r = JS_UNDEFINED; 44171 44172 switch(magic) { 44173 case JS_ITERATOR_HELPER_KIND_EVERY: 44174 { 44175 r = JS_TRUE; 44176 for (idx = 0; /*empty*/; idx++) { 44177 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44178 if (JS_IsException(item)) 44179 goto fail_no_close; 44180 if (done) 44181 break; 44182 index_val = JS_NewInt64(ctx, idx); 44183 args[0] = item; 44184 args[1] = index_val; 44185 ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); 44186 JS_FreeValue(ctx, item); 44187 JS_FreeValue(ctx, index_val); 44188 if (JS_IsException(ret)) 44189 goto fail; 44190 if (!JS_ToBoolFree(ctx, ret)) { 44191 if (JS_IteratorClose(ctx, this_val, FALSE) < 0) 44192 r = JS_EXCEPTION; 44193 else 44194 r = JS_FALSE; 44195 break; 44196 } 44197 index_val = JS_UNDEFINED; 44198 ret = JS_UNDEFINED; 44199 item = JS_UNDEFINED; 44200 } 44201 } 44202 break; 44203 case JS_ITERATOR_HELPER_KIND_FIND: 44204 { 44205 for (idx = 0; /*empty*/; idx++) { 44206 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44207 if (JS_IsException(item)) 44208 goto fail_no_close; 44209 if (done) 44210 break; 44211 index_val = JS_NewInt64(ctx, idx); 44212 args[0] = item; 44213 args[1] = index_val; 44214 ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); 44215 JS_FreeValue(ctx, index_val); 44216 if (JS_IsException(ret)) { 44217 JS_FreeValue(ctx, item); 44218 goto fail; 44219 } 44220 if (JS_ToBoolFree(ctx, ret)) { 44221 if (JS_IteratorClose(ctx, this_val, FALSE) < 0) { 44222 JS_FreeValue(ctx, item); 44223 r = JS_EXCEPTION; 44224 } else { 44225 r = item; 44226 } 44227 break; 44228 } 44229 JS_FreeValue(ctx, item); 44230 index_val = JS_UNDEFINED; 44231 ret = JS_UNDEFINED; 44232 item = JS_UNDEFINED; 44233 } 44234 } 44235 break; 44236 case JS_ITERATOR_HELPER_KIND_FOR_EACH: 44237 { 44238 for (idx = 0; /*empty*/; idx++) { 44239 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44240 if (JS_IsException(item)) 44241 goto fail_no_close; 44242 if (done) 44243 break; 44244 index_val = JS_NewInt64(ctx, idx); 44245 args[0] = item; 44246 args[1] = index_val; 44247 ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); 44248 JS_FreeValue(ctx, item); 44249 JS_FreeValue(ctx, index_val); 44250 if (JS_IsException(ret)) 44251 goto fail; 44252 JS_FreeValue(ctx, ret); 44253 index_val = JS_UNDEFINED; 44254 ret = JS_UNDEFINED; 44255 item = JS_UNDEFINED; 44256 } 44257 } 44258 break; 44259 case JS_ITERATOR_HELPER_KIND_SOME: 44260 { 44261 r = JS_FALSE; 44262 for (idx = 0; /*empty*/; idx++) { 44263 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44264 if (JS_IsException(item)) 44265 goto fail_no_close; 44266 if (done) 44267 break; 44268 index_val = JS_NewInt64(ctx, idx); 44269 args[0] = item; 44270 args[1] = index_val; 44271 ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); 44272 JS_FreeValue(ctx, item); 44273 JS_FreeValue(ctx, index_val); 44274 if (JS_IsException(ret)) 44275 goto fail; 44276 if (JS_ToBoolFree(ctx, ret)) { 44277 if (JS_IteratorClose(ctx, this_val, FALSE) < 0) 44278 r = JS_EXCEPTION; 44279 else 44280 r = JS_TRUE; 44281 break; 44282 } 44283 index_val = JS_UNDEFINED; 44284 ret = JS_UNDEFINED; 44285 item = JS_UNDEFINED; 44286 } 44287 } 44288 break; 44289 default: 44290 abort(); 44291 break; 44292 } 44293 44294 JS_FreeValue(ctx, func); 44295 JS_FreeValue(ctx, method); 44296 return r; 44297 fail: 44298 JS_IteratorClose(ctx, this_val, TRUE); 44299 fail_no_close: 44300 JS_FreeValue(ctx, func); 44301 JS_FreeValue(ctx, method); 44302 return JS_EXCEPTION; 44303 } 44304 44305 static JSValue js_iterator_proto_reduce(JSContext *ctx, JSValueConst this_val, 44306 int argc, JSValueConst *argv) 44307 { 44308 JSValue item, method, ret, func, index_val, acc; 44309 JSValueConst args[3]; 44310 int64_t idx; 44311 int done; 44312 44313 if (!JS_IsObject(this_val)) 44314 return JS_ThrowTypeErrorNotAnObject(ctx); 44315 acc = JS_UNDEFINED; 44316 func = JS_UNDEFINED; 44317 method = JS_UNDEFINED; 44318 if (check_function(ctx, argv[0])) 44319 goto exception; 44320 func = JS_DupValue(ctx, argv[0]); 44321 method = JS_GetProperty(ctx, this_val, JS_ATOM_next); 44322 if (JS_IsException(method)) 44323 goto exception; 44324 if (argc > 1) { 44325 acc = JS_DupValue(ctx, argv[1]); 44326 idx = 0; 44327 } else { 44328 acc = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44329 if (JS_IsException(acc)) 44330 goto exception_no_close; 44331 if (done) { 44332 JS_ThrowTypeError(ctx, "empty iterator"); 44333 goto exception; 44334 } 44335 idx = 1; 44336 } 44337 for (/* empty */; /*empty*/; idx++) { 44338 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44339 if (JS_IsException(item)) 44340 goto exception_no_close; 44341 if (done) 44342 break; 44343 index_val = JS_NewInt64(ctx, idx); 44344 args[0] = acc; 44345 args[1] = item; 44346 args[2] = index_val; 44347 ret = JS_Call(ctx, func, JS_UNDEFINED, countof(args), args); 44348 JS_FreeValue(ctx, item); 44349 JS_FreeValue(ctx, index_val); 44350 if (JS_IsException(ret)) 44351 goto exception; 44352 JS_FreeValue(ctx, acc); 44353 acc = ret; 44354 index_val = JS_UNDEFINED; 44355 ret = JS_UNDEFINED; 44356 item = JS_UNDEFINED; 44357 } 44358 JS_FreeValue(ctx, func); 44359 JS_FreeValue(ctx, method); 44360 return acc; 44361 exception: 44362 JS_IteratorClose(ctx, this_val, TRUE); 44363 exception_no_close: 44364 JS_FreeValue(ctx, acc); 44365 JS_FreeValue(ctx, func); 44366 JS_FreeValue(ctx, method); 44367 return JS_EXCEPTION; 44368 } 44369 44370 static JSValue js_iterator_proto_toArray(JSContext *ctx, JSValueConst this_val, 44371 int argc, JSValueConst *argv) 44372 { 44373 JSValue item, method, result; 44374 int64_t idx; 44375 int done; 44376 44377 result = JS_UNDEFINED; 44378 if (!JS_IsObject(this_val)) 44379 return JS_ThrowTypeErrorNotAnObject(ctx); 44380 method = JS_GetProperty(ctx, this_val, JS_ATOM_next); 44381 if (JS_IsException(method)) 44382 return JS_EXCEPTION; 44383 result = JS_NewArray(ctx); 44384 if (JS_IsException(result)) 44385 goto exception; 44386 for (idx = 0; /*empty*/; idx++) { 44387 item = JS_IteratorNext(ctx, this_val, method, 0, NULL, &done); 44388 if (JS_IsException(item)) 44389 goto exception; 44390 if (done) 44391 break; 44392 if (JS_DefinePropertyValueInt64(ctx, result, idx, item, 44393 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44394 goto exception; 44395 } 44396 if (JS_SetProperty(ctx, result, JS_ATOM_length, JS_NewUint32(ctx, idx)) < 0) 44397 goto exception; 44398 JS_FreeValue(ctx, method); 44399 return result; 44400 exception: 44401 JS_FreeValue(ctx, result); 44402 JS_FreeValue(ctx, method); 44403 return JS_EXCEPTION; 44404 } 44405 44406 static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val, 44407 int argc, JSValueConst *argv) 44408 { 44409 return JS_DupValue(ctx, this_val); 44410 } 44411 44412 static JSValue js_iterator_proto_get_toStringTag(JSContext *ctx, JSValueConst this_val) 44413 { 44414 return JS_AtomToString(ctx, JS_ATOM_Iterator); 44415 } 44416 44417 static JSValue js_iterator_proto_set_toStringTag(JSContext *ctx, JSValueConst this_val, JSValueConst val) 44418 { 44419 int res; 44420 44421 if (!JS_IsObject(this_val)) 44422 return JS_ThrowTypeErrorNotAnObject(ctx); 44423 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_ITERATOR])) 44424 return JS_ThrowTypeError(ctx, "Cannot assign to read only property"); 44425 res = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_Symbol_toStringTag); 44426 if (res < 0) 44427 return JS_EXCEPTION; 44428 if (res) { 44429 if (JS_SetProperty(ctx, this_val, JS_ATOM_Symbol_toStringTag, JS_DupValue(ctx, val)) < 0) 44430 return JS_EXCEPTION; 44431 } else { 44432 if (JS_DefinePropertyValue(ctx, this_val, JS_ATOM_Symbol_toStringTag, JS_DupValue(ctx, val), JS_PROP_C_W_E) < 0) 44433 return JS_EXCEPTION; 44434 } 44435 return JS_UNDEFINED; 44436 } 44437 44438 /* Iterator Helper */ 44439 44440 static void js_iterator_helper_finalizer(JSRuntime *rt, JSValue val) 44441 { 44442 JSObject *p = JS_VALUE_GET_OBJ(val); 44443 JSIteratorHelperData *it = p->u.iterator_helper_data; 44444 if (it) { 44445 JS_FreeValueRT(rt, it->obj); 44446 JS_FreeValueRT(rt, it->func); 44447 JS_FreeValueRT(rt, it->next); 44448 JS_FreeValueRT(rt, it->inner); 44449 js_free_rt(rt, it); 44450 } 44451 } 44452 44453 static void js_iterator_helper_mark(JSRuntime *rt, JSValueConst val, 44454 JS_MarkFunc *mark_func) 44455 { 44456 JSObject *p = JS_VALUE_GET_OBJ(val); 44457 JSIteratorHelperData *it = p->u.iterator_helper_data; 44458 if (it) { 44459 JS_MarkValue(rt, it->obj, mark_func); 44460 JS_MarkValue(rt, it->func, mark_func); 44461 JS_MarkValue(rt, it->next, mark_func); 44462 JS_MarkValue(rt, it->inner, mark_func); 44463 } 44464 } 44465 44466 static JSValue js_iterator_helper_next(JSContext *ctx, JSValueConst this_val, 44467 int argc, JSValueConst *argv, 44468 int *pdone, int magic) 44469 { 44470 JSIteratorHelperData *it; 44471 JSValue ret; 44472 44473 *pdone = FALSE; 44474 44475 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ITERATOR_HELPER); 44476 if (!it) 44477 return JS_EXCEPTION; 44478 if (it->executing) 44479 return JS_ThrowTypeError(ctx, "cannot invoke a running iterator"); 44480 if (it->done) { 44481 *pdone = TRUE; 44482 return JS_UNDEFINED; 44483 } 44484 44485 it->executing = 1; 44486 44487 switch (it->kind) { 44488 case JS_ITERATOR_HELPER_KIND_DROP: 44489 { 44490 JSValue item, method; 44491 if (magic == GEN_MAGIC_NEXT) { 44492 method = JS_DupValue(ctx, it->next); 44493 } else { 44494 method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); 44495 if (JS_IsException(method)) 44496 goto fail; 44497 } 44498 while (it->count > 0) { 44499 it->count--; 44500 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44501 if (JS_IsException(item)) { 44502 JS_FreeValue(ctx, method); 44503 goto fail_no_close; 44504 } 44505 JS_FreeValue(ctx, item); 44506 if (magic == GEN_MAGIC_RETURN) 44507 *pdone = TRUE; 44508 if (*pdone) { 44509 JS_FreeValue(ctx, method); 44510 ret = JS_UNDEFINED; 44511 goto done; 44512 } 44513 } 44514 44515 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44516 JS_FreeValue(ctx, method); 44517 if (JS_IsException(item)) 44518 goto fail_no_close; 44519 ret = item; 44520 goto done; 44521 } 44522 break; 44523 case JS_ITERATOR_HELPER_KIND_FILTER: 44524 { 44525 JSValue item, method, selected, index_val; 44526 JSValueConst args[2]; 44527 if (magic == GEN_MAGIC_NEXT) { 44528 method = JS_DupValue(ctx, it->next); 44529 } else { 44530 method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); 44531 if (JS_IsException(method)) 44532 goto fail; 44533 } 44534 filter_again: 44535 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44536 if (JS_IsException(item)) { 44537 JS_FreeValue(ctx, method); 44538 goto fail_no_close; 44539 } 44540 if (*pdone || magic == GEN_MAGIC_RETURN) { 44541 JS_FreeValue(ctx, method); 44542 ret = item; 44543 goto done; 44544 } 44545 index_val = JS_NewInt64(ctx, it->count++); 44546 args[0] = item; 44547 args[1] = index_val; 44548 selected = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); 44549 JS_FreeValue(ctx, index_val); 44550 if (JS_IsException(selected)) { 44551 JS_FreeValue(ctx, item); 44552 JS_FreeValue(ctx, method); 44553 goto fail; 44554 } 44555 if (JS_ToBoolFree(ctx, selected)) { 44556 JS_FreeValue(ctx, method); 44557 ret = item; 44558 goto done; 44559 } 44560 JS_FreeValue(ctx, item); 44561 goto filter_again; 44562 } 44563 break; 44564 case JS_ITERATOR_HELPER_KIND_FLAT_MAP: 44565 { 44566 JSValue item, method, index_val, iter; 44567 JSValueConst args[2]; 44568 flat_map_again: 44569 if (JS_IsUndefined(it->inner)) { 44570 if (magic == GEN_MAGIC_NEXT) { 44571 method = JS_DupValue(ctx, it->next); 44572 } else { 44573 method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); 44574 if (JS_IsException(method)) 44575 goto fail; 44576 } 44577 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44578 JS_FreeValue(ctx, method); 44579 if (JS_IsException(item)) 44580 goto fail_no_close; 44581 if (*pdone || magic == GEN_MAGIC_RETURN) { 44582 ret = item; 44583 goto done; 44584 } 44585 index_val = JS_NewInt64(ctx, it->count++); 44586 args[0] = item; 44587 args[1] = index_val; 44588 ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); 44589 JS_FreeValue(ctx, item); 44590 JS_FreeValue(ctx, index_val); 44591 if (JS_IsException(ret)) 44592 goto fail; 44593 if (!JS_IsObject(ret)) { 44594 JS_FreeValue(ctx, ret); 44595 JS_ThrowTypeError(ctx, "not an object"); 44596 goto fail; 44597 } 44598 method = JS_GetProperty(ctx, ret, JS_ATOM_Symbol_iterator); 44599 if (JS_IsException(method)) { 44600 JS_FreeValue(ctx, ret); 44601 goto fail; 44602 } 44603 if (JS_IsNull(method) || JS_IsUndefined(method)) { 44604 JS_FreeValue(ctx, method); 44605 iter = ret; 44606 } else { 44607 iter = JS_GetIterator2(ctx, ret, method); 44608 JS_FreeValue(ctx, method); 44609 JS_FreeValue(ctx, ret); 44610 if (JS_IsException(iter)) 44611 goto fail; 44612 } 44613 44614 it->inner = iter; 44615 } 44616 44617 if (magic == GEN_MAGIC_NEXT) 44618 method = JS_GetProperty(ctx, it->inner, JS_ATOM_next); 44619 else 44620 method = JS_GetProperty(ctx, it->inner, JS_ATOM_return); 44621 if (JS_IsException(method)) { 44622 inner_fail: 44623 JS_IteratorClose(ctx, it->inner, FALSE); 44624 JS_FreeValue(ctx, it->inner); 44625 it->inner = JS_UNDEFINED; 44626 goto fail; 44627 } 44628 if (magic == GEN_MAGIC_RETURN && (JS_IsUndefined(method) || JS_IsNull(method))) { 44629 goto inner_end; 44630 } else { 44631 item = JS_IteratorNext(ctx, it->inner, method, 0, NULL, pdone); 44632 JS_FreeValue(ctx, method); 44633 if (JS_IsException(item)) 44634 goto inner_fail; 44635 } 44636 if (*pdone) { 44637 inner_end: 44638 *pdone = FALSE; // The outer iterator must continue. 44639 JS_IteratorClose(ctx, it->inner, FALSE); 44640 JS_FreeValue(ctx, it->inner); 44641 it->inner = JS_UNDEFINED; 44642 goto flat_map_again; 44643 } 44644 ret = item; 44645 goto done; 44646 } 44647 break; 44648 case JS_ITERATOR_HELPER_KIND_MAP: 44649 { 44650 JSValue item, method, index_val; 44651 JSValueConst args[2]; 44652 if (magic == GEN_MAGIC_NEXT) { 44653 method = JS_DupValue(ctx, it->next); 44654 } else { 44655 method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); 44656 if (JS_IsException(method)) 44657 goto fail; 44658 } 44659 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44660 JS_FreeValue(ctx, method); 44661 if (JS_IsException(item)) 44662 goto fail_no_close; 44663 if (*pdone || magic == GEN_MAGIC_RETURN) { 44664 ret = item; 44665 goto done; 44666 } 44667 index_val = JS_NewInt64(ctx, it->count++); 44668 args[0] = item; 44669 args[1] = index_val; 44670 ret = JS_Call(ctx, it->func, JS_UNDEFINED, countof(args), args); 44671 JS_FreeValue(ctx, index_val); 44672 JS_FreeValue(ctx, item); 44673 if (JS_IsException(ret)) 44674 goto fail; 44675 goto done; 44676 } 44677 break; 44678 case JS_ITERATOR_HELPER_KIND_TAKE: 44679 { 44680 JSValue item, method; 44681 if (it->count > 0) { 44682 if (magic == GEN_MAGIC_NEXT) { 44683 method = JS_DupValue(ctx, it->next); 44684 } else { 44685 method = JS_GetProperty(ctx, it->obj, JS_ATOM_return); 44686 if (JS_IsException(method)) 44687 goto fail; 44688 } 44689 it->count--; 44690 item = JS_IteratorNext(ctx, it->obj, method, 0, NULL, pdone); 44691 JS_FreeValue(ctx, method); 44692 if (JS_IsException(item)) 44693 goto fail_no_close; 44694 ret = item; 44695 goto done; 44696 } 44697 44698 *pdone = TRUE; 44699 if (JS_IteratorClose(ctx, it->obj, FALSE)) 44700 ret = JS_EXCEPTION; 44701 else 44702 ret = JS_UNDEFINED; 44703 goto done; 44704 } 44705 break; 44706 default: 44707 abort(); 44708 } 44709 44710 done: 44711 it->done = magic == GEN_MAGIC_NEXT ? *pdone : 1; 44712 it->executing = 0; 44713 return ret; 44714 fail: 44715 /* close the iterator object, preserving pending exception */ 44716 JS_IteratorClose(ctx, it->obj, TRUE); 44717 fail_no_close: 44718 ret = JS_EXCEPTION; 44719 goto done; 44720 } 44721 44722 static const JSCFunctionListEntry js_iterator_funcs[] = { 44723 JS_CFUNC_DEF("concat", 0, js_iterator_concat ), 44724 JS_CFUNC_DEF("from", 1, js_iterator_from ), 44725 }; 44726 44727 static const JSCFunctionListEntry js_iterator_proto_funcs[] = { 44728 JS_CFUNC_MAGIC_DEF("drop", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_DROP ), 44729 JS_CFUNC_MAGIC_DEF("filter", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FILTER ), 44730 JS_CFUNC_MAGIC_DEF("flatMap", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FLAT_MAP ), 44731 JS_CFUNC_MAGIC_DEF("map", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_MAP ), 44732 JS_CFUNC_MAGIC_DEF("take", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_TAKE ), 44733 JS_CFUNC_MAGIC_DEF("every", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_EVERY ), 44734 JS_CFUNC_MAGIC_DEF("find", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FIND), 44735 JS_CFUNC_MAGIC_DEF("forEach", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FOR_EACH ), 44736 JS_CFUNC_MAGIC_DEF("some", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_SOME ), 44737 JS_CFUNC_DEF("reduce", 1, js_iterator_proto_reduce ), 44738 JS_CFUNC_DEF("toArray", 0, js_iterator_proto_toArray ), 44739 JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ), 44740 JS_CGETSET_DEF("[Symbol.toStringTag]", js_iterator_proto_get_toStringTag, js_iterator_proto_set_toStringTag), 44741 }; 44742 44743 static const JSCFunctionListEntry js_iterator_helper_proto_funcs[] = { 44744 JS_ITERATOR_NEXT_DEF("next", 0, js_iterator_helper_next, GEN_MAGIC_NEXT ), 44745 JS_ITERATOR_NEXT_DEF("return", 0, js_iterator_helper_next, GEN_MAGIC_RETURN ), 44746 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Iterator Helper", JS_PROP_CONFIGURABLE ), 44747 }; 44748 44749 static const JSCFunctionListEntry js_array_unscopables_funcs[] = { 44750 JS_PROP_BOOL_DEF("at", TRUE, JS_PROP_C_W_E), 44751 JS_PROP_BOOL_DEF("copyWithin", TRUE, JS_PROP_C_W_E), 44752 JS_PROP_BOOL_DEF("entries", TRUE, JS_PROP_C_W_E), 44753 JS_PROP_BOOL_DEF("fill", TRUE, JS_PROP_C_W_E), 44754 JS_PROP_BOOL_DEF("find", TRUE, JS_PROP_C_W_E), 44755 JS_PROP_BOOL_DEF("findIndex", TRUE, JS_PROP_C_W_E), 44756 JS_PROP_BOOL_DEF("findLast", TRUE, JS_PROP_C_W_E), 44757 JS_PROP_BOOL_DEF("findLastIndex", TRUE, JS_PROP_C_W_E), 44758 JS_PROP_BOOL_DEF("flat", TRUE, JS_PROP_C_W_E), 44759 JS_PROP_BOOL_DEF("flatMap", TRUE, JS_PROP_C_W_E), 44760 JS_PROP_BOOL_DEF("includes", TRUE, JS_PROP_C_W_E), 44761 JS_PROP_BOOL_DEF("keys", TRUE, JS_PROP_C_W_E), 44762 JS_PROP_BOOL_DEF("toReversed", TRUE, JS_PROP_C_W_E), 44763 JS_PROP_BOOL_DEF("toSorted", TRUE, JS_PROP_C_W_E), 44764 JS_PROP_BOOL_DEF("toSpliced", TRUE, JS_PROP_C_W_E), 44765 JS_PROP_BOOL_DEF("values", TRUE, JS_PROP_C_W_E), 44766 }; 44767 44768 static const JSCFunctionListEntry js_array_proto_funcs[] = { 44769 JS_CFUNC_DEF("at", 1, js_array_at ), 44770 JS_CFUNC_DEF("with", 2, js_array_with ), 44771 JS_CFUNC_DEF("concat", 1, js_array_concat ), 44772 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), 44773 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), 44774 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), 44775 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), 44776 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), 44777 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), 44778 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), 44779 JS_CFUNC_DEF("fill", 1, js_array_fill ), 44780 JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ), 44781 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ), 44782 JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ), 44783 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ), 44784 JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), 44785 JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), 44786 JS_CFUNC_DEF("includes", 1, js_array_includes ), 44787 JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), 44788 JS_CFUNC_DEF("toString", 0, js_array_toString ), 44789 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), 44790 JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), 44791 JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), 44792 JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), 44793 JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), 44794 JS_CFUNC_DEF("reverse", 0, js_array_reverse ), 44795 JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), 44796 JS_CFUNC_DEF("sort", 1, js_array_sort ), 44797 JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), 44798 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), 44799 JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), 44800 JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), 44801 JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), 44802 JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), 44803 JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), 44804 JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ), 44805 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 44806 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ), 44807 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), 44808 JS_OBJECT_DEF("[Symbol.unscopables]", js_array_unscopables_funcs, countof(js_array_unscopables_funcs), JS_PROP_CONFIGURABLE ), 44809 }; 44810 44811 static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { 44812 JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ), 44813 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ), 44814 }; 44815 44816 /* Number */ 44817 44818 static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, 44819 int argc, JSValueConst *argv) 44820 { 44821 JSValue val, obj; 44822 if (argc == 0) { 44823 val = JS_NewInt32(ctx, 0); 44824 } else { 44825 val = JS_ToNumeric(ctx, argv[0]); 44826 if (JS_IsException(val)) 44827 return val; 44828 switch(JS_VALUE_GET_TAG(val)) { 44829 case JS_TAG_SHORT_BIG_INT: 44830 val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val)); 44831 if (JS_IsException(val)) 44832 return val; 44833 break; 44834 case JS_TAG_BIG_INT: 44835 { 44836 JSBigInt *p = JS_VALUE_GET_PTR(val); 44837 double d; 44838 d = js_bigint_to_float64(ctx, p); 44839 JS_FreeValue(ctx, val); 44840 val = JS_NewFloat64(ctx, d); 44841 } 44842 break; 44843 default: 44844 break; 44845 } 44846 } 44847 if (!JS_IsUndefined(new_target)) { 44848 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); 44849 if (!JS_IsException(obj)) 44850 JS_SetObjectData(ctx, obj, val); 44851 return obj; 44852 } else { 44853 return val; 44854 } 44855 } 44856 44857 #if 0 44858 static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val, 44859 int argc, JSValueConst *argv) 44860 { 44861 return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0])); 44862 } 44863 44864 static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, 44865 int argc, JSValueConst *argv) 44866 { 44867 int64_t v; 44868 if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0]))) 44869 return JS_EXCEPTION; 44870 return JS_NewInt64(ctx, v); 44871 } 44872 #endif 44873 44874 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, 44875 int argc, JSValueConst *argv) 44876 { 44877 if (!JS_IsNumber(argv[0])) 44878 return JS_FALSE; 44879 return js_global_isNaN(ctx, this_val, argc, argv); 44880 } 44881 44882 static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, 44883 int argc, JSValueConst *argv) 44884 { 44885 if (!JS_IsNumber(argv[0])) 44886 return JS_FALSE; 44887 return js_global_isFinite(ctx, this_val, argc, argv); 44888 } 44889 44890 static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, 44891 int argc, JSValueConst *argv) 44892 { 44893 int ret; 44894 ret = JS_NumberIsInteger(ctx, argv[0]); 44895 if (ret < 0) 44896 return JS_EXCEPTION; 44897 else 44898 return JS_NewBool(ctx, ret); 44899 } 44900 44901 static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, 44902 int argc, JSValueConst *argv) 44903 { 44904 double d; 44905 if (!JS_IsNumber(argv[0])) 44906 return JS_FALSE; 44907 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 44908 return JS_EXCEPTION; 44909 return JS_NewBool(ctx, is_safe_integer(d)); 44910 } 44911 44912 static const JSCFunctionListEntry js_number_funcs[] = { 44913 /* global ParseInt and parseFloat should be defined already or delayed */ 44914 JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ), 44915 JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ), 44916 JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ), 44917 JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ), 44918 JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ), 44919 JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), 44920 JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), 44921 JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), 44922 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), 44923 JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), 44924 JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), 44925 JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ 44926 JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ 44927 JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ 44928 //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ), 44929 //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ), 44930 }; 44931 44932 static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) 44933 { 44934 if (JS_IsNumber(this_val)) 44935 return JS_DupValue(ctx, this_val); 44936 44937 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 44938 JSObject *p = JS_VALUE_GET_OBJ(this_val); 44939 if (p->class_id == JS_CLASS_NUMBER) { 44940 if (JS_IsNumber(p->u.object_data)) 44941 return JS_DupValue(ctx, p->u.object_data); 44942 } 44943 } 44944 return JS_ThrowTypeError(ctx, "not a number"); 44945 } 44946 44947 static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, 44948 int argc, JSValueConst *argv) 44949 { 44950 return js_thisNumberValue(ctx, this_val); 44951 } 44952 44953 static int js_get_radix(JSContext *ctx, JSValueConst val) 44954 { 44955 int radix; 44956 if (JS_ToInt32Sat(ctx, &radix, val)) 44957 return -1; 44958 if (radix < 2 || radix > 36) { 44959 JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); 44960 return -1; 44961 } 44962 return radix; 44963 } 44964 44965 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, 44966 int argc, JSValueConst *argv, int magic) 44967 { 44968 JSValue val; 44969 int base, flags; 44970 double d; 44971 44972 val = js_thisNumberValue(ctx, this_val); 44973 if (JS_IsException(val)) 44974 return val; 44975 if (magic || JS_IsUndefined(argv[0])) { 44976 base = 10; 44977 } else { 44978 base = js_get_radix(ctx, argv[0]); 44979 if (base < 0) 44980 goto fail; 44981 } 44982 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { 44983 char buf1[70]; 44984 int len; 44985 len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base); 44986 return js_new_string8_len(ctx, buf1, len); 44987 } 44988 if (JS_ToFloat64Free(ctx, &d, val)) 44989 return JS_EXCEPTION; 44990 flags = JS_DTOA_FORMAT_FREE; 44991 if (base != 10) 44992 flags |= JS_DTOA_EXP_DISABLED; 44993 return js_dtoa2(ctx, d, base, 0, flags); 44994 fail: 44995 JS_FreeValue(ctx, val); 44996 return JS_EXCEPTION; 44997 } 44998 44999 static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, 45000 int argc, JSValueConst *argv) 45001 { 45002 JSValue val; 45003 int f, flags; 45004 double d; 45005 45006 val = js_thisNumberValue(ctx, this_val); 45007 if (JS_IsException(val)) 45008 return val; 45009 if (JS_ToFloat64Free(ctx, &d, val)) 45010 return JS_EXCEPTION; 45011 if (JS_ToInt32Sat(ctx, &f, argv[0])) 45012 return JS_EXCEPTION; 45013 if (f < 0 || f > 100) 45014 return JS_ThrowRangeError(ctx, "invalid number of digits"); 45015 if (fabs(d) >= 1e21) 45016 flags = JS_DTOA_FORMAT_FREE; 45017 else 45018 flags = JS_DTOA_FORMAT_FRAC; 45019 return js_dtoa2(ctx, d, 10, f, flags); 45020 } 45021 45022 static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, 45023 int argc, JSValueConst *argv) 45024 { 45025 JSValue val; 45026 int f, flags; 45027 double d; 45028 45029 val = js_thisNumberValue(ctx, this_val); 45030 if (JS_IsException(val)) 45031 return val; 45032 if (JS_ToFloat64Free(ctx, &d, val)) 45033 return JS_EXCEPTION; 45034 if (JS_ToInt32Sat(ctx, &f, argv[0])) 45035 return JS_EXCEPTION; 45036 if (!isfinite(d)) { 45037 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); 45038 } 45039 if (JS_IsUndefined(argv[0])) { 45040 flags = JS_DTOA_FORMAT_FREE; 45041 f = 0; 45042 } else { 45043 if (f < 0 || f > 100) 45044 return JS_ThrowRangeError(ctx, "invalid number of digits"); 45045 f++; 45046 flags = JS_DTOA_FORMAT_FIXED; 45047 } 45048 return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED); 45049 } 45050 45051 static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, 45052 int argc, JSValueConst *argv) 45053 { 45054 JSValue val; 45055 int p; 45056 double d; 45057 45058 val = js_thisNumberValue(ctx, this_val); 45059 if (JS_IsException(val)) 45060 return val; 45061 if (JS_ToFloat64Free(ctx, &d, val)) 45062 return JS_EXCEPTION; 45063 if (JS_IsUndefined(argv[0])) 45064 goto to_string; 45065 if (JS_ToInt32Sat(ctx, &p, argv[0])) 45066 return JS_EXCEPTION; 45067 if (!isfinite(d)) { 45068 to_string: 45069 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); 45070 } 45071 if (p < 1 || p > 100) 45072 return JS_ThrowRangeError(ctx, "invalid number of digits"); 45073 return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED); 45074 } 45075 45076 static const JSCFunctionListEntry js_number_proto_funcs[] = { 45077 JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), 45078 JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), 45079 JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), 45080 JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ), 45081 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ), 45082 JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), 45083 }; 45084 45085 static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, 45086 int argc, JSValueConst *argv) 45087 { 45088 const char *str, *p; 45089 int radix, flags; 45090 JSValue ret; 45091 45092 str = JS_ToCString(ctx, argv[0]); 45093 if (!str) 45094 return JS_EXCEPTION; 45095 if (JS_ToInt32(ctx, &radix, argv[1])) { 45096 JS_FreeCString(ctx, str); 45097 return JS_EXCEPTION; 45098 } 45099 if (radix != 0 && (radix < 2 || radix > 36)) { 45100 ret = JS_NAN; 45101 } else { 45102 p = str; 45103 p += skip_spaces(p); 45104 flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; 45105 ret = js_atof(ctx, p, NULL, radix, flags); 45106 } 45107 JS_FreeCString(ctx, str); 45108 return ret; 45109 } 45110 45111 static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, 45112 int argc, JSValueConst *argv) 45113 { 45114 const char *str, *p; 45115 JSValue ret; 45116 45117 str = JS_ToCString(ctx, argv[0]); 45118 if (!str) 45119 return JS_EXCEPTION; 45120 p = str; 45121 p += skip_spaces(p); 45122 ret = js_atof(ctx, p, NULL, 10, 0); 45123 JS_FreeCString(ctx, str); 45124 return ret; 45125 } 45126 45127 /* Boolean */ 45128 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target, 45129 int argc, JSValueConst *argv) 45130 { 45131 JSValue val, obj; 45132 val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); 45133 if (!JS_IsUndefined(new_target)) { 45134 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); 45135 if (!JS_IsException(obj)) 45136 JS_SetObjectData(ctx, obj, val); 45137 return obj; 45138 } else { 45139 return val; 45140 } 45141 } 45142 45143 static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val) 45144 { 45145 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) 45146 return JS_DupValue(ctx, this_val); 45147 45148 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 45149 JSObject *p = JS_VALUE_GET_OBJ(this_val); 45150 if (p->class_id == JS_CLASS_BOOLEAN) { 45151 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) 45152 return p->u.object_data; 45153 } 45154 } 45155 return JS_ThrowTypeError(ctx, "not a boolean"); 45156 } 45157 45158 static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val, 45159 int argc, JSValueConst *argv) 45160 { 45161 JSValue val = js_thisBooleanValue(ctx, this_val); 45162 if (JS_IsException(val)) 45163 return val; 45164 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? 45165 JS_ATOM_true : JS_ATOM_false); 45166 } 45167 45168 static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val, 45169 int argc, JSValueConst *argv) 45170 { 45171 return js_thisBooleanValue(ctx, this_val); 45172 } 45173 45174 static const JSCFunctionListEntry js_boolean_proto_funcs[] = { 45175 JS_CFUNC_DEF("toString", 0, js_boolean_toString ), 45176 JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ), 45177 }; 45178 45179 /* String */ 45180 45181 static int js_string_get_own_property(JSContext *ctx, 45182 JSPropertyDescriptor *desc, 45183 JSValueConst obj, JSAtom prop) 45184 { 45185 JSObject *p; 45186 JSString *p1; 45187 uint32_t idx, ch; 45188 45189 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ 45190 if (__JS_AtomIsTaggedInt(prop)) { 45191 p = JS_VALUE_GET_OBJ(obj); 45192 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { 45193 p1 = JS_VALUE_GET_STRING(p->u.object_data); 45194 idx = __JS_AtomToUInt32(prop); 45195 if (idx < p1->len) { 45196 if (desc) { 45197 ch = string_get(p1, idx); 45198 desc->flags = JS_PROP_ENUMERABLE; 45199 desc->value = js_new_string_char(ctx, ch); 45200 desc->getter = JS_UNDEFINED; 45201 desc->setter = JS_UNDEFINED; 45202 } 45203 return TRUE; 45204 } 45205 } 45206 } 45207 return FALSE; 45208 } 45209 45210 static int js_string_define_own_property(JSContext *ctx, 45211 JSValueConst this_obj, 45212 JSAtom prop, JSValueConst val, 45213 JSValueConst getter, 45214 JSValueConst setter, int flags) 45215 { 45216 uint32_t idx; 45217 JSObject *p; 45218 JSString *p1, *p2; 45219 45220 if (__JS_AtomIsTaggedInt(prop)) { 45221 idx = __JS_AtomToUInt32(prop); 45222 p = JS_VALUE_GET_OBJ(this_obj); 45223 if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) 45224 goto def; 45225 p1 = JS_VALUE_GET_STRING(p->u.object_data); 45226 if (idx >= p1->len) 45227 goto def; 45228 if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) 45229 goto fail; 45230 /* check that the same value is configured */ 45231 if (flags & JS_PROP_HAS_VALUE) { 45232 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 45233 goto fail; 45234 p2 = JS_VALUE_GET_STRING(val); 45235 if (p2->len != 1) 45236 goto fail; 45237 if (string_get(p1, idx) != string_get(p2, 0)) { 45238 fail: 45239 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); 45240 } 45241 } 45242 return TRUE; 45243 } else { 45244 def: 45245 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, 45246 flags | JS_PROP_NO_EXOTIC); 45247 } 45248 } 45249 45250 static int js_string_delete_property(JSContext *ctx, 45251 JSValueConst obj, JSAtom prop) 45252 { 45253 uint32_t idx; 45254 45255 if (__JS_AtomIsTaggedInt(prop)) { 45256 idx = __JS_AtomToUInt32(prop); 45257 if (idx < js_string_obj_get_length(ctx, obj)) { 45258 return FALSE; 45259 } 45260 } 45261 return TRUE; 45262 } 45263 45264 static const JSClassExoticMethods js_string_exotic_methods = { 45265 .get_own_property = js_string_get_own_property, 45266 .define_own_property = js_string_define_own_property, 45267 .delete_property = js_string_delete_property, 45268 }; 45269 45270 static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, 45271 int argc, JSValueConst *argv) 45272 { 45273 JSValue val, obj; 45274 if (argc == 0) { 45275 val = JS_AtomToString(ctx, JS_ATOM_empty_string); 45276 } else { 45277 if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { 45278 JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); 45279 val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); 45280 } else { 45281 val = JS_ToString(ctx, argv[0]); 45282 } 45283 if (JS_IsException(val)) 45284 return val; 45285 } 45286 if (!JS_IsUndefined(new_target)) { 45287 JSString *p1 = JS_VALUE_GET_STRING(val); 45288 45289 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); 45290 if (JS_IsException(obj)) { 45291 JS_FreeValue(ctx, val); 45292 } else { 45293 JS_SetObjectData(ctx, obj, val); 45294 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); 45295 } 45296 return obj; 45297 } else { 45298 return val; 45299 } 45300 } 45301 45302 static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) 45303 { 45304 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING || 45305 JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING_ROPE) 45306 return JS_DupValue(ctx, this_val); 45307 45308 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 45309 JSObject *p = JS_VALUE_GET_OBJ(this_val); 45310 if (p->class_id == JS_CLASS_STRING) { 45311 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) 45312 return JS_DupValue(ctx, p->u.object_data); 45313 } 45314 } 45315 return JS_ThrowTypeError(ctx, "not a string"); 45316 } 45317 45318 static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val, 45319 int argc, JSValueConst *argv) 45320 { 45321 int i; 45322 StringBuffer b_s, *b = &b_s; 45323 45324 string_buffer_init(ctx, b, argc); 45325 45326 for(i = 0; i < argc; i++) { 45327 int32_t c; 45328 if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { 45329 string_buffer_free(b); 45330 return JS_EXCEPTION; 45331 } 45332 } 45333 return string_buffer_end(b); 45334 } 45335 45336 static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, 45337 int argc, JSValueConst *argv) 45338 { 45339 double d; 45340 int i, c; 45341 StringBuffer b_s, *b = &b_s; 45342 45343 /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ 45344 45345 if (string_buffer_init(ctx, b, argc)) 45346 goto fail; 45347 for(i = 0; i < argc; i++) { 45348 if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { 45349 c = JS_VALUE_GET_INT(argv[i]); 45350 if (c < 0 || c > 0x10ffff) 45351 goto range_error; 45352 } else { 45353 if (JS_ToFloat64(ctx, &d, argv[i])) 45354 goto fail; 45355 if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d) 45356 goto range_error; 45357 } 45358 if (string_buffer_putc(b, c)) 45359 goto fail; 45360 } 45361 return string_buffer_end(b); 45362 45363 range_error: 45364 JS_ThrowRangeError(ctx, "invalid code point"); 45365 fail: 45366 string_buffer_free(b); 45367 return JS_EXCEPTION; 45368 } 45369 45370 static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, 45371 int argc, JSValueConst *argv) 45372 { 45373 // raw(temp,...a) 45374 JSValue cooked, val, raw; 45375 StringBuffer b_s, *b = &b_s; 45376 int64_t i, n; 45377 45378 string_buffer_init(ctx, b, 0); 45379 raw = JS_UNDEFINED; 45380 cooked = JS_ToObject(ctx, argv[0]); 45381 if (JS_IsException(cooked)) 45382 goto exception; 45383 raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); 45384 if (JS_IsException(raw)) 45385 goto exception; 45386 if (js_get_length64(ctx, &n, raw) < 0) 45387 goto exception; 45388 45389 for (i = 0; i < n; i++) { 45390 val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); 45391 if (JS_IsException(val)) 45392 goto exception; 45393 string_buffer_concat_value_free(b, val); 45394 if (i < n - 1 && i + 1 < argc) { 45395 if (string_buffer_concat_value(b, argv[i + 1])) 45396 goto exception; 45397 } 45398 } 45399 JS_FreeValue(ctx, cooked); 45400 JS_FreeValue(ctx, raw); 45401 return string_buffer_end(b); 45402 45403 exception: 45404 JS_FreeValue(ctx, cooked); 45405 JS_FreeValue(ctx, raw); 45406 string_buffer_free(b); 45407 return JS_EXCEPTION; 45408 } 45409 45410 /* only used in test262 */ 45411 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, 45412 int argc, JSValueConst *argv) 45413 { 45414 uint32_t start, end, i, n; 45415 StringBuffer b_s, *b = &b_s; 45416 45417 if (JS_ToUint32(ctx, &start, argv[0]) || 45418 JS_ToUint32(ctx, &end, argv[1])) 45419 return JS_EXCEPTION; 45420 end = min_uint32(end, 0x10ffff + 1); 45421 45422 if (start > end) { 45423 start = end; 45424 } 45425 n = end - start; 45426 if (end > 0x10000) { 45427 n += end - max_uint32(start, 0x10000); 45428 } 45429 if (string_buffer_init2(ctx, b, n, end >= 0x100)) 45430 return JS_EXCEPTION; 45431 for(i = start; i < end; i++) { 45432 string_buffer_putc(b, i); 45433 } 45434 return string_buffer_end(b); 45435 } 45436 45437 #if 0 45438 static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, 45439 int argc, JSValueConst *argv) 45440 { 45441 int c; 45442 if (JS_ToInt32(ctx, &c, argv[0])) 45443 return JS_EXCEPTION; 45444 return JS_NewBool(ctx, lre_is_space(c)); 45445 } 45446 #endif 45447 45448 static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, 45449 int argc, JSValueConst *argv) 45450 { 45451 JSValue val, ret; 45452 JSString *p; 45453 int idx, c; 45454 45455 val = JS_ToStringCheckObject(ctx, this_val); 45456 if (JS_IsException(val)) 45457 return val; 45458 p = JS_VALUE_GET_STRING(val); 45459 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 45460 JS_FreeValue(ctx, val); 45461 return JS_EXCEPTION; 45462 } 45463 if (idx < 0 || idx >= p->len) { 45464 ret = JS_NAN; 45465 } else { 45466 c = string_get(p, idx); 45467 ret = JS_NewInt32(ctx, c); 45468 } 45469 JS_FreeValue(ctx, val); 45470 return ret; 45471 } 45472 45473 static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, 45474 int argc, JSValueConst *argv, int is_at) 45475 { 45476 JSValue val, ret; 45477 JSString *p; 45478 int idx, c; 45479 45480 val = JS_ToStringCheckObject(ctx, this_val); 45481 if (JS_IsException(val)) 45482 return val; 45483 p = JS_VALUE_GET_STRING(val); 45484 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 45485 JS_FreeValue(ctx, val); 45486 return JS_EXCEPTION; 45487 } 45488 if (idx < 0 && is_at) 45489 idx += p->len; 45490 if (idx < 0 || idx >= p->len) { 45491 if (is_at) 45492 ret = JS_UNDEFINED; 45493 else 45494 ret = JS_AtomToString(ctx, JS_ATOM_empty_string); 45495 } else { 45496 c = string_get(p, idx); 45497 ret = js_new_string_char(ctx, c); 45498 } 45499 JS_FreeValue(ctx, val); 45500 return ret; 45501 } 45502 45503 static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val, 45504 int argc, JSValueConst *argv) 45505 { 45506 JSValue val, ret; 45507 JSString *p; 45508 int idx, c; 45509 45510 val = JS_ToStringCheckObject(ctx, this_val); 45511 if (JS_IsException(val)) 45512 return val; 45513 p = JS_VALUE_GET_STRING(val); 45514 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 45515 JS_FreeValue(ctx, val); 45516 return JS_EXCEPTION; 45517 } 45518 if (idx < 0 || idx >= p->len) { 45519 ret = JS_UNDEFINED; 45520 } else { 45521 c = string_getc(p, &idx); 45522 ret = JS_NewInt32(ctx, c); 45523 } 45524 JS_FreeValue(ctx, val); 45525 return ret; 45526 } 45527 45528 static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val, 45529 int argc, JSValueConst *argv) 45530 { 45531 JSValue r; 45532 int i; 45533 45534 /* XXX: Use more efficient method */ 45535 /* XXX: This method is OK if r has a single refcount */ 45536 /* XXX: should use string_buffer? */ 45537 r = JS_ToStringCheckObject(ctx, this_val); 45538 for (i = 0; i < argc; i++) { 45539 if (JS_IsException(r)) 45540 break; 45541 r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); 45542 } 45543 return r; 45544 } 45545 45546 static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) 45547 { 45548 int i, c1, c2; 45549 for (i = 0; i < len; i++) { 45550 if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) 45551 return c1 - c2; 45552 } 45553 return 0; 45554 } 45555 45556 static int string_indexof_char(JSString *p, int c, int from) 45557 { 45558 /* assuming 0 <= from <= p->len */ 45559 int i, len = p->len; 45560 if (p->is_wide_char) { 45561 for (i = from; i < len; i++) { 45562 if (p->u.str16[i] == c) 45563 return i; 45564 } 45565 } else { 45566 if ((c & ~0xff) == 0) { 45567 for (i = from; i < len; i++) { 45568 if (p->u.str8[i] == (uint8_t)c) 45569 return i; 45570 } 45571 } 45572 } 45573 return -1; 45574 } 45575 45576 static int string_indexof(JSString *p1, JSString *p2, int from) 45577 { 45578 /* assuming 0 <= from <= p1->len */ 45579 int c, i, j, len1 = p1->len, len2 = p2->len; 45580 if (len2 == 0) 45581 return from; 45582 for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { 45583 j = string_indexof_char(p1, c, i); 45584 if (j < 0 || j + len2 > len1) 45585 break; 45586 if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) 45587 return j; 45588 } 45589 return -1; 45590 } 45591 45592 static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) 45593 { 45594 if (!unicode || index >= p->len || !p->is_wide_char) { 45595 index++; 45596 } else { 45597 int index32 = (int)index; 45598 string_getc(p, &index32); 45599 index = index32; 45600 } 45601 return index; 45602 } 45603 45604 /* return the position of the first invalid character in the string or 45605 -1 if none */ 45606 static int js_string_find_invalid_codepoint(JSString *p) 45607 { 45608 int i; 45609 if (!p->is_wide_char) 45610 return -1; 45611 for(i = 0; i < p->len; i++) { 45612 uint32_t c = p->u.str16[i]; 45613 if (is_surrogate(c)) { 45614 if (is_hi_surrogate(c) && (i + 1) < p->len 45615 && is_lo_surrogate(p->u.str16[i + 1])) { 45616 i++; 45617 } else { 45618 return i; 45619 } 45620 } 45621 } 45622 return -1; 45623 } 45624 45625 static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val, 45626 int argc, JSValueConst *argv) 45627 { 45628 JSValue str; 45629 JSString *p; 45630 BOOL ret; 45631 45632 str = JS_ToStringCheckObject(ctx, this_val); 45633 if (JS_IsException(str)) 45634 return JS_EXCEPTION; 45635 p = JS_VALUE_GET_STRING(str); 45636 ret = (js_string_find_invalid_codepoint(p) < 0); 45637 JS_FreeValue(ctx, str); 45638 return JS_NewBool(ctx, ret); 45639 } 45640 45641 static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val, 45642 int argc, JSValueConst *argv) 45643 { 45644 JSValue str, ret; 45645 JSString *p; 45646 int i; 45647 45648 str = JS_ToStringCheckObject(ctx, this_val); 45649 if (JS_IsException(str)) 45650 return JS_EXCEPTION; 45651 45652 p = JS_VALUE_GET_STRING(str); 45653 /* avoid reallocating the string if it is well-formed */ 45654 i = js_string_find_invalid_codepoint(p); 45655 if (i < 0) 45656 return str; 45657 45658 ret = js_new_string16_len(ctx, p->u.str16, p->len); 45659 JS_FreeValue(ctx, str); 45660 if (JS_IsException(ret)) 45661 return JS_EXCEPTION; 45662 45663 p = JS_VALUE_GET_STRING(ret); 45664 for (; i < p->len; i++) { 45665 uint32_t c = p->u.str16[i]; 45666 if (is_surrogate(c)) { 45667 if (is_hi_surrogate(c) && (i + 1) < p->len 45668 && is_lo_surrogate(p->u.str16[i + 1])) { 45669 i++; 45670 } else { 45671 p->u.str16[i] = 0xFFFD; 45672 } 45673 } 45674 } 45675 return ret; 45676 } 45677 45678 static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, 45679 int argc, JSValueConst *argv, int lastIndexOf) 45680 { 45681 JSValue str, v; 45682 int i, len, v_len, pos, start, stop, ret, inc; 45683 JSString *p; 45684 JSString *p1; 45685 45686 str = JS_ToStringCheckObject(ctx, this_val); 45687 if (JS_IsException(str)) 45688 return str; 45689 v = JS_ToString(ctx, argv[0]); 45690 if (JS_IsException(v)) 45691 goto fail; 45692 p = JS_VALUE_GET_STRING(str); 45693 p1 = JS_VALUE_GET_STRING(v); 45694 len = p->len; 45695 v_len = p1->len; 45696 if (lastIndexOf) { 45697 pos = len - v_len; 45698 if (argc > 1) { 45699 double d; 45700 if (JS_ToFloat64(ctx, &d, argv[1])) 45701 goto fail; 45702 if (!isnan(d)) { 45703 if (d <= 0) 45704 pos = 0; 45705 else if (d < pos) 45706 pos = d; 45707 } 45708 } 45709 start = pos; 45710 stop = 0; 45711 inc = -1; 45712 } else { 45713 pos = 0; 45714 if (argc > 1) { 45715 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) 45716 goto fail; 45717 } 45718 start = pos; 45719 stop = len - v_len; 45720 inc = 1; 45721 } 45722 ret = -1; 45723 if (len >= v_len && inc * (stop - start) >= 0) { 45724 for (i = start;; i += inc) { 45725 if (!string_cmp(p, p1, i, 0, v_len)) { 45726 ret = i; 45727 break; 45728 } 45729 if (i == stop) 45730 break; 45731 } 45732 } 45733 JS_FreeValue(ctx, str); 45734 JS_FreeValue(ctx, v); 45735 return JS_NewInt32(ctx, ret); 45736 45737 fail: 45738 JS_FreeValue(ctx, str); 45739 JS_FreeValue(ctx, v); 45740 return JS_EXCEPTION; 45741 } 45742 45743 /* return < 0 if exception or TRUE/FALSE */ 45744 static int js_is_regexp(JSContext *ctx, JSValueConst obj); 45745 45746 static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, 45747 int argc, JSValueConst *argv, int magic) 45748 { 45749 JSValue str, v = JS_UNDEFINED; 45750 int i, len, v_len, pos, start, stop, ret; 45751 JSString *p; 45752 JSString *p1; 45753 45754 str = JS_ToStringCheckObject(ctx, this_val); 45755 if (JS_IsException(str)) 45756 return str; 45757 ret = js_is_regexp(ctx, argv[0]); 45758 if (ret) { 45759 if (ret > 0) 45760 JS_ThrowTypeError(ctx, "regexp not supported"); 45761 goto fail; 45762 } 45763 v = JS_ToString(ctx, argv[0]); 45764 if (JS_IsException(v)) 45765 goto fail; 45766 p = JS_VALUE_GET_STRING(str); 45767 p1 = JS_VALUE_GET_STRING(v); 45768 len = p->len; 45769 v_len = p1->len; 45770 pos = (magic == 2) ? len : 0; 45771 if (argc > 1 && !JS_IsUndefined(argv[1])) { 45772 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) 45773 goto fail; 45774 } 45775 len -= v_len; 45776 ret = 0; 45777 if (magic == 0) { 45778 start = pos; 45779 stop = len; 45780 } else { 45781 if (magic == 1) { 45782 if (pos > len) 45783 goto done; 45784 } else { 45785 pos -= v_len; 45786 } 45787 start = stop = pos; 45788 } 45789 if (start >= 0 && start <= stop) { 45790 for (i = start;; i++) { 45791 if (!string_cmp(p, p1, i, 0, v_len)) { 45792 ret = 1; 45793 break; 45794 } 45795 if (i == stop) 45796 break; 45797 } 45798 } 45799 done: 45800 JS_FreeValue(ctx, str); 45801 JS_FreeValue(ctx, v); 45802 return JS_NewBool(ctx, ret); 45803 45804 fail: 45805 JS_FreeValue(ctx, str); 45806 JS_FreeValue(ctx, v); 45807 return JS_EXCEPTION; 45808 } 45809 45810 static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) 45811 { 45812 int ret; 45813 JSValue flags; 45814 45815 ret = js_is_regexp(ctx, regexp); 45816 if (ret < 0) 45817 return -1; 45818 if (ret) { 45819 flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags); 45820 if (JS_IsException(flags)) 45821 return -1; 45822 if (JS_IsUndefined(flags) || JS_IsNull(flags)) { 45823 JS_ThrowTypeError(ctx, "cannot convert to object"); 45824 return -1; 45825 } 45826 flags = JS_ToStringFree(ctx, flags); 45827 if (JS_IsException(flags)) 45828 return -1; 45829 ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0); 45830 JS_FreeValue(ctx, flags); 45831 if (ret < 0) { 45832 JS_ThrowTypeError(ctx, "regexp must have the 'g' flag"); 45833 return -1; 45834 } 45835 } 45836 return 0; 45837 } 45838 45839 static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, 45840 int argc, JSValueConst *argv, int atom) 45841 { 45842 // match(rx), search(rx), matchAll(rx) 45843 // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll 45844 JSValueConst O = this_val, regexp = argv[0], args[2]; 45845 JSValue matcher, S, rx, result, str; 45846 int args_len; 45847 45848 if (JS_IsUndefined(O) || JS_IsNull(O)) 45849 return JS_ThrowTypeError(ctx, "cannot convert to object"); 45850 45851 if (JS_IsObject(regexp)) { 45852 matcher = JS_GetProperty(ctx, regexp, atom); 45853 if (JS_IsException(matcher)) 45854 return JS_EXCEPTION; 45855 if (atom == JS_ATOM_Symbol_matchAll) { 45856 if (check_regexp_g_flag(ctx, regexp) < 0) { 45857 JS_FreeValue(ctx, matcher); 45858 return JS_EXCEPTION; 45859 } 45860 } 45861 if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { 45862 return JS_CallFree(ctx, matcher, regexp, 1, &O); 45863 } 45864 } 45865 S = JS_ToString(ctx, O); 45866 if (JS_IsException(S)) 45867 return JS_EXCEPTION; 45868 args_len = 1; 45869 args[0] = regexp; 45870 str = JS_UNDEFINED; 45871 if (atom == JS_ATOM_Symbol_matchAll) { 45872 str = js_new_string8(ctx, "g"); 45873 if (JS_IsException(str)) 45874 goto fail; 45875 args[args_len++] = (JSValueConst)str; 45876 } 45877 rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); 45878 JS_FreeValue(ctx, str); 45879 if (JS_IsException(rx)) { 45880 fail: 45881 JS_FreeValue(ctx, S); 45882 return JS_EXCEPTION; 45883 } 45884 result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); 45885 JS_FreeValue(ctx, S); 45886 return result; 45887 } 45888 45889 /* if captures != NULL, captures_val and matched are ignored. Otherwise, 45890 captures_len is ignored */ 45891 static int js_string_GetSubstitution(JSContext *ctx, 45892 StringBuffer *b, 45893 JSValueConst matched, 45894 JSString *sp, 45895 uint32_t position, 45896 JSValueConst captures_val, 45897 JSValueConst namedCaptures, 45898 JSValueConst rep, 45899 uint8_t **captures, 45900 uint32_t captures_len) 45901 { 45902 JSValue capture, name, s; 45903 uint32_t len, matched_len; 45904 int i, j, j0, k, k1, shift; 45905 int c, c1; 45906 JSString *rp; 45907 45908 if (JS_VALUE_GET_TAG(rep) != JS_TAG_STRING) { 45909 JS_ThrowTypeError(ctx, "not a string"); 45910 goto exception; 45911 } 45912 shift = sp->is_wide_char; 45913 rp = JS_VALUE_GET_STRING(rep); 45914 45915 if (captures) { 45916 matched_len = (captures[1] - captures[0]) >> shift; 45917 } else { 45918 captures_len = 0; 45919 if (!JS_IsUndefined(captures_val)) { 45920 if (js_get_length32(ctx, &captures_len, captures_val)) 45921 goto exception; 45922 } 45923 if (js_get_length32(ctx, &matched_len, matched)) 45924 goto exception; 45925 } 45926 45927 len = rp->len; 45928 i = 0; 45929 for(;;) { 45930 j = string_indexof_char(rp, '$', i); 45931 if (j < 0 || j + 1 >= len) 45932 break; 45933 string_buffer_concat(b, rp, i, j); 45934 j0 = j++; 45935 c = string_get(rp, j++); 45936 if (c == '$') { 45937 string_buffer_putc8(b, '$'); 45938 } else if (c == '&') { 45939 if (captures) { 45940 string_buffer_concat(b, sp, position, position + matched_len); 45941 } else { 45942 if (string_buffer_concat_value(b, matched)) 45943 goto exception; 45944 } 45945 } else if (c == '`') { 45946 string_buffer_concat(b, sp, 0, position); 45947 } else if (c == '\'') { 45948 string_buffer_concat(b, sp, position + matched_len, sp->len); 45949 } else if (c >= '0' && c <= '9') { 45950 k = c - '0'; 45951 if (j < len) { 45952 c1 = string_get(rp, j); 45953 if (c1 >= '0' && c1 <= '9') { 45954 /* This behavior is specified in ES6 and refined in ECMA 2019 */ 45955 /* ECMA 2019 does not have the extra test, but 45956 Test262 S15.5.4.11_A3_T1..3 require this behavior */ 45957 k1 = k * 10 + c1 - '0'; 45958 if (k1 >= 1 && k1 < captures_len) { 45959 k = k1; 45960 j++; 45961 } 45962 } 45963 } 45964 if (k >= 1 && k < captures_len) { 45965 if (captures) { 45966 int start, end; 45967 if (captures[2 * k] && captures[2 * k + 1]) { 45968 start = (captures[2 * k] - sp->u.str8) >> shift; 45969 end = (captures[2 * k + 1] - sp->u.str8) >> shift; 45970 string_buffer_concat(b, sp, start, end); 45971 } 45972 } else { 45973 s = JS_GetPropertyInt64(ctx, captures_val, k); 45974 if (JS_IsException(s)) 45975 goto exception; 45976 if (!JS_IsUndefined(s)) { 45977 if (string_buffer_concat_value_free(b, s)) 45978 goto exception; 45979 } 45980 } 45981 } else { 45982 goto norep; 45983 } 45984 } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { 45985 k = string_indexof_char(rp, '>', j); 45986 if (k < 0) 45987 goto norep; 45988 name = js_sub_string(ctx, rp, j, k); 45989 if (JS_IsException(name)) 45990 goto exception; 45991 capture = JS_GetPropertyValue(ctx, namedCaptures, name); 45992 if (JS_IsException(capture)) 45993 goto exception; 45994 if (!JS_IsUndefined(capture)) { 45995 if (string_buffer_concat_value_free(b, capture)) 45996 goto exception; 45997 } 45998 j = k + 1; 45999 } else { 46000 norep: 46001 string_buffer_concat(b, rp, j0, j); 46002 } 46003 i = j; 46004 } 46005 string_buffer_concat(b, rp, i, rp->len); 46006 return 0; 46007 exception: 46008 return -1; 46009 } 46010 46011 static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, 46012 int argc, JSValueConst *argv, 46013 int is_replaceAll) 46014 { 46015 // replace(rx, rep) 46016 JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; 46017 JSValueConst args[3]; 46018 JSValue str, search_str, replaceValue_str, repl_str; 46019 JSString *sp, *searchp; 46020 StringBuffer b_s, *b = &b_s; 46021 int pos, functionalReplace, endOfLastMatch; 46022 BOOL is_first; 46023 46024 if (JS_IsUndefined(O) || JS_IsNull(O)) 46025 return JS_ThrowTypeError(ctx, "cannot convert to object"); 46026 46027 search_str = JS_UNDEFINED; 46028 replaceValue_str = JS_UNDEFINED; 46029 repl_str = JS_UNDEFINED; 46030 46031 if (JS_IsObject(searchValue)) { 46032 JSValue replacer; 46033 if (is_replaceAll) { 46034 if (check_regexp_g_flag(ctx, searchValue) < 0) 46035 return JS_EXCEPTION; 46036 } 46037 replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); 46038 if (JS_IsException(replacer)) 46039 return JS_EXCEPTION; 46040 if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { 46041 args[0] = O; 46042 args[1] = replaceValue; 46043 return JS_CallFree(ctx, replacer, searchValue, 2, args); 46044 } 46045 } 46046 string_buffer_init(ctx, b, 0); 46047 46048 str = JS_ToString(ctx, O); 46049 if (JS_IsException(str)) 46050 goto exception; 46051 search_str = JS_ToString(ctx, searchValue); 46052 if (JS_IsException(search_str)) 46053 goto exception; 46054 functionalReplace = JS_IsFunction(ctx, replaceValue); 46055 if (!functionalReplace) { 46056 replaceValue_str = JS_ToString(ctx, replaceValue); 46057 if (JS_IsException(replaceValue_str)) 46058 goto exception; 46059 } 46060 46061 sp = JS_VALUE_GET_STRING(str); 46062 searchp = JS_VALUE_GET_STRING(search_str); 46063 endOfLastMatch = 0; 46064 is_first = TRUE; 46065 for(;;) { 46066 if (unlikely(searchp->len == 0)) { 46067 if (is_first) 46068 pos = 0; 46069 else if (endOfLastMatch >= sp->len) 46070 pos = -1; 46071 else 46072 pos = endOfLastMatch + 1; 46073 } else { 46074 pos = string_indexof(sp, searchp, endOfLastMatch); 46075 } 46076 if (pos < 0) { 46077 if (is_first) { 46078 string_buffer_free(b); 46079 JS_FreeValue(ctx, search_str); 46080 JS_FreeValue(ctx, replaceValue_str); 46081 return str; 46082 } else { 46083 break; 46084 } 46085 } 46086 46087 string_buffer_concat(b, sp, endOfLastMatch, pos); 46088 46089 if (functionalReplace) { 46090 args[0] = search_str; 46091 args[1] = JS_NewInt32(ctx, pos); 46092 args[2] = str; 46093 repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); 46094 if (JS_IsException(repl_str)) 46095 goto exception; 46096 string_buffer_concat_value_free(b, repl_str); 46097 } else { 46098 if (js_string_GetSubstitution(ctx, b, search_str, sp, pos, 46099 JS_UNDEFINED, JS_UNDEFINED, replaceValue_str, 46100 NULL, 0)) { 46101 goto exception; 46102 } 46103 } 46104 46105 endOfLastMatch = pos + searchp->len; 46106 is_first = FALSE; 46107 if (!is_replaceAll) 46108 break; 46109 } 46110 string_buffer_concat(b, sp, endOfLastMatch, sp->len); 46111 JS_FreeValue(ctx, search_str); 46112 JS_FreeValue(ctx, replaceValue_str); 46113 JS_FreeValue(ctx, str); 46114 return string_buffer_end(b); 46115 46116 exception: 46117 string_buffer_free(b); 46118 JS_FreeValue(ctx, search_str); 46119 JS_FreeValue(ctx, replaceValue_str); 46120 JS_FreeValue(ctx, str); 46121 return JS_EXCEPTION; 46122 } 46123 46124 static JSValue js_string_split(JSContext *ctx, JSValueConst this_val, 46125 int argc, JSValueConst *argv) 46126 { 46127 // split(sep, limit) 46128 JSValueConst O = this_val, separator = argv[0], limit = argv[1]; 46129 JSValueConst args[2]; 46130 JSValue S, A, R, T; 46131 uint32_t lim, lengthA; 46132 int64_t p, q, s, r, e; 46133 JSString *sp, *rp; 46134 46135 if (JS_IsUndefined(O) || JS_IsNull(O)) 46136 return JS_ThrowTypeError(ctx, "cannot convert to object"); 46137 46138 S = JS_UNDEFINED; 46139 A = JS_UNDEFINED; 46140 R = JS_UNDEFINED; 46141 46142 if (JS_IsObject(separator)) { 46143 JSValue splitter; 46144 splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); 46145 if (JS_IsException(splitter)) 46146 return JS_EXCEPTION; 46147 if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { 46148 args[0] = O; 46149 args[1] = limit; 46150 return JS_CallFree(ctx, splitter, separator, 2, args); 46151 } 46152 } 46153 S = JS_ToString(ctx, O); 46154 if (JS_IsException(S)) 46155 goto exception; 46156 A = JS_NewArray(ctx); 46157 if (JS_IsException(A)) 46158 goto exception; 46159 lengthA = 0; 46160 if (JS_IsUndefined(limit)) { 46161 lim = 0xffffffff; 46162 } else { 46163 if (JS_ToUint32(ctx, &lim, limit) < 0) 46164 goto exception; 46165 } 46166 sp = JS_VALUE_GET_STRING(S); 46167 s = sp->len; 46168 R = JS_ToString(ctx, separator); 46169 if (JS_IsException(R)) 46170 goto exception; 46171 rp = JS_VALUE_GET_STRING(R); 46172 r = rp->len; 46173 p = 0; 46174 if (lim == 0) 46175 goto done; 46176 if (JS_IsUndefined(separator)) 46177 goto add_tail; 46178 if (s == 0) { 46179 if (r != 0) 46180 goto add_tail; 46181 goto done; 46182 } 46183 for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { 46184 e = string_indexof(sp, rp, q); 46185 if (e < 0) 46186 break; 46187 T = js_sub_string(ctx, sp, p, e); 46188 if (JS_IsException(T)) 46189 goto exception; 46190 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) 46191 goto exception; 46192 if (lengthA == lim) 46193 goto done; 46194 } 46195 add_tail: 46196 T = js_sub_string(ctx, sp, p, s); 46197 if (JS_IsException(T)) 46198 goto exception; 46199 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0) 46200 goto exception; 46201 done: 46202 JS_FreeValue(ctx, S); 46203 JS_FreeValue(ctx, R); 46204 return A; 46205 46206 exception: 46207 JS_FreeValue(ctx, A); 46208 JS_FreeValue(ctx, S); 46209 JS_FreeValue(ctx, R); 46210 return JS_EXCEPTION; 46211 } 46212 46213 static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val, 46214 int argc, JSValueConst *argv) 46215 { 46216 JSValue str, ret; 46217 int a, b, start, end; 46218 JSString *p; 46219 46220 str = JS_ToStringCheckObject(ctx, this_val); 46221 if (JS_IsException(str)) 46222 return str; 46223 p = JS_VALUE_GET_STRING(str); 46224 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { 46225 JS_FreeValue(ctx, str); 46226 return JS_EXCEPTION; 46227 } 46228 b = p->len; 46229 if (!JS_IsUndefined(argv[1])) { 46230 if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { 46231 JS_FreeValue(ctx, str); 46232 return JS_EXCEPTION; 46233 } 46234 } 46235 if (a < b) { 46236 start = a; 46237 end = b; 46238 } else { 46239 start = b; 46240 end = a; 46241 } 46242 ret = js_sub_string(ctx, p, start, end); 46243 JS_FreeValue(ctx, str); 46244 return ret; 46245 } 46246 46247 static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val, 46248 int argc, JSValueConst *argv) 46249 { 46250 JSValue str, ret; 46251 int a, len, n; 46252 JSString *p; 46253 46254 str = JS_ToStringCheckObject(ctx, this_val); 46255 if (JS_IsException(str)) 46256 return str; 46257 p = JS_VALUE_GET_STRING(str); 46258 len = p->len; 46259 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { 46260 JS_FreeValue(ctx, str); 46261 return JS_EXCEPTION; 46262 } 46263 n = len - a; 46264 if (!JS_IsUndefined(argv[1])) { 46265 if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { 46266 JS_FreeValue(ctx, str); 46267 return JS_EXCEPTION; 46268 } 46269 } 46270 ret = js_sub_string(ctx, p, a, a + n); 46271 JS_FreeValue(ctx, str); 46272 return ret; 46273 } 46274 46275 static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val, 46276 int argc, JSValueConst *argv) 46277 { 46278 JSValue str, ret; 46279 int len, start, end; 46280 JSString *p; 46281 46282 str = JS_ToStringCheckObject(ctx, this_val); 46283 if (JS_IsException(str)) 46284 return str; 46285 p = JS_VALUE_GET_STRING(str); 46286 len = p->len; 46287 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { 46288 JS_FreeValue(ctx, str); 46289 return JS_EXCEPTION; 46290 } 46291 end = len; 46292 if (!JS_IsUndefined(argv[1])) { 46293 if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { 46294 JS_FreeValue(ctx, str); 46295 return JS_EXCEPTION; 46296 } 46297 } 46298 ret = js_sub_string(ctx, p, start, max_int(end, start)); 46299 JS_FreeValue(ctx, str); 46300 return ret; 46301 } 46302 46303 static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, 46304 int argc, JSValueConst *argv, int padEnd) 46305 { 46306 JSValue str, v = JS_UNDEFINED; 46307 StringBuffer b_s, *b = &b_s; 46308 JSString *p, *p1 = NULL; 46309 int n, len, c = ' '; 46310 46311 str = JS_ToStringCheckObject(ctx, this_val); 46312 if (JS_IsException(str)) 46313 goto fail1; 46314 if (JS_ToInt32Sat(ctx, &n, argv[0])) 46315 goto fail2; 46316 p = JS_VALUE_GET_STRING(str); 46317 len = p->len; 46318 if (len >= n) 46319 return str; 46320 if (argc > 1 && !JS_IsUndefined(argv[1])) { 46321 v = JS_ToString(ctx, argv[1]); 46322 if (JS_IsException(v)) 46323 goto fail2; 46324 p1 = JS_VALUE_GET_STRING(v); 46325 if (p1->len == 0) { 46326 JS_FreeValue(ctx, v); 46327 return str; 46328 } 46329 if (p1->len == 1) { 46330 c = string_get(p1, 0); 46331 p1 = NULL; 46332 } 46333 } 46334 if (n > JS_STRING_LEN_MAX) { 46335 JS_ThrowRangeError(ctx, "invalid string length"); 46336 goto fail3; 46337 } 46338 if (string_buffer_init(ctx, b, n)) 46339 goto fail3; 46340 n -= len; 46341 if (padEnd) { 46342 if (string_buffer_concat(b, p, 0, len)) 46343 goto fail; 46344 } 46345 if (p1) { 46346 while (n > 0) { 46347 int chunk = min_int(n, p1->len); 46348 if (string_buffer_concat(b, p1, 0, chunk)) 46349 goto fail; 46350 n -= chunk; 46351 } 46352 } else { 46353 if (string_buffer_fill(b, c, n)) 46354 goto fail; 46355 } 46356 if (!padEnd) { 46357 if (string_buffer_concat(b, p, 0, len)) 46358 goto fail; 46359 } 46360 JS_FreeValue(ctx, v); 46361 JS_FreeValue(ctx, str); 46362 return string_buffer_end(b); 46363 46364 fail: 46365 string_buffer_free(b); 46366 fail3: 46367 JS_FreeValue(ctx, v); 46368 fail2: 46369 JS_FreeValue(ctx, str); 46370 fail1: 46371 return JS_EXCEPTION; 46372 } 46373 46374 static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, 46375 int argc, JSValueConst *argv) 46376 { 46377 JSValue str; 46378 StringBuffer b_s, *b = &b_s; 46379 JSString *p; 46380 int64_t val; 46381 int n, len; 46382 46383 str = JS_ToStringCheckObject(ctx, this_val); 46384 if (JS_IsException(str)) 46385 goto fail; 46386 if (JS_ToInt64Sat(ctx, &val, argv[0])) 46387 goto fail; 46388 if (val < 0 || val > 2147483647) { 46389 JS_ThrowRangeError(ctx, "invalid repeat count"); 46390 goto fail; 46391 } 46392 n = val; 46393 p = JS_VALUE_GET_STRING(str); 46394 len = p->len; 46395 if (len == 0 || n == 1) 46396 return str; 46397 // XXX: potential arithmetic overflow 46398 if (val * len > JS_STRING_LEN_MAX) { 46399 JS_ThrowRangeError(ctx, "invalid string length"); 46400 goto fail; 46401 } 46402 if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) 46403 goto fail; 46404 if (len == 1) { 46405 string_buffer_fill(b, string_get(p, 0), n); 46406 } else { 46407 while (n-- > 0) { 46408 string_buffer_concat(b, p, 0, len); 46409 } 46410 } 46411 JS_FreeValue(ctx, str); 46412 return string_buffer_end(b); 46413 46414 fail: 46415 JS_FreeValue(ctx, str); 46416 return JS_EXCEPTION; 46417 } 46418 46419 static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, 46420 int argc, JSValueConst *argv, int magic) 46421 { 46422 JSValue str, ret; 46423 int a, b, len; 46424 JSString *p; 46425 46426 str = JS_ToStringCheckObject(ctx, this_val); 46427 if (JS_IsException(str)) 46428 return str; 46429 p = JS_VALUE_GET_STRING(str); 46430 a = 0; 46431 b = len = p->len; 46432 if (magic & 1) { 46433 while (a < len && lre_is_space(string_get(p, a))) 46434 a++; 46435 } 46436 if (magic & 2) { 46437 while (b > a && lre_is_space(string_get(p, b - 1))) 46438 b--; 46439 } 46440 ret = js_sub_string(ctx, p, a, b); 46441 JS_FreeValue(ctx, str); 46442 return ret; 46443 } 46444 46445 /* return 0 if before the first char */ 46446 static int string_prevc(JSString *p, int *pidx) 46447 { 46448 int idx, c, c1; 46449 46450 idx = *pidx; 46451 if (idx <= 0) 46452 return 0; 46453 idx--; 46454 if (p->is_wide_char) { 46455 c = p->u.str16[idx]; 46456 if (is_lo_surrogate(c) && idx > 0) { 46457 c1 = p->u.str16[idx - 1]; 46458 if (is_hi_surrogate(c1)) { 46459 c = from_surrogate(c1, c); 46460 idx--; 46461 } 46462 } 46463 } else { 46464 c = p->u.str8[idx]; 46465 } 46466 *pidx = idx; 46467 return c; 46468 } 46469 46470 static BOOL test_final_sigma(JSString *p, int sigma_pos) 46471 { 46472 int k, c1; 46473 46474 /* before C: skip case ignorable chars and check there is 46475 a cased letter */ 46476 k = sigma_pos; 46477 for(;;) { 46478 c1 = string_prevc(p, &k); 46479 if (!lre_is_case_ignorable(c1)) 46480 break; 46481 } 46482 if (!lre_is_cased(c1)) 46483 return FALSE; 46484 46485 /* after C: skip case ignorable chars and check there is 46486 no cased letter */ 46487 k = sigma_pos + 1; 46488 for(;;) { 46489 if (k >= p->len) 46490 return TRUE; 46491 c1 = string_getc(p, &k); 46492 if (!lre_is_case_ignorable(c1)) 46493 break; 46494 } 46495 return !lre_is_cased(c1); 46496 } 46497 46498 static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, 46499 int argc, JSValueConst *argv, int to_lower) 46500 { 46501 JSValue val; 46502 StringBuffer b_s, *b = &b_s; 46503 JSString *p; 46504 int i, c, j, l; 46505 uint32_t res[LRE_CC_RES_LEN_MAX]; 46506 46507 val = JS_ToStringCheckObject(ctx, this_val); 46508 if (JS_IsException(val)) 46509 return val; 46510 p = JS_VALUE_GET_STRING(val); 46511 if (p->len == 0) 46512 return val; 46513 if (string_buffer_init(ctx, b, p->len)) 46514 goto fail; 46515 for(i = 0; i < p->len;) { 46516 c = string_getc(p, &i); 46517 if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { 46518 res[0] = 0x3c2; /* final sigma */ 46519 l = 1; 46520 } else { 46521 l = lre_case_conv(res, c, to_lower); 46522 } 46523 for(j = 0; j < l; j++) { 46524 if (string_buffer_putc(b, res[j])) 46525 goto fail; 46526 } 46527 } 46528 JS_FreeValue(ctx, val); 46529 return string_buffer_end(b); 46530 fail: 46531 JS_FreeValue(ctx, val); 46532 string_buffer_free(b); 46533 return JS_EXCEPTION; 46534 } 46535 46536 #ifdef CONFIG_ALL_UNICODE 46537 46538 /* return (-1, NULL) if exception, otherwise (len, buf) */ 46539 static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1) 46540 { 46541 JSValue val; 46542 JSString *p; 46543 uint32_t *buf; 46544 int i, j, len; 46545 46546 val = JS_ToString(ctx, val1); 46547 if (JS_IsException(val)) 46548 return -1; 46549 p = JS_VALUE_GET_STRING(val); 46550 len = p->len; 46551 /* UTF32 buffer length is len minus the number of correct surrogates pairs */ 46552 buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); 46553 if (!buf) { 46554 JS_FreeValue(ctx, val); 46555 goto fail; 46556 } 46557 for(i = j = 0; i < len;) 46558 buf[j++] = string_getc(p, &i); 46559 JS_FreeValue(ctx, val); 46560 *pbuf = buf; 46561 return j; 46562 fail: 46563 *pbuf = NULL; 46564 return -1; 46565 } 46566 46567 static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) 46568 { 46569 int i; 46570 StringBuffer b_s, *b = &b_s; 46571 if (string_buffer_init(ctx, b, len)) 46572 return JS_EXCEPTION; 46573 for(i = 0; i < len; i++) { 46574 if (string_buffer_putc(b, buf[i])) 46575 goto fail; 46576 } 46577 return string_buffer_end(b); 46578 fail: 46579 string_buffer_free(b); 46580 return JS_EXCEPTION; 46581 } 46582 46583 static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf, 46584 JSValueConst val, 46585 UnicodeNormalizationEnum n_type) 46586 { 46587 int buf_len, out_len; 46588 uint32_t *buf, *out_buf; 46589 46590 buf_len = JS_ToUTF32String(ctx, &buf, val); 46591 if (buf_len < 0) 46592 return -1; 46593 out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, 46594 ctx->rt, js_realloc_rt_opaque); 46595 js_free(ctx, buf); 46596 if (out_len < 0) 46597 return -1; 46598 *pout_buf = out_buf; 46599 return out_len; 46600 } 46601 46602 static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, 46603 int argc, JSValueConst *argv) 46604 { 46605 const char *form, *p; 46606 size_t form_len; 46607 int is_compat, out_len; 46608 UnicodeNormalizationEnum n_type; 46609 JSValue val; 46610 uint32_t *out_buf; 46611 46612 val = JS_ToStringCheckObject(ctx, this_val); 46613 if (JS_IsException(val)) 46614 return val; 46615 46616 if (argc == 0 || JS_IsUndefined(argv[0])) { 46617 n_type = UNICODE_NFC; 46618 } else { 46619 form = JS_ToCStringLen(ctx, &form_len, argv[0]); 46620 if (!form) 46621 goto fail1; 46622 p = form; 46623 if (p[0] != 'N' || p[1] != 'F') 46624 goto bad_form; 46625 p += 2; 46626 is_compat = FALSE; 46627 if (*p == 'K') { 46628 is_compat = TRUE; 46629 p++; 46630 } 46631 if (*p == 'C' || *p == 'D') { 46632 n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); 46633 if ((p + 1 - form) != form_len) 46634 goto bad_form; 46635 } else { 46636 bad_form: 46637 JS_FreeCString(ctx, form); 46638 JS_ThrowRangeError(ctx, "bad normalization form"); 46639 fail1: 46640 JS_FreeValue(ctx, val); 46641 return JS_EXCEPTION; 46642 } 46643 JS_FreeCString(ctx, form); 46644 } 46645 46646 out_len = js_string_normalize1(ctx, &out_buf, val, n_type); 46647 JS_FreeValue(ctx, val); 46648 if (out_len < 0) 46649 return JS_EXCEPTION; 46650 val = JS_NewUTF32String(ctx, out_buf, out_len); 46651 js_free(ctx, out_buf); 46652 return val; 46653 } 46654 46655 /* return < 0, 0 or > 0 */ 46656 static int js_UTF32_compare(const uint32_t *buf1, int buf1_len, 46657 const uint32_t *buf2, int buf2_len) 46658 { 46659 int i, len, c, res; 46660 len = min_int(buf1_len, buf2_len); 46661 for(i = 0; i < len; i++) { 46662 /* Note: range is limited so a subtraction is valid */ 46663 c = buf1[i] - buf2[i]; 46664 if (c != 0) 46665 return c; 46666 } 46667 if (buf1_len == buf2_len) 46668 res = 0; 46669 else if (buf1_len < buf2_len) 46670 res = -1; 46671 else 46672 res = 1; 46673 return res; 46674 } 46675 46676 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, 46677 int argc, JSValueConst *argv) 46678 { 46679 JSValue a, b; 46680 int cmp, a_len, b_len; 46681 uint32_t *a_buf, *b_buf; 46682 46683 a = JS_ToStringCheckObject(ctx, this_val); 46684 if (JS_IsException(a)) 46685 return JS_EXCEPTION; 46686 b = JS_ToString(ctx, argv[0]); 46687 if (JS_IsException(b)) { 46688 JS_FreeValue(ctx, a); 46689 return JS_EXCEPTION; 46690 } 46691 a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC); 46692 JS_FreeValue(ctx, a); 46693 if (a_len < 0) { 46694 JS_FreeValue(ctx, b); 46695 return JS_EXCEPTION; 46696 } 46697 46698 b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC); 46699 JS_FreeValue(ctx, b); 46700 if (b_len < 0) { 46701 js_free(ctx, a_buf); 46702 return JS_EXCEPTION; 46703 } 46704 cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len); 46705 js_free(ctx, a_buf); 46706 js_free(ctx, b_buf); 46707 return JS_NewInt32(ctx, cmp); 46708 } 46709 #else /* CONFIG_ALL_UNICODE */ 46710 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, 46711 int argc, JSValueConst *argv) 46712 { 46713 JSValue a, b; 46714 int cmp; 46715 46716 a = JS_ToStringCheckObject(ctx, this_val); 46717 if (JS_IsException(a)) 46718 return JS_EXCEPTION; 46719 b = JS_ToString(ctx, argv[0]); 46720 if (JS_IsException(b)) { 46721 JS_FreeValue(ctx, a); 46722 return JS_EXCEPTION; 46723 } 46724 cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); 46725 JS_FreeValue(ctx, a); 46726 JS_FreeValue(ctx, b); 46727 return JS_NewInt32(ctx, cmp); 46728 } 46729 #endif /* !CONFIG_ALL_UNICODE */ 46730 46731 /* also used for String.prototype.valueOf */ 46732 static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, 46733 int argc, JSValueConst *argv) 46734 { 46735 return js_thisStringValue(ctx, this_val); 46736 } 46737 46738 /* String Iterator */ 46739 46740 static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, 46741 int argc, JSValueConst *argv, 46742 BOOL *pdone, int magic) 46743 { 46744 JSArrayIteratorData *it; 46745 uint32_t idx, c, start; 46746 JSString *p; 46747 46748 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); 46749 if (!it) { 46750 *pdone = FALSE; 46751 return JS_EXCEPTION; 46752 } 46753 if (JS_IsUndefined(it->obj)) 46754 goto done; 46755 p = JS_VALUE_GET_STRING(it->obj); 46756 idx = it->idx; 46757 if (idx >= p->len) { 46758 JS_FreeValue(ctx, it->obj); 46759 it->obj = JS_UNDEFINED; 46760 done: 46761 *pdone = TRUE; 46762 return JS_UNDEFINED; 46763 } 46764 46765 start = idx; 46766 c = string_getc(p, (int *)&idx); 46767 it->idx = idx; 46768 *pdone = FALSE; 46769 if (c <= 0xffff) { 46770 return js_new_string_char(ctx, c); 46771 } else { 46772 return js_new_string16_len(ctx, p->u.str16 + start, 2); 46773 } 46774 } 46775 46776 /* ES6 Annex B 2.3.2 etc. */ 46777 enum { 46778 magic_string_anchor, 46779 magic_string_big, 46780 magic_string_blink, 46781 magic_string_bold, 46782 magic_string_fixed, 46783 magic_string_fontcolor, 46784 magic_string_fontsize, 46785 magic_string_italics, 46786 magic_string_link, 46787 magic_string_small, 46788 magic_string_strike, 46789 magic_string_sub, 46790 magic_string_sup, 46791 }; 46792 46793 static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, 46794 int argc, JSValueConst *argv, int magic) 46795 { 46796 JSValue str; 46797 const JSString *p; 46798 StringBuffer b_s, *b = &b_s; 46799 static struct { const char *tag, *attr; } const defs[] = { 46800 { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, 46801 { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, 46802 { "a", "href" }, { "small", NULL }, { "strike", NULL }, 46803 { "sub", NULL }, { "sup", NULL }, 46804 }; 46805 46806 str = JS_ToStringCheckObject(ctx, this_val); 46807 if (JS_IsException(str)) 46808 return JS_EXCEPTION; 46809 string_buffer_init(ctx, b, 7); 46810 string_buffer_putc8(b, '<'); 46811 string_buffer_puts8(b, defs[magic].tag); 46812 if (defs[magic].attr) { 46813 // r += " " + attr + "=\"" + value + "\""; 46814 JSValue value; 46815 int i; 46816 46817 string_buffer_putc8(b, ' '); 46818 string_buffer_puts8(b, defs[magic].attr); 46819 string_buffer_puts8(b, "=\""); 46820 value = JS_ToStringCheckObject(ctx, argv[0]); 46821 if (JS_IsException(value)) { 46822 JS_FreeValue(ctx, str); 46823 string_buffer_free(b); 46824 return JS_EXCEPTION; 46825 } 46826 p = JS_VALUE_GET_STRING(value); 46827 for (i = 0; i < p->len; i++) { 46828 int c = string_get(p, i); 46829 if (c == '"') { 46830 string_buffer_puts8(b, """); 46831 } else { 46832 string_buffer_putc16(b, c); 46833 } 46834 } 46835 JS_FreeValue(ctx, value); 46836 string_buffer_putc8(b, '\"'); 46837 } 46838 // return r + ">" + str + "</" + tag + ">"; 46839 string_buffer_putc8(b, '>'); 46840 string_buffer_concat_value_free(b, str); 46841 string_buffer_puts8(b, "</"); 46842 string_buffer_puts8(b, defs[magic].tag); 46843 string_buffer_putc8(b, '>'); 46844 return string_buffer_end(b); 46845 } 46846 46847 static const JSCFunctionListEntry js_string_funcs[] = { 46848 JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ), 46849 JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ), 46850 JS_CFUNC_DEF("raw", 1, js_string_raw ), 46851 }; 46852 46853 static const JSCFunctionListEntry js_string_proto_funcs[] = { 46854 JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), 46855 JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ), 46856 JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), 46857 JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ), 46858 JS_CFUNC_DEF("concat", 1, js_string_concat ), 46859 JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), 46860 JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ), 46861 JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ), 46862 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), 46863 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), 46864 JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), 46865 JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ), 46866 JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ), 46867 JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ), 46868 JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ), 46869 JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ), 46870 JS_CFUNC_DEF("split", 2, js_string_split ), 46871 JS_CFUNC_DEF("substring", 2, js_string_substring ), 46872 JS_CFUNC_DEF("substr", 2, js_string_substr ), 46873 JS_CFUNC_DEF("slice", 2, js_string_slice ), 46874 JS_CFUNC_DEF("repeat", 1, js_string_repeat ), 46875 JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), 46876 JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), 46877 JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ), 46878 JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ), 46879 JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), 46880 JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), 46881 JS_ALIAS_DEF("trimRight", "trimEnd" ), 46882 JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), 46883 JS_ALIAS_DEF("trimLeft", "trimStart" ), 46884 JS_CFUNC_DEF("toString", 0, js_string_toString ), 46885 JS_CFUNC_DEF("valueOf", 0, js_string_toString ), 46886 JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), 46887 JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), 46888 JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), 46889 JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ), 46890 JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ), 46891 /* ES6 Annex B 2.3.2 etc. */ 46892 JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ), 46893 JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ), 46894 JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ), 46895 JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ), 46896 JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ), 46897 JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ), 46898 JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ), 46899 JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ), 46900 JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ), 46901 JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ), 46902 JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ), 46903 JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ), 46904 JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ), 46905 }; 46906 46907 static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { 46908 JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ), 46909 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), 46910 }; 46911 46912 static const JSCFunctionListEntry js_string_proto_normalize[] = { 46913 #ifdef CONFIG_ALL_UNICODE 46914 JS_CFUNC_DEF("normalize", 0, js_string_normalize ), 46915 #endif 46916 JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), 46917 }; 46918 46919 int JS_AddIntrinsicStringNormalize(JSContext *ctx) 46920 { 46921 return JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize, 46922 countof(js_string_proto_normalize)); 46923 } 46924 46925 /* Math */ 46926 46927 /* precondition: a and b are not NaN */ 46928 static double js_fmin(double a, double b) 46929 { 46930 if (a == 0 && b == 0) { 46931 JSFloat64Union a1, b1; 46932 a1.d = a; 46933 b1.d = b; 46934 a1.u64 |= b1.u64; 46935 return a1.d; 46936 } else { 46937 return fmin(a, b); 46938 } 46939 } 46940 46941 /* precondition: a and b are not NaN */ 46942 static double js_fmax(double a, double b) 46943 { 46944 if (a == 0 && b == 0) { 46945 JSFloat64Union a1, b1; 46946 a1.d = a; 46947 b1.d = b; 46948 a1.u64 &= b1.u64; 46949 return a1.d; 46950 } else { 46951 return fmax(a, b); 46952 } 46953 } 46954 46955 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, 46956 int argc, JSValueConst *argv, int magic) 46957 { 46958 BOOL is_max = magic; 46959 double r, a; 46960 int i; 46961 uint32_t tag; 46962 46963 if (unlikely(argc == 0)) { 46964 return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); 46965 } 46966 46967 tag = JS_VALUE_GET_TAG(argv[0]); 46968 if (tag == JS_TAG_INT) { 46969 int a1, r1 = JS_VALUE_GET_INT(argv[0]); 46970 for(i = 1; i < argc; i++) { 46971 tag = JS_VALUE_GET_TAG(argv[i]); 46972 if (tag != JS_TAG_INT) { 46973 r = r1; 46974 goto generic_case; 46975 } 46976 a1 = JS_VALUE_GET_INT(argv[i]); 46977 if (is_max) 46978 r1 = max_int(r1, a1); 46979 else 46980 r1 = min_int(r1, a1); 46981 46982 } 46983 return JS_NewInt32(ctx, r1); 46984 } else { 46985 if (JS_ToFloat64(ctx, &r, argv[0])) 46986 return JS_EXCEPTION; 46987 i = 1; 46988 generic_case: 46989 while (i < argc) { 46990 if (JS_ToFloat64(ctx, &a, argv[i])) 46991 return JS_EXCEPTION; 46992 if (!isnan(r)) { 46993 if (isnan(a)) { 46994 r = a; 46995 } else { 46996 if (is_max) 46997 r = js_fmax(r, a); 46998 else 46999 r = js_fmin(r, a); 47000 } 47001 } 47002 i++; 47003 } 47004 return JS_NewFloat64(ctx, r); 47005 } 47006 } 47007 47008 static double js_math_sign(double a) 47009 { 47010 if (isnan(a) || a == 0.0) 47011 return a; 47012 if (a < 0) 47013 return -1; 47014 else 47015 return 1; 47016 } 47017 47018 static double js_math_round(double a) 47019 { 47020 JSFloat64Union u; 47021 uint64_t frac_mask, one; 47022 unsigned int e, s; 47023 47024 u.d = a; 47025 e = (u.u64 >> 52) & 0x7ff; 47026 if (e < 1023) { 47027 /* abs(a) < 1 */ 47028 if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { 47029 /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ 47030 u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); 47031 } else { 47032 /* return +/-0.0 */ 47033 u.u64 &= (uint64_t)1 << 63; 47034 } 47035 } else if (e < (1023 + 52)) { 47036 s = u.u64 >> 63; 47037 one = (uint64_t)1 << (52 - (e - 1023)); 47038 frac_mask = one - 1; 47039 u.u64 += (one >> 1) - s; 47040 u.u64 &= ~frac_mask; /* truncate to an integer */ 47041 } 47042 /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ 47043 return u.d; 47044 } 47045 47046 static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, 47047 int argc, JSValueConst *argv) 47048 { 47049 double r, a; 47050 int i; 47051 47052 r = 0; 47053 if (argc > 0) { 47054 if (JS_ToFloat64(ctx, &r, argv[0])) 47055 return JS_EXCEPTION; 47056 if (argc == 1) { 47057 r = fabs(r); 47058 } else { 47059 /* use the built-in function to minimize precision loss */ 47060 for (i = 1; i < argc; i++) { 47061 if (JS_ToFloat64(ctx, &a, argv[i])) 47062 return JS_EXCEPTION; 47063 r = hypot(r, a); 47064 } 47065 } 47066 } 47067 return JS_NewFloat64(ctx, r); 47068 } 47069 47070 static double js_math_f16round(double a) 47071 { 47072 return fromfp16(tofp16(a)); 47073 } 47074 47075 static double js_math_fround(double a) 47076 { 47077 return (float)a; 47078 } 47079 47080 static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, 47081 int argc, JSValueConst *argv) 47082 { 47083 uint32_t a, b, c; 47084 int32_t d; 47085 47086 if (JS_ToUint32(ctx, &a, argv[0])) 47087 return JS_EXCEPTION; 47088 if (JS_ToUint32(ctx, &b, argv[1])) 47089 return JS_EXCEPTION; 47090 c = a * b; 47091 memcpy(&d, &c, sizeof(d)); 47092 return JS_NewInt32(ctx, d); 47093 } 47094 47095 static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, 47096 int argc, JSValueConst *argv) 47097 { 47098 uint32_t a, r; 47099 47100 if (JS_ToUint32(ctx, &a, argv[0])) 47101 return JS_EXCEPTION; 47102 if (a == 0) 47103 r = 32; 47104 else 47105 r = clz32(a); 47106 return JS_NewInt32(ctx, r); 47107 } 47108 47109 typedef enum { 47110 SUM_PRECISE_STATE_FINITE, 47111 SUM_PRECISE_STATE_INFINITY, 47112 SUM_PRECISE_STATE_MINUS_INFINITY, /* must be after SUM_PRECISE_STATE_INFINITY */ 47113 SUM_PRECISE_STATE_NAN, /* must be after SUM_PRECISE_STATE_MINUS_INFINITY */ 47114 } SumPreciseStateEnum; 47115 47116 #define SP_LIMB_BITS 56 47117 #define SP_RND_BITS (SP_LIMB_BITS - 53) 47118 /* we add one extra limb to avoid having to test for overflows during the sum */ 47119 #define SUM_PRECISE_ACC_LEN 39 47120 47121 #define SUM_PRECISE_COUNTER_INIT 250 47122 47123 typedef struct { 47124 SumPreciseStateEnum state; 47125 uint32_t counter; 47126 int n_limbs; /* 'acc' contains n_limbs and is not necessarily 47127 acc[n_limb - 1] may be 0. 0 indicates minus zero 47128 result when state = SUM_PRECISE_STATE_FINITE */ 47129 int64_t acc[SUM_PRECISE_ACC_LEN]; 47130 } SumPreciseState; 47131 47132 static void sum_precise_init(SumPreciseState *s) 47133 { 47134 memset(s->acc, 0, sizeof(s->acc)); 47135 s->state = SUM_PRECISE_STATE_FINITE; 47136 s->counter = SUM_PRECISE_COUNTER_INIT; 47137 s->n_limbs = 0; 47138 } 47139 47140 static void sum_precise_renorm(SumPreciseState *s) 47141 { 47142 int64_t v, carry; 47143 int i; 47144 47145 carry = 0; 47146 for(i = 0; i < s->n_limbs; i++) { 47147 v = s->acc[i] + carry; 47148 s->acc[i] = v & (((uint64_t)1 << SP_LIMB_BITS) - 1); 47149 carry = v >> SP_LIMB_BITS; 47150 } 47151 /* we add a failsafe but it should be never reached in a 47152 reasonnable amount of time */ 47153 if (carry != 0 && s->n_limbs < SUM_PRECISE_ACC_LEN) 47154 s->acc[s->n_limbs++] = carry; 47155 } 47156 47157 static void sum_precise_add(SumPreciseState *s, double d) 47158 { 47159 uint64_t a, m, a0, a1; 47160 int sgn, e, p; 47161 unsigned int shift; 47162 47163 a = float64_as_uint64(d); 47164 sgn = a >> 63; 47165 e = (a >> 52) & ((1 << 11) - 1); 47166 m = a & (((uint64_t)1 << 52) - 1); 47167 if (unlikely(e == 2047)) { 47168 if (m == 0) { 47169 /* +/- infinity */ 47170 if (s->state == SUM_PRECISE_STATE_NAN || 47171 (s->state == SUM_PRECISE_STATE_MINUS_INFINITY && !sgn) || 47172 (s->state == SUM_PRECISE_STATE_INFINITY && sgn)) { 47173 s->state = SUM_PRECISE_STATE_NAN; 47174 } else { 47175 s->state = SUM_PRECISE_STATE_INFINITY + sgn; 47176 } 47177 } else { 47178 /* NaN */ 47179 s->state = SUM_PRECISE_STATE_NAN; 47180 } 47181 } else if (e == 0) { 47182 if (likely(m == 0)) { 47183 /* zero */ 47184 if (s->n_limbs == 0 && !sgn) 47185 s->n_limbs = 1; 47186 } else { 47187 /* subnormal */ 47188 p = 0; 47189 shift = 0; 47190 goto add; 47191 } 47192 } else { 47193 /* Note: we sum even if state != SUM_PRECISE_STATE_FINITE to 47194 avoid tests */ 47195 m |= (uint64_t)1 << 52; 47196 shift = e - 1; 47197 /* 'p' is the position of a0 in acc. The division is normally 47198 implementation as a multiplication by the compiler. */ 47199 p = shift / SP_LIMB_BITS; 47200 shift %= SP_LIMB_BITS; 47201 add: 47202 a0 = (m << shift) & (((uint64_t)1 << SP_LIMB_BITS) - 1); 47203 a1 = m >> (SP_LIMB_BITS - shift); 47204 if (!sgn) { 47205 s->acc[p] += a0; 47206 s->acc[p + 1] += a1; 47207 } else { 47208 s->acc[p] -= a0; 47209 s->acc[p + 1] -= a1; 47210 } 47211 s->n_limbs = max_int(s->n_limbs, p + 2); 47212 47213 if (unlikely(--s->counter == 0)) { 47214 s->counter = SUM_PRECISE_COUNTER_INIT; 47215 sum_precise_renorm(s); 47216 } 47217 } 47218 } 47219 47220 static double sum_precise_get_result(SumPreciseState *s) 47221 { 47222 int n, shift, e, p, is_neg; 47223 uint64_t m, addend; 47224 47225 if (s->state != SUM_PRECISE_STATE_FINITE) { 47226 switch(s->state) { 47227 default: 47228 case SUM_PRECISE_STATE_INFINITY: 47229 return INFINITY; 47230 case SUM_PRECISE_STATE_MINUS_INFINITY: 47231 return -INFINITY; 47232 case SUM_PRECISE_STATE_NAN: 47233 return NAN; 47234 } 47235 } 47236 47237 sum_precise_renorm(s); 47238 47239 /* extract the sign and absolute value */ 47240 #if 0 47241 { 47242 int i; 47243 printf("len=%d:", s->n_limbs); 47244 for(i = s->n_limbs - 1; i >= 0; i--) 47245 printf(" %014lx", s->acc[i]); 47246 printf("\n"); 47247 } 47248 #endif 47249 n = s->n_limbs; 47250 /* minus zero result */ 47251 if (n == 0) 47252 return -0.0; 47253 47254 /* normalize */ 47255 while (n > 0 && s->acc[n - 1] == 0) 47256 n--; 47257 /* zero result. The spec tells it is always positive in the finite case */ 47258 if (n == 0) 47259 return 0.0; 47260 is_neg = (s->acc[n - 1] < 0); 47261 if (is_neg) { 47262 uint64_t v, carry; 47263 int i; 47264 /* negate */ 47265 /* XXX: do it only when needed */ 47266 carry = 1; 47267 for(i = 0; i < n - 1; i++) { 47268 v = (((uint64_t)1 << SP_LIMB_BITS) - 1) - s->acc[i] + carry; 47269 carry = v >> SP_LIMB_BITS; 47270 s->acc[i] = v & (((uint64_t)1 << SP_LIMB_BITS) - 1); 47271 } 47272 s->acc[n - 1] = -s->acc[n - 1] + carry - 1; 47273 while (n > 1 && s->acc[n - 1] == 0) 47274 n--; 47275 } 47276 /* subnormal case */ 47277 if (n == 1 && s->acc[0] < ((uint64_t)1 << 52)) 47278 return uint64_as_float64(((uint64_t)is_neg << 63) | s->acc[0]); 47279 /* normal case */ 47280 e = n * SP_LIMB_BITS; 47281 p = n - 1; 47282 m = s->acc[p]; 47283 shift = clz64(m) - (64 - SP_LIMB_BITS); 47284 e = e - shift - 52; 47285 if (shift != 0) { 47286 m <<= shift; 47287 if (p > 0) { 47288 int shift1; 47289 uint64_t nz; 47290 p--; 47291 shift1 = SP_LIMB_BITS - shift; 47292 nz = s->acc[p] & (((uint64_t)1 << shift1) - 1); 47293 m = m | (s->acc[p] >> shift1) | (nz != 0); 47294 } 47295 } 47296 if ((m & ((1 << SP_RND_BITS) - 1)) == (1 << (SP_RND_BITS - 1))) { 47297 /* see if the LSB part is non zero for the final rounding */ 47298 while (p > 0) { 47299 p--; 47300 if (s->acc[p] != 0) { 47301 m |= 1; 47302 break; 47303 } 47304 } 47305 } 47306 /* rounding to nearest with ties to even */ 47307 addend = (1 << (SP_RND_BITS - 1)) - 1 + ((m >> SP_RND_BITS) & 1); 47308 m = (m + addend) >> SP_RND_BITS; 47309 /* handle overflow in the rounding */ 47310 if (m == ((uint64_t)1 << 53)) 47311 e++; 47312 if (unlikely(e >= 2047)) { 47313 /* infinity */ 47314 return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)2047 << 52)); 47315 } else { 47316 m &= (((uint64_t)1 << 52) - 1); 47317 return uint64_as_float64(((uint64_t)is_neg << 63) | ((uint64_t)e << 52) | m); 47318 } 47319 } 47320 47321 static JSValue js_math_sumPrecise(JSContext *ctx, JSValueConst this_val, 47322 int argc, JSValueConst *argv) 47323 { 47324 JSValue iter, next, item, ret; 47325 uint32_t tag; 47326 int done; 47327 double d; 47328 SumPreciseState s_s, *s = &s_s; 47329 47330 iter = JS_GetIterator(ctx, argv[0], FALSE); 47331 if (JS_IsException(iter)) 47332 return JS_EXCEPTION; 47333 ret = JS_EXCEPTION; 47334 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 47335 if (JS_IsException(next)) 47336 goto fail; 47337 sum_precise_init(s); 47338 for (;;) { 47339 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 47340 if (JS_IsException(item)) 47341 goto fail; 47342 if (done) 47343 break; 47344 tag = JS_VALUE_GET_TAG(item); 47345 if (JS_TAG_IS_FLOAT64(tag)) { 47346 d = JS_VALUE_GET_FLOAT64(item); 47347 } else if (tag == JS_TAG_INT) { 47348 d = JS_VALUE_GET_INT(item); 47349 } else { 47350 JS_FreeValue(ctx, item); 47351 JS_ThrowTypeError(ctx, "not a number"); 47352 JS_IteratorClose(ctx, iter, TRUE); 47353 goto fail; 47354 } 47355 sum_precise_add(s, d); 47356 } 47357 ret = __JS_NewFloat64(ctx, sum_precise_get_result(s)); 47358 fail: 47359 JS_FreeValue(ctx, iter); 47360 JS_FreeValue(ctx, next); 47361 return ret; 47362 } 47363 47364 /* xorshift* random number generator by Marsaglia */ 47365 static uint64_t xorshift64star(uint64_t *pstate) 47366 { 47367 uint64_t x; 47368 x = *pstate; 47369 x ^= x >> 12; 47370 x ^= x << 25; 47371 x ^= x >> 27; 47372 *pstate = x; 47373 return x * 0x2545F4914F6CDD1D; 47374 } 47375 47376 static void js_random_init(JSContext *ctx) 47377 { 47378 struct timeval tv; 47379 gettimeofday(&tv, NULL); 47380 ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; 47381 /* the state must be non zero */ 47382 if (ctx->random_state == 0) 47383 ctx->random_state = 1; 47384 } 47385 47386 static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, 47387 int argc, JSValueConst *argv) 47388 { 47389 JSFloat64Union u; 47390 uint64_t v; 47391 47392 v = xorshift64star(&ctx->random_state); 47393 /* 1.0 <= u.d < 2 */ 47394 u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); 47395 return __JS_NewFloat64(ctx, u.d - 1.0); 47396 } 47397 47398 static const JSCFunctionListEntry js_math_funcs[] = { 47399 JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), 47400 JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), 47401 JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), 47402 JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), 47403 JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), 47404 JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), 47405 JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), 47406 47407 JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), 47408 JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), 47409 JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), 47410 JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), 47411 JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), 47412 JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), 47413 JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), 47414 JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), 47415 JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), 47416 JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), 47417 /* ES6 */ 47418 JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), 47419 JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), 47420 JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), 47421 JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), 47422 JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), 47423 JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), 47424 JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), 47425 JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), 47426 JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), 47427 JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), 47428 JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), 47429 JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), 47430 JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), 47431 JS_CFUNC_DEF("hypot", 2, js_math_hypot ), 47432 JS_CFUNC_DEF("random", 0, js_math_random ), 47433 JS_CFUNC_SPECIAL_DEF("f16round", 1, f_f, js_math_f16round ), 47434 JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), 47435 JS_CFUNC_DEF("imul", 2, js_math_imul ), 47436 JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), 47437 JS_CFUNC_DEF("sumPrecise", 1, js_math_sumPrecise ), 47438 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), 47439 JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), 47440 JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), 47441 JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), 47442 JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), 47443 JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), 47444 JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), 47445 JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), 47446 JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), 47447 }; 47448 47449 static const JSCFunctionListEntry js_math_obj[] = { 47450 JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 47451 }; 47452 47453 /* Date */ 47454 47455 /* OS dependent. d = argv[0] is in ms from 1970. Return the difference 47456 between UTC time and local time 'd' in minutes */ 47457 static int getTimezoneOffset(int64_t time) 47458 { 47459 time_t ti; 47460 int res; 47461 47462 time /= 1000; /* convert to seconds */ 47463 if (sizeof(time_t) == 4) { 47464 /* on 32-bit systems, we need to clamp the time value to the 47465 range of `time_t`. This is better than truncating values to 47466 32 bits and hopefully provides the same result as 64-bit 47467 implementation of localtime_r. 47468 */ 47469 if ((time_t)-1 < 0) { 47470 if (time < INT32_MIN) { 47471 time = INT32_MIN; 47472 } else if (time > INT32_MAX) { 47473 time = INT32_MAX; 47474 } 47475 } else { 47476 if (time < 0) { 47477 time = 0; 47478 } else if (time > UINT32_MAX) { 47479 time = UINT32_MAX; 47480 } 47481 } 47482 } 47483 ti = time; 47484 #if defined(_WIN32) 47485 { 47486 struct tm *tm; 47487 time_t gm_ti, loc_ti; 47488 47489 tm = gmtime(&ti); 47490 if (!tm) 47491 return 0; 47492 gm_ti = mktime(tm); 47493 47494 tm = localtime(&ti); 47495 if (!tm) 47496 return 0; 47497 loc_ti = mktime(tm); 47498 47499 res = (gm_ti - loc_ti) / 60; 47500 } 47501 #else 47502 { 47503 struct tm tm; 47504 localtime_r(&ti, &tm); 47505 res = -tm.tm_gmtoff / 60; 47506 } 47507 #endif 47508 return res; 47509 } 47510 47511 #if 0 47512 static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, 47513 int argc, JSValueConst *argv) 47514 { 47515 double dd; 47516 47517 if (JS_ToFloat64(ctx, &dd, argv[0])) 47518 return JS_EXCEPTION; 47519 if (isnan(dd)) 47520 return __JS_NewFloat64(ctx, dd); 47521 else 47522 return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); 47523 } 47524 47525 static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, 47526 JSValueConst def_proto) 47527 { 47528 JSValue proto; 47529 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); 47530 if (JS_IsException(proto)) 47531 return proto; 47532 if (!JS_IsObject(proto)) { 47533 JS_FreeValue(ctx, proto); 47534 proto = JS_DupValue(ctx, def_proto); 47535 } 47536 return proto; 47537 } 47538 47539 /* create a new date object */ 47540 static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, 47541 int argc, JSValueConst *argv) 47542 { 47543 JSValue obj, proto; 47544 proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); 47545 if (JS_IsException(proto)) 47546 return proto; 47547 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); 47548 JS_FreeValue(ctx, proto); 47549 if (!JS_IsException(obj)) 47550 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); 47551 return obj; 47552 } 47553 #endif 47554 47555 /* RegExp */ 47556 47557 static void js_regexp_finalizer(JSRuntime *rt, JSValue val) 47558 { 47559 JSObject *p = JS_VALUE_GET_OBJ(val); 47560 JSRegExp *re = &p->u.regexp; 47561 if (re->bytecode != NULL) 47562 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 47563 if (re->pattern != NULL) 47564 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); 47565 } 47566 47567 /* create a string containing the RegExp bytecode */ 47568 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, 47569 JSValueConst flags) 47570 { 47571 const char *str; 47572 int re_flags, mask; 47573 uint8_t *re_bytecode_buf; 47574 size_t i, len; 47575 int re_bytecode_len; 47576 JSValue ret; 47577 char error_msg[64]; 47578 47579 re_flags = 0; 47580 if (!JS_IsUndefined(flags)) { 47581 str = JS_ToCStringLen(ctx, &len, flags); 47582 if (!str) 47583 return JS_EXCEPTION; 47584 /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ 47585 for (i = 0; i < len; i++) { 47586 switch(str[i]) { 47587 case 'd': 47588 mask = LRE_FLAG_INDICES; 47589 break; 47590 case 'g': 47591 mask = LRE_FLAG_GLOBAL; 47592 break; 47593 case 'i': 47594 mask = LRE_FLAG_IGNORECASE; 47595 break; 47596 case 'm': 47597 mask = LRE_FLAG_MULTILINE; 47598 break; 47599 case 's': 47600 mask = LRE_FLAG_DOTALL; 47601 break; 47602 case 'u': 47603 mask = LRE_FLAG_UNICODE; 47604 break; 47605 case 'v': 47606 mask = LRE_FLAG_UNICODE_SETS; 47607 break; 47608 case 'y': 47609 mask = LRE_FLAG_STICKY; 47610 break; 47611 default: 47612 goto bad_flags; 47613 } 47614 if ((re_flags & mask) != 0) { 47615 bad_flags: 47616 JS_FreeCString(ctx, str); 47617 goto bad_flags1; 47618 } 47619 re_flags |= mask; 47620 } 47621 JS_FreeCString(ctx, str); 47622 } 47623 47624 /* 'u' and 'v' cannot be both set */ 47625 if ((re_flags & LRE_FLAG_UNICODE_SETS) && (re_flags & LRE_FLAG_UNICODE)) { 47626 bad_flags1: 47627 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); 47628 } 47629 47630 str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS))); 47631 if (!str) 47632 return JS_EXCEPTION; 47633 re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, 47634 sizeof(error_msg), str, len, re_flags, ctx); 47635 JS_FreeCString(ctx, str); 47636 if (!re_bytecode_buf) { 47637 JS_ThrowSyntaxError(ctx, "%s", error_msg); 47638 return JS_EXCEPTION; 47639 } 47640 47641 ret = js_new_string8_len(ctx, (const char *)re_bytecode_buf, re_bytecode_len); 47642 js_free(ctx, re_bytecode_buf); 47643 return ret; 47644 } 47645 47646 /* fast regexp creation */ 47647 static JSValue JS_NewRegexp(JSContext *ctx, JSValue pattern, JSValue bc) 47648 { 47649 JSValue obj; 47650 JSProperty props[1]; 47651 JSObject *p; 47652 JSRegExp *re; 47653 47654 /* sanity check */ 47655 if (unlikely(JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || 47656 JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING)) { 47657 JS_ThrowTypeError(ctx, "string expected"); 47658 goto fail; 47659 } 47660 props[0].u.value = JS_NewInt32(ctx, 0); /* lastIndex */ 47661 obj = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->regexp_shape), JS_CLASS_REGEXP, props); 47662 if (JS_IsException(obj)) 47663 goto fail; 47664 p = JS_VALUE_GET_OBJ(obj); 47665 re = &p->u.regexp; 47666 re->pattern = JS_VALUE_GET_STRING(pattern); 47667 re->bytecode = JS_VALUE_GET_STRING(bc); 47668 return obj; 47669 fail: 47670 JS_FreeValue(ctx, bc); 47671 JS_FreeValue(ctx, pattern); 47672 return JS_EXCEPTION; 47673 } 47674 47675 /* set the RegExp fields */ 47676 static JSValue js_regexp_set_internal(JSContext *ctx, 47677 JSValue obj, 47678 JSValue pattern, JSValue bc) 47679 { 47680 JSObject *p; 47681 JSRegExp *re; 47682 47683 /* sanity check */ 47684 if (unlikely(JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || 47685 JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING)) { 47686 JS_ThrowTypeError(ctx, "string expected"); 47687 JS_FreeValue(ctx, obj); 47688 JS_FreeValue(ctx, bc); 47689 JS_FreeValue(ctx, pattern); 47690 return JS_EXCEPTION; 47691 } 47692 47693 p = JS_VALUE_GET_OBJ(obj); 47694 re = &p->u.regexp; 47695 re->pattern = JS_VALUE_GET_STRING(pattern); 47696 re->bytecode = JS_VALUE_GET_STRING(bc); 47697 /* Note: cannot fail because the field is preallocated */ 47698 JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), 47699 JS_PROP_WRITABLE); 47700 return obj; 47701 } 47702 47703 static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) 47704 { 47705 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 47706 JSObject *p = JS_VALUE_GET_OBJ(obj); 47707 if (p->class_id == JS_CLASS_REGEXP) 47708 return &p->u.regexp; 47709 } 47710 if (throw_error) { 47711 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); 47712 } 47713 return NULL; 47714 } 47715 47716 /* return < 0 if exception or TRUE/FALSE */ 47717 static int js_is_regexp(JSContext *ctx, JSValueConst obj) 47718 { 47719 JSValue m; 47720 47721 if (!JS_IsObject(obj)) 47722 return FALSE; 47723 m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); 47724 if (JS_IsException(m)) 47725 return -1; 47726 if (!JS_IsUndefined(m)) 47727 return JS_ToBoolFree(ctx, m); 47728 return js_get_regexp(ctx, obj, FALSE) != NULL; 47729 } 47730 47731 static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, 47732 int argc, JSValueConst *argv) 47733 { 47734 JSValue pattern, flags, bc, val, obj = JS_UNDEFINED; 47735 JSValueConst pat, flags1; 47736 JSRegExp *re; 47737 int pat_is_regexp; 47738 47739 pat = argv[0]; 47740 flags1 = argv[1]; 47741 pat_is_regexp = js_is_regexp(ctx, pat); 47742 if (pat_is_regexp < 0) 47743 return JS_EXCEPTION; 47744 if (JS_IsUndefined(new_target)) { 47745 /* called as a function */ 47746 new_target = JS_GetActiveFunction(ctx); 47747 if (pat_is_regexp && JS_IsUndefined(flags1)) { 47748 JSValue ctor; 47749 BOOL res; 47750 ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); 47751 if (JS_IsException(ctor)) 47752 return ctor; 47753 res = js_same_value(ctx, ctor, new_target); 47754 JS_FreeValue(ctx, ctor); 47755 if (res) 47756 return JS_DupValue(ctx, pat); 47757 } 47758 } 47759 re = js_get_regexp(ctx, pat, FALSE); 47760 flags = JS_UNDEFINED; 47761 if (re) { 47762 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); 47763 if (JS_IsUndefined(flags1)) { 47764 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 47765 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP); 47766 if (JS_IsException(obj)) 47767 goto fail; 47768 goto no_compilation; 47769 } else { 47770 flags = JS_DupValue(ctx, flags1); 47771 } 47772 } else { 47773 if (pat_is_regexp) { 47774 pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); 47775 if (JS_IsException(pattern)) 47776 goto fail; 47777 if (JS_IsUndefined(flags1)) { 47778 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); 47779 if (JS_IsException(flags)) 47780 goto fail; 47781 } else { 47782 flags = JS_DupValue(ctx, flags1); 47783 } 47784 } else { 47785 pattern = JS_DupValue(ctx, pat); 47786 flags = JS_DupValue(ctx, flags1); 47787 } 47788 if (JS_IsUndefined(pattern)) { 47789 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); 47790 } else { 47791 val = pattern; 47792 pattern = JS_ToString(ctx, val); 47793 JS_FreeValue(ctx, val); 47794 if (JS_IsException(pattern)) 47795 goto fail; 47796 } 47797 } 47798 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_REGEXP); 47799 if (JS_IsException(obj)) 47800 goto fail; 47801 bc = js_compile_regexp(ctx, pattern, flags); 47802 if (JS_IsException(bc)) 47803 goto fail; 47804 JS_FreeValue(ctx, flags); 47805 no_compilation: 47806 return js_regexp_set_internal(ctx, obj, pattern, bc); 47807 fail: 47808 JS_FreeValue(ctx, pattern); 47809 JS_FreeValue(ctx, flags); 47810 JS_FreeValue(ctx, obj); 47811 return JS_EXCEPTION; 47812 } 47813 47814 static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, 47815 int argc, JSValueConst *argv) 47816 { 47817 JSRegExp *re1, *re; 47818 JSValueConst pattern1, flags1; 47819 JSValue bc, pattern; 47820 47821 re = js_get_regexp(ctx, this_val, TRUE); 47822 if (!re) 47823 return JS_EXCEPTION; 47824 pattern1 = argv[0]; 47825 flags1 = argv[1]; 47826 re1 = js_get_regexp(ctx, pattern1, FALSE); 47827 if (re1) { 47828 if (!JS_IsUndefined(flags1)) 47829 return JS_ThrowTypeError(ctx, "flags must be undefined"); 47830 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); 47831 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); 47832 } else { 47833 bc = JS_UNDEFINED; 47834 if (JS_IsUndefined(pattern1)) 47835 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); 47836 else 47837 pattern = JS_ToString(ctx, pattern1); 47838 if (JS_IsException(pattern)) 47839 goto fail; 47840 bc = js_compile_regexp(ctx, pattern, flags1); 47841 if (JS_IsException(bc)) 47842 goto fail; 47843 } 47844 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); 47845 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 47846 re->pattern = JS_VALUE_GET_STRING(pattern); 47847 re->bytecode = JS_VALUE_GET_STRING(bc); 47848 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 47849 JS_NewInt32(ctx, 0)) < 0) 47850 return JS_EXCEPTION; 47851 return JS_DupValue(ctx, this_val); 47852 fail: 47853 JS_FreeValue(ctx, pattern); 47854 JS_FreeValue(ctx, bc); 47855 return JS_EXCEPTION; 47856 } 47857 47858 static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) 47859 { 47860 JSRegExp *re; 47861 JSString *p; 47862 StringBuffer b_s, *b = &b_s; 47863 int i, n, c, c2, bra; 47864 47865 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 47866 return JS_ThrowTypeErrorNotAnObject(ctx); 47867 47868 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) 47869 goto empty_regex; 47870 47871 re = js_get_regexp(ctx, this_val, TRUE); 47872 if (!re) 47873 return JS_EXCEPTION; 47874 47875 p = re->pattern; 47876 47877 if (p->len == 0) { 47878 empty_regex: 47879 return js_new_string8(ctx, "(?:)"); 47880 } 47881 string_buffer_init2(ctx, b, p->len, p->is_wide_char); 47882 47883 /* Escape '/' and newline sequences as needed */ 47884 bra = 0; 47885 for (i = 0, n = p->len; i < n;) { 47886 c2 = -1; 47887 switch (c = string_get(p, i++)) { 47888 case '\\': 47889 if (i < n) 47890 c2 = string_get(p, i++); 47891 break; 47892 case ']': 47893 bra = 0; 47894 break; 47895 case '[': 47896 if (!bra) { 47897 if (i < n && string_get(p, i) == ']') 47898 c2 = string_get(p, i++); 47899 bra = 1; 47900 } 47901 break; 47902 case '\n': 47903 c = '\\'; 47904 c2 = 'n'; 47905 break; 47906 case '\r': 47907 c = '\\'; 47908 c2 = 'r'; 47909 break; 47910 case '/': 47911 if (!bra) { 47912 c = '\\'; 47913 c2 = '/'; 47914 } 47915 break; 47916 } 47917 string_buffer_putc16(b, c); 47918 if (c2 >= 0) 47919 string_buffer_putc16(b, c2); 47920 } 47921 return string_buffer_end(b); 47922 } 47923 47924 static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) 47925 { 47926 JSRegExp *re; 47927 int flags; 47928 47929 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 47930 return JS_ThrowTypeErrorNotAnObject(ctx); 47931 47932 re = js_get_regexp(ctx, this_val, FALSE); 47933 if (!re) { 47934 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) 47935 return JS_UNDEFINED; 47936 else 47937 return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); 47938 } 47939 47940 flags = lre_get_flags(re->bytecode->u.str8); 47941 return JS_NewBool(ctx, flags & mask); 47942 } 47943 47944 #define RE_FLAG_COUNT 8 47945 47946 static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) 47947 { 47948 char str[RE_FLAG_COUNT], *p = str; 47949 int res, i; 47950 static const int flag_atom[RE_FLAG_COUNT] = { 47951 JS_ATOM_hasIndices, 47952 JS_ATOM_global, 47953 JS_ATOM_ignoreCase, 47954 JS_ATOM_multiline, 47955 JS_ATOM_dotAll, 47956 JS_ATOM_unicode, 47957 JS_ATOM_unicodeSets, 47958 JS_ATOM_sticky, 47959 }; 47960 static const char flag_char[RE_FLAG_COUNT] = { 'd', 'g', 'i', 'm', 's', 'u', 'v', 'y' }; 47961 47962 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 47963 return JS_ThrowTypeErrorNotAnObject(ctx); 47964 47965 for(i = 0; i < RE_FLAG_COUNT; i++) { 47966 res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, flag_atom[i])); 47967 if (res < 0) 47968 goto exception; 47969 if (res) 47970 *p++ = flag_char[i]; 47971 } 47972 return JS_NewStringLen(ctx, str, p - str); 47973 47974 exception: 47975 return JS_EXCEPTION; 47976 } 47977 47978 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, 47979 int argc, JSValueConst *argv) 47980 { 47981 JSValue pattern, flags; 47982 StringBuffer b_s, *b = &b_s; 47983 47984 if (!JS_IsObject(this_val)) 47985 return JS_ThrowTypeErrorNotAnObject(ctx); 47986 47987 string_buffer_init(ctx, b, 0); 47988 string_buffer_putc8(b, '/'); 47989 pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); 47990 if (string_buffer_concat_value_free(b, pattern)) 47991 goto fail; 47992 string_buffer_putc8(b, '/'); 47993 flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); 47994 if (string_buffer_concat_value_free(b, flags)) 47995 goto fail; 47996 return string_buffer_end(b); 47997 47998 fail: 47999 string_buffer_free(b); 48000 return JS_EXCEPTION; 48001 } 48002 48003 int lre_check_stack_overflow(void *opaque, size_t alloca_size) 48004 { 48005 JSContext *ctx = opaque; 48006 return js_check_stack_overflow(ctx->rt, alloca_size); 48007 } 48008 48009 int lre_check_timeout(void *opaque) 48010 { 48011 JSContext *ctx = opaque; 48012 JSRuntime *rt = ctx->rt; 48013 return (rt->interrupt_handler && 48014 rt->interrupt_handler(rt, rt->interrupt_opaque)); 48015 } 48016 48017 void *lre_realloc(void *opaque, void *ptr, size_t size) 48018 { 48019 JSContext *ctx = opaque; 48020 /* No JS exception is raised here */ 48021 return js_realloc_rt(ctx->rt, ptr, size); 48022 } 48023 48024 static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val, 48025 int argc, JSValueConst *argv) 48026 { 48027 JSValue str; 48028 StringBuffer b_s, *b = &b_s; 48029 JSString *p; 48030 uint32_t c; 48031 char s[16]; 48032 int i, i0; 48033 48034 if (!JS_IsString(argv[0])) 48035 return JS_ThrowTypeError(ctx, "not a string"); 48036 str = JS_ToString(ctx, argv[0]); /* must call it to linearlize ropes */ 48037 if (JS_IsException(str)) 48038 return JS_EXCEPTION; 48039 p = JS_VALUE_GET_STRING(str); 48040 string_buffer_init2(ctx, b, 0, p->is_wide_char); 48041 for (i = 0; i < p->len; ) { 48042 i0 = i; 48043 c = string_getc(p, &i); 48044 if (c < 33) { 48045 if (c >= 9 && c <= 13) { 48046 string_buffer_putc8(b, '\\'); 48047 string_buffer_putc8(b, "tnvfr"[c - 9]); 48048 } else { 48049 goto hex2; 48050 } 48051 } else if (c < 128) { 48052 if ((c >= '0' && c <= '9') 48053 || (c >= 'A' && c <= 'Z') 48054 || (c >= 'a' && c <= 'z')) { 48055 if (i0 == 0) 48056 goto hex2; 48057 } else if (strchr(",-=<>#&!%:;@~'`\"", c)) { 48058 goto hex2; 48059 } else if (c != '_') { 48060 string_buffer_putc8(b, '\\'); 48061 } 48062 string_buffer_putc8(b, c); 48063 } else if (c < 256) { 48064 hex2: 48065 snprintf(s, sizeof(s), "\\x%02x", c); 48066 string_buffer_puts8(b, s); 48067 } else if (is_surrogate(c) || lre_is_space(c)) { 48068 snprintf(s, sizeof(s), "\\u%04x", c); 48069 string_buffer_puts8(b, s); 48070 } else { 48071 string_buffer_putc(b, c); 48072 } 48073 } 48074 JS_FreeValue(ctx, str); 48075 return string_buffer_end(b); 48076 } 48077 48078 /* this_val must be of JS_CLASS_REGEXP */ 48079 static force_inline int js_regexp_get_lastIndex(JSContext *ctx, int64_t *plast_index, 48080 JSValueConst this_val) 48081 { 48082 JSObject *p = JS_VALUE_GET_OBJ(this_val); 48083 48084 /* lastIndex is always the first property (it is not configurable) */ 48085 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { 48086 *plast_index = max_int(JS_VALUE_GET_INT(p->prop[0].u.value), 0); 48087 return 0; 48088 } else { 48089 return JS_ToLengthFree(ctx, plast_index, JS_DupValue(ctx, p->prop[0].u.value)); 48090 } 48091 } 48092 48093 /* this_val must be of JS_CLASS_REGEXP */ 48094 static force_inline int js_regexp_set_lastIndex(JSContext *ctx, JSValueConst this_val, 48095 int last_index) 48096 { 48097 JSObject *p = JS_VALUE_GET_OBJ(this_val); 48098 48099 /* lastIndex is always the first property (it is not configurable) */ 48100 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT && 48101 (get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { 48102 set_value(ctx, &p->prop[0].u.value, JS_NewInt32(ctx, last_index)); 48103 } else { 48104 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 48105 JS_NewInt32(ctx, last_index)) < 0) 48106 return -1; 48107 } 48108 return 0; 48109 } 48110 48111 static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, 48112 int argc, JSValueConst *argv) 48113 { 48114 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 48115 JSString *str; 48116 JSValue t, ret, str_val, obj, groups; 48117 JSValue indices, indices_groups; 48118 uint8_t *re_bytecode; 48119 uint8_t **capture, *str_buf; 48120 int rc, capture_count, shift, i, re_flags, alloc_count; 48121 int64_t last_index; 48122 const char *group_name_ptr; 48123 JSObject *p_obj; 48124 JSAtom group_name; 48125 48126 if (!re) 48127 return JS_EXCEPTION; 48128 48129 str_val = JS_ToString(ctx, argv[0]); 48130 if (JS_IsException(str_val)) 48131 return JS_EXCEPTION; 48132 48133 ret = JS_EXCEPTION; 48134 obj = JS_NULL; 48135 groups = JS_UNDEFINED; 48136 indices = JS_UNDEFINED; 48137 indices_groups = JS_UNDEFINED; 48138 capture = NULL; 48139 group_name = JS_ATOM_NULL; 48140 48141 if (js_regexp_get_lastIndex(ctx, &last_index, this_val)) 48142 goto fail; 48143 48144 re_bytecode = re->bytecode->u.str8; 48145 re_flags = lre_get_flags(re_bytecode); 48146 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { 48147 last_index = 0; 48148 } 48149 str = JS_VALUE_GET_STRING(str_val); 48150 alloc_count = lre_get_alloc_count(re_bytecode); 48151 if (alloc_count > 0) { 48152 capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count); 48153 if (!capture) 48154 goto fail; 48155 } 48156 capture_count = lre_get_capture_count(re_bytecode); 48157 shift = str->is_wide_char; 48158 str_buf = str->u.str8; 48159 if (last_index > str->len) { 48160 rc = 2; 48161 } else { 48162 rc = lre_exec(capture, re_bytecode, 48163 str_buf, last_index, str->len, 48164 shift, ctx); 48165 } 48166 if (rc != 1) { 48167 if (rc >= 0) { 48168 if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { 48169 if (js_regexp_set_lastIndex(ctx, this_val, 0) < 0) 48170 goto fail; 48171 } 48172 } else { 48173 if (rc == LRE_RET_TIMEOUT) { 48174 JS_ThrowInterrupted(ctx); 48175 } else { 48176 JS_ThrowInternalError(ctx, "out of memory in regexp execution"); 48177 } 48178 goto fail; 48179 } 48180 } else { 48181 int prop_flags; 48182 JSProperty props[4]; 48183 48184 if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { 48185 if (js_regexp_set_lastIndex(ctx, this_val, 48186 (capture[1] - str_buf) >> shift) < 0) 48187 goto fail; 48188 } 48189 prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; 48190 group_name_ptr = lre_get_groupnames(re_bytecode); 48191 if (group_name_ptr) { 48192 groups = JS_NewObjectProto(ctx, JS_NULL); 48193 if (JS_IsException(groups)) 48194 goto fail; 48195 } 48196 if (re_flags & LRE_FLAG_INDICES) { 48197 indices = JS_NewArray(ctx); 48198 if (JS_IsException(indices)) 48199 goto fail; 48200 if (group_name_ptr) { 48201 indices_groups = JS_NewObjectProto(ctx, JS_NULL); 48202 if (JS_IsException(indices_groups)) 48203 goto fail; 48204 } 48205 } 48206 48207 props[0].u.value = JS_NewInt32(ctx, capture_count); /* length */ 48208 props[1].u.value = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); /* index */ 48209 props[2].u.value = str_val; /* input */ 48210 props[3].u.value = JS_DupValue(ctx, groups); /* groups */ 48211 48212 str_val = JS_UNDEFINED; 48213 obj = JS_NewObjectFromShape(ctx, js_dup_shape(ctx->regexp_result_shape), 48214 JS_CLASS_ARRAY, props); 48215 if (JS_IsException(obj)) 48216 goto fail; 48217 48218 p_obj = JS_VALUE_GET_OBJ(obj); 48219 if (expand_fast_array(ctx, p_obj, capture_count)) 48220 goto fail; 48221 48222 for(i = 0; i < capture_count; i++) { 48223 uint8_t **match = &capture[2 * i]; 48224 int start = -1; 48225 int end = -1; 48226 JSValue val; 48227 48228 if (group_name_ptr && i > 0) { 48229 if (*group_name_ptr) { 48230 /* XXX: slow, should create a shape when the regexp is 48231 compiled */ 48232 group_name = JS_NewAtom(ctx, group_name_ptr); 48233 if (group_name == JS_ATOM_NULL) 48234 goto fail; 48235 } 48236 group_name_ptr += strlen(group_name_ptr) + LRE_GROUP_NAME_TRAILER_LEN; 48237 } 48238 48239 if (match[0] && match[1]) { 48240 start = (match[0] - str_buf) >> shift; 48241 end = (match[1] - str_buf) >> shift; 48242 } 48243 48244 if (!JS_IsUndefined(indices)) { 48245 val = JS_UNDEFINED; 48246 if (start != -1) { 48247 val = JS_NewArray(ctx); 48248 if (JS_IsException(val)) 48249 goto fail; 48250 if (JS_DefinePropertyValueUint32(ctx, val, 0, 48251 JS_NewInt32(ctx, start), 48252 prop_flags) < 0) { 48253 JS_FreeValue(ctx, val); 48254 goto fail; 48255 } 48256 if (JS_DefinePropertyValueUint32(ctx, val, 1, 48257 JS_NewInt32(ctx, end), 48258 prop_flags) < 0) { 48259 JS_FreeValue(ctx, val); 48260 goto fail; 48261 } 48262 } 48263 if (group_name != JS_ATOM_NULL) { 48264 /* JS_HasProperty() cannot fail here */ 48265 if (!JS_IsUndefined(val) || 48266 !JS_HasProperty(ctx, indices_groups, group_name)) { 48267 if (JS_DefinePropertyValue(ctx, indices_groups, 48268 group_name, JS_DupValue(ctx, val), prop_flags) < 0) { 48269 JS_FreeValue(ctx, val); 48270 goto fail; 48271 } 48272 } 48273 } 48274 if (JS_DefinePropertyValueUint32(ctx, indices, i, val, 48275 prop_flags) < 0) { 48276 goto fail; 48277 } 48278 } 48279 48280 val = JS_UNDEFINED; 48281 if (start != -1) { 48282 val = js_sub_string(ctx, str, start, end); 48283 if (JS_IsException(val)) 48284 goto fail; 48285 } 48286 48287 if (group_name != JS_ATOM_NULL) { 48288 /* JS_HasProperty() cannot fail here */ 48289 if (!JS_IsUndefined(val) || 48290 !JS_HasProperty(ctx, groups, group_name)) { 48291 if (JS_DefinePropertyValue(ctx, groups, group_name, 48292 JS_DupValue(ctx, val), 48293 prop_flags) < 0) { 48294 JS_FreeValue(ctx, val); 48295 goto fail; 48296 } 48297 } 48298 JS_FreeAtom(ctx, group_name); 48299 group_name = JS_ATOM_NULL; 48300 } 48301 p_obj->u.array.u.values[p_obj->u.array.count++] = val; 48302 } 48303 48304 if (!JS_IsUndefined(indices)) { 48305 t = indices_groups, indices_groups = JS_UNDEFINED; 48306 if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, 48307 t, prop_flags) < 0) { 48308 goto fail; 48309 } 48310 t = indices, indices = JS_UNDEFINED; 48311 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, 48312 t, prop_flags) < 0) { 48313 goto fail; 48314 } 48315 } 48316 } 48317 ret = obj; 48318 obj = JS_UNDEFINED; 48319 fail: 48320 JS_FreeAtom(ctx, group_name); 48321 JS_FreeValue(ctx, indices_groups); 48322 JS_FreeValue(ctx, indices); 48323 JS_FreeValue(ctx, str_val); 48324 JS_FreeValue(ctx, groups); 48325 JS_FreeValue(ctx, obj); 48326 js_free(ctx, capture); 48327 return ret; 48328 } 48329 48330 /* XXX: add group names support */ 48331 static JSValue js_regexp_replace(JSContext *ctx, JSValueConst this_val, JSValueConst arg, 48332 JSValueConst rep_val) 48333 { 48334 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 48335 JSString *str; 48336 JSValue str_val; 48337 uint8_t *re_bytecode; 48338 int ret; 48339 uint8_t **capture, *str_buf; 48340 int capture_count, alloc_count, shift, re_flags; 48341 int next_src_pos, start, end; 48342 int64_t last_index; 48343 StringBuffer b_s, *b = &b_s; 48344 JSString *rp = JS_VALUE_GET_STRING(rep_val); 48345 const char *group_name_ptr; 48346 BOOL fullUnicode; 48347 48348 if (!re) 48349 return JS_EXCEPTION; 48350 re_bytecode = re->bytecode->u.str8; 48351 group_name_ptr = lre_get_groupnames(re_bytecode); 48352 if (group_name_ptr) 48353 return JS_UNDEFINED; /* group names are not supported yet */ 48354 48355 string_buffer_init(ctx, b, 0); 48356 48357 capture = NULL; 48358 str_val = JS_ToString(ctx, arg); 48359 if (JS_IsException(str_val)) 48360 goto fail; 48361 str = JS_VALUE_GET_STRING(str_val); 48362 re_flags = lre_get_flags(re_bytecode); 48363 48364 if (re_flags & LRE_FLAG_GLOBAL) { 48365 if (js_regexp_set_lastIndex(ctx, this_val, 0)) 48366 goto fail; 48367 } 48368 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { 48369 last_index = 0; 48370 } else { 48371 if (js_regexp_get_lastIndex(ctx, &last_index, this_val)) 48372 goto fail; 48373 } 48374 alloc_count = lre_get_alloc_count(re_bytecode); 48375 if (alloc_count > 0) { 48376 capture = js_malloc(ctx, sizeof(capture[0]) * alloc_count); 48377 if (!capture) 48378 goto fail; 48379 } 48380 capture_count = lre_get_capture_count(re_bytecode); 48381 fullUnicode = ((re_flags & (LRE_FLAG_UNICODE | LRE_FLAG_UNICODE_SETS)) != 0); 48382 shift = str->is_wide_char; 48383 str_buf = str->u.str8; 48384 next_src_pos = 0; 48385 for (;;) { 48386 if (last_index > str->len) { 48387 ret = 0; 48388 } else { 48389 ret = lre_exec(capture, re_bytecode, 48390 str_buf, last_index, str->len, shift, ctx); 48391 } 48392 if (ret != 1) { 48393 if (ret >= 0) { 48394 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { 48395 if (js_regexp_set_lastIndex(ctx, this_val, 0) < 0) 48396 goto fail; 48397 } 48398 } else { 48399 if (ret == LRE_RET_TIMEOUT) { 48400 JS_ThrowInterrupted(ctx); 48401 } else { 48402 JS_ThrowInternalError(ctx, "out of memory in regexp execution"); 48403 } 48404 goto fail; 48405 } 48406 break; 48407 } 48408 start = (capture[0] - str_buf) >> shift; 48409 end = (capture[1] - str_buf) >> shift; 48410 last_index = end; 48411 if (next_src_pos < start) { 48412 if (string_buffer_concat(b, str, next_src_pos, start)) 48413 goto fail; 48414 } 48415 if (rp->len != 0) { 48416 if (js_string_GetSubstitution(ctx, b, JS_UNDEFINED, str, start, 48417 JS_UNDEFINED, JS_UNDEFINED, rep_val, 48418 capture, capture_count)) { 48419 goto fail; 48420 } 48421 } 48422 next_src_pos = end; 48423 if (!(re_flags & LRE_FLAG_GLOBAL)) { 48424 if (re_flags & LRE_FLAG_STICKY) { 48425 if (js_regexp_set_lastIndex(ctx, this_val, end) < 0) 48426 goto fail; 48427 } 48428 break; 48429 } 48430 if (end == start) { 48431 end = string_advance_index(str, end, fullUnicode); 48432 } 48433 last_index = end; 48434 } 48435 if (string_buffer_concat(b, str, next_src_pos, str->len)) 48436 goto fail; 48437 JS_FreeValue(ctx, str_val); 48438 js_free(ctx, capture); 48439 return string_buffer_end(b); 48440 fail: 48441 JS_FreeValue(ctx, str_val); 48442 js_free(ctx, capture); 48443 string_buffer_free(b); 48444 return JS_EXCEPTION; 48445 } 48446 48447 static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) 48448 { 48449 JSValue method, ret; 48450 48451 method = JS_GetProperty(ctx, r, JS_ATOM_exec); 48452 if (JS_IsException(method)) 48453 return method; 48454 if (JS_IsFunction(ctx, method)) { 48455 ret = JS_CallFree(ctx, method, r, 1, &s); 48456 if (JS_IsException(ret)) 48457 return ret; 48458 if (!JS_IsObject(ret) && !JS_IsNull(ret)) { 48459 JS_FreeValue(ctx, ret); 48460 return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); 48461 } 48462 return ret; 48463 } 48464 JS_FreeValue(ctx, method); 48465 return js_regexp_exec(ctx, r, 1, &s); 48466 } 48467 48468 static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, 48469 int argc, JSValueConst *argv) 48470 { 48471 JSValue val; 48472 BOOL ret; 48473 48474 val = JS_RegExpExec(ctx, this_val, argv[0]); 48475 if (JS_IsException(val)) 48476 return JS_EXCEPTION; 48477 ret = !JS_IsNull(val); 48478 JS_FreeValue(ctx, val); 48479 return JS_NewBool(ctx, ret); 48480 } 48481 48482 static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, 48483 int argc, JSValueConst *argv) 48484 { 48485 // [Symbol.match](str) 48486 JSValueConst rx = this_val; 48487 JSValue A, S, flags, result, matchStr; 48488 int global, n, fullUnicode, isEmpty; 48489 JSString *p; 48490 48491 if (!JS_IsObject(rx)) 48492 return JS_ThrowTypeErrorNotAnObject(ctx); 48493 48494 A = JS_UNDEFINED; 48495 flags = JS_UNDEFINED; 48496 result = JS_UNDEFINED; 48497 matchStr = JS_UNDEFINED; 48498 S = JS_ToString(ctx, argv[0]); 48499 if (JS_IsException(S)) 48500 goto exception; 48501 48502 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); 48503 if (JS_IsException(flags)) 48504 goto exception; 48505 flags = JS_ToStringFree(ctx, flags); 48506 if (JS_IsException(flags)) 48507 goto exception; 48508 p = JS_VALUE_GET_STRING(flags); 48509 48510 global = (-1 != string_indexof_char(p, 'g', 0)); 48511 if (!global) { 48512 A = JS_RegExpExec(ctx, rx, S); 48513 } else { 48514 fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 || 48515 string_indexof_char(p, 'v', 0) >= 0); 48516 48517 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) 48518 goto exception; 48519 A = JS_NewArray(ctx); 48520 if (JS_IsException(A)) 48521 goto exception; 48522 n = 0; 48523 for(;;) { 48524 JS_FreeValue(ctx, result); 48525 result = JS_RegExpExec(ctx, rx, S); 48526 if (JS_IsException(result)) 48527 goto exception; 48528 if (JS_IsNull(result)) 48529 break; 48530 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 48531 if (JS_IsException(matchStr)) 48532 goto exception; 48533 isEmpty = JS_IsEmptyString(matchStr); 48534 if (JS_DefinePropertyValueInt64(ctx, A, n++, matchStr, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48535 goto exception; 48536 if (isEmpty) { 48537 int64_t thisIndex, nextIndex; 48538 if (JS_ToLengthFree(ctx, &thisIndex, 48539 JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) 48540 goto exception; 48541 p = JS_VALUE_GET_STRING(S); 48542 nextIndex = string_advance_index(p, thisIndex, fullUnicode); 48543 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) 48544 goto exception; 48545 } 48546 } 48547 if (n == 0) { 48548 JS_FreeValue(ctx, A); 48549 A = JS_NULL; 48550 } 48551 } 48552 JS_FreeValue(ctx, result); 48553 JS_FreeValue(ctx, flags); 48554 JS_FreeValue(ctx, S); 48555 return A; 48556 48557 exception: 48558 JS_FreeValue(ctx, A); 48559 JS_FreeValue(ctx, result); 48560 JS_FreeValue(ctx, flags); 48561 JS_FreeValue(ctx, S); 48562 return JS_EXCEPTION; 48563 } 48564 48565 typedef struct JSRegExpStringIteratorData { 48566 JSValue iterating_regexp; 48567 JSValue iterated_string; 48568 BOOL global; 48569 BOOL unicode; 48570 BOOL done; 48571 } JSRegExpStringIteratorData; 48572 48573 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) 48574 { 48575 JSObject *p = JS_VALUE_GET_OBJ(val); 48576 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; 48577 if (it) { 48578 JS_FreeValueRT(rt, it->iterating_regexp); 48579 JS_FreeValueRT(rt, it->iterated_string); 48580 js_free_rt(rt, it); 48581 } 48582 } 48583 48584 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, 48585 JS_MarkFunc *mark_func) 48586 { 48587 JSObject *p = JS_VALUE_GET_OBJ(val); 48588 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; 48589 if (it) { 48590 JS_MarkValue(rt, it->iterating_regexp, mark_func); 48591 JS_MarkValue(rt, it->iterated_string, mark_func); 48592 } 48593 } 48594 48595 static JSValue js_regexp_string_iterator_next(JSContext *ctx, 48596 JSValueConst this_val, 48597 int argc, JSValueConst *argv, 48598 BOOL *pdone, int magic) 48599 { 48600 JSRegExpStringIteratorData *it; 48601 JSValueConst R, S; 48602 JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; 48603 JSString *sp; 48604 48605 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); 48606 if (!it) 48607 goto exception; 48608 if (it->done) { 48609 *pdone = TRUE; 48610 return JS_UNDEFINED; 48611 } 48612 R = it->iterating_regexp; 48613 S = it->iterated_string; 48614 match = JS_RegExpExec(ctx, R, S); 48615 if (JS_IsException(match)) 48616 goto exception; 48617 if (JS_IsNull(match)) { 48618 it->done = TRUE; 48619 *pdone = TRUE; 48620 return JS_UNDEFINED; 48621 } else if (it->global) { 48622 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); 48623 if (JS_IsException(matchStr)) 48624 goto exception; 48625 if (JS_IsEmptyString(matchStr)) { 48626 int64_t thisIndex, nextIndex; 48627 if (JS_ToLengthFree(ctx, &thisIndex, 48628 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) 48629 goto exception; 48630 sp = JS_VALUE_GET_STRING(S); 48631 nextIndex = string_advance_index(sp, thisIndex, it->unicode); 48632 if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, 48633 JS_NewInt64(ctx, nextIndex)) < 0) 48634 goto exception; 48635 } 48636 JS_FreeValue(ctx, matchStr); 48637 } else { 48638 it->done = TRUE; 48639 } 48640 *pdone = FALSE; 48641 return match; 48642 exception: 48643 JS_FreeValue(ctx, match); 48644 JS_FreeValue(ctx, matchStr); 48645 *pdone = FALSE; 48646 return JS_EXCEPTION; 48647 } 48648 48649 static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, 48650 int argc, JSValueConst *argv) 48651 { 48652 // [Symbol.matchAll](str) 48653 JSValueConst R = this_val; 48654 JSValue S, C, flags, matcher, iter; 48655 JSValueConst args[2]; 48656 JSString *strp; 48657 int64_t lastIndex; 48658 JSRegExpStringIteratorData *it; 48659 48660 if (!JS_IsObject(R)) 48661 return JS_ThrowTypeErrorNotAnObject(ctx); 48662 48663 C = JS_UNDEFINED; 48664 flags = JS_UNDEFINED; 48665 matcher = JS_UNDEFINED; 48666 iter = JS_UNDEFINED; 48667 48668 S = JS_ToString(ctx, argv[0]); 48669 if (JS_IsException(S)) 48670 goto exception; 48671 C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); 48672 if (JS_IsException(C)) 48673 goto exception; 48674 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); 48675 if (JS_IsException(flags)) 48676 goto exception; 48677 args[0] = R; 48678 args[1] = flags; 48679 matcher = JS_CallConstructor(ctx, C, 2, args); 48680 if (JS_IsException(matcher)) 48681 goto exception; 48682 if (JS_ToLengthFree(ctx, &lastIndex, 48683 JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) 48684 goto exception; 48685 if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, 48686 JS_NewInt64(ctx, lastIndex)) < 0) 48687 goto exception; 48688 48689 iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); 48690 if (JS_IsException(iter)) 48691 goto exception; 48692 it = js_malloc(ctx, sizeof(*it)); 48693 if (!it) 48694 goto exception; 48695 it->iterating_regexp = matcher; 48696 it->iterated_string = S; 48697 strp = JS_VALUE_GET_STRING(flags); 48698 it->global = string_indexof_char(strp, 'g', 0) >= 0; 48699 it->unicode = (string_indexof_char(strp, 'u', 0) >= 0 || 48700 string_indexof_char(strp, 'v', 0) >= 0); 48701 it->done = FALSE; 48702 JS_SetOpaque(iter, it); 48703 48704 JS_FreeValue(ctx, C); 48705 JS_FreeValue(ctx, flags); 48706 return iter; 48707 exception: 48708 JS_FreeValue(ctx, S); 48709 JS_FreeValue(ctx, C); 48710 JS_FreeValue(ctx, flags); 48711 JS_FreeValue(ctx, matcher); 48712 JS_FreeValue(ctx, iter); 48713 return JS_EXCEPTION; 48714 } 48715 48716 typedef struct ValueBuffer { 48717 JSContext *ctx; 48718 JSValue *arr; 48719 JSValue def[4]; 48720 int len; 48721 int size; 48722 int error_status; 48723 } ValueBuffer; 48724 48725 static int value_buffer_init(JSContext *ctx, ValueBuffer *b) 48726 { 48727 b->ctx = ctx; 48728 b->len = 0; 48729 b->size = 4; 48730 b->error_status = 0; 48731 b->arr = b->def; 48732 return 0; 48733 } 48734 48735 static void value_buffer_free(ValueBuffer *b) 48736 { 48737 while (b->len > 0) 48738 JS_FreeValue(b->ctx, b->arr[--b->len]); 48739 if (b->arr != b->def) 48740 js_free(b->ctx, b->arr); 48741 b->arr = b->def; 48742 b->size = 4; 48743 } 48744 48745 static int value_buffer_append(ValueBuffer *b, JSValue val) 48746 { 48747 if (b->error_status) 48748 return -1; 48749 48750 if (b->len >= b->size) { 48751 int new_size = (b->len + (b->len >> 1) + 31) & ~16; 48752 size_t slack; 48753 JSValue *new_arr; 48754 48755 if (b->arr == b->def) { 48756 new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); 48757 if (new_arr) 48758 memcpy(new_arr, b->def, sizeof b->def); 48759 } else { 48760 new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); 48761 } 48762 if (!new_arr) { 48763 value_buffer_free(b); 48764 JS_FreeValue(b->ctx, val); 48765 b->error_status = -1; 48766 return -1; 48767 } 48768 new_size += slack / sizeof(*new_arr); 48769 b->arr = new_arr; 48770 b->size = new_size; 48771 } 48772 b->arr[b->len++] = val; 48773 return 0; 48774 } 48775 48776 /* find in 'p' or its prototypes */ 48777 static JSShapeProperty *find_property_regexp(JSProperty **ppr, 48778 JSObject *p, JSAtom atom) 48779 { 48780 JSShapeProperty *prs; 48781 48782 for(;;) { 48783 prs = find_own_property(ppr, p, atom); 48784 if (prs) 48785 return prs; 48786 p = p->shape->proto; 48787 if (!p) 48788 return NULL; 48789 if (p->is_exotic) 48790 return NULL; 48791 } 48792 } 48793 48794 static BOOL check_regexp_getter(JSContext *ctx, 48795 JSObject *p, JSAtom atom, 48796 JSCFunction *func, int magic) 48797 { 48798 JSProperty *pr; 48799 JSShapeProperty *prs; 48800 48801 prs = find_property_regexp(&pr, p, atom); 48802 if (!prs) 48803 return FALSE; 48804 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) 48805 return FALSE; 48806 if (!pr->u.getset.getter) 48807 return FALSE; 48808 return JS_IsCFunction(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter), 48809 func, magic); 48810 } 48811 48812 static BOOL js_is_standard_regexp(JSContext *ctx, JSValueConst obj) 48813 { 48814 JSObject *p; 48815 JSProperty *pr; 48816 JSShapeProperty *prs; 48817 JSCFunctionType ft; 48818 48819 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 48820 return FALSE; 48821 p = JS_VALUE_GET_OBJ(obj); 48822 if (p->class_id != JS_CLASS_REGEXP) 48823 return FALSE; 48824 /* check that the lastIndex is a number (no side effect while getting it) */ 48825 prs = find_own_property(&pr, p, JS_ATOM_lastIndex); 48826 if (!prs) 48827 return FALSE; 48828 if (!JS_IsNumber(pr->u.value)) 48829 return FALSE; 48830 48831 /* check the 'exec' method. */ 48832 prs = find_property_regexp(&pr, p, JS_ATOM_exec); 48833 if (!prs) 48834 return FALSE; 48835 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 48836 return FALSE; 48837 if (!JS_IsCFunction(ctx, pr->u.value, js_regexp_exec, 0)) 48838 return FALSE; 48839 /* check the flag getters */ 48840 ft.getter = js_regexp_get_flags; 48841 if (!check_regexp_getter(ctx, p, JS_ATOM_flags, ft.generic, 0)) 48842 return FALSE; 48843 ft.getter_magic = js_regexp_get_flag; 48844 if (!check_regexp_getter(ctx, p, JS_ATOM_global, ft.generic, LRE_FLAG_GLOBAL)) 48845 return FALSE; 48846 if (!check_regexp_getter(ctx, p, JS_ATOM_unicode, ft.generic, LRE_FLAG_UNICODE)) 48847 return FALSE; 48848 /* XXX: need to check all accessors, need a faster way. */ 48849 return TRUE; 48850 } 48851 48852 static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, 48853 int argc, JSValueConst *argv) 48854 { 48855 // [Symbol.replace](str, rep) 48856 JSValueConst rx = this_val, rep = argv[1]; 48857 JSValueConst args[6]; 48858 JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; 48859 JSString *p, *sp; 48860 StringBuffer b_s, *b = &b_s; 48861 ValueBuffer v_b, *results = &v_b; 48862 int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; 48863 uint32_t nCaptures; 48864 int64_t position; 48865 48866 if (!JS_IsObject(rx)) 48867 return JS_ThrowTypeErrorNotAnObject(ctx); 48868 48869 string_buffer_init(ctx, b, 0); 48870 value_buffer_init(ctx, results); 48871 48872 rep_val = JS_UNDEFINED; 48873 matched = JS_UNDEFINED; 48874 tab = JS_UNDEFINED; 48875 flags = JS_UNDEFINED; 48876 rep_str = JS_UNDEFINED; 48877 namedCaptures = JS_UNDEFINED; 48878 48879 str = JS_ToString(ctx, argv[0]); 48880 if (JS_IsException(str)) 48881 goto exception; 48882 48883 sp = JS_VALUE_GET_STRING(str); 48884 functionalReplace = JS_IsFunction(ctx, rep); 48885 if (!functionalReplace) { 48886 rep_val = JS_ToString(ctx, rep); 48887 if (JS_IsException(rep_val)) 48888 goto exception; 48889 } 48890 48891 if (!functionalReplace && js_is_standard_regexp(ctx, rx)) { 48892 /* use faster version for simple cases */ 48893 res = js_regexp_replace(ctx, rx, str, rep_val); 48894 if (!JS_IsUndefined(res)) 48895 goto done; 48896 } 48897 48898 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); 48899 if (JS_IsException(flags)) 48900 goto exception; 48901 flags = JS_ToStringFree(ctx, flags); 48902 if (JS_IsException(flags)) 48903 goto exception; 48904 p = JS_VALUE_GET_STRING(flags); 48905 48906 fullUnicode = 0; 48907 is_global = (-1 != string_indexof_char(p, 'g', 0)); 48908 if (is_global) { 48909 fullUnicode = (string_indexof_char(p, 'u', 0) >= 0 || 48910 string_indexof_char(p, 'v', 0) >= 0); 48911 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) 48912 goto exception; 48913 } 48914 48915 for(;;) { 48916 JSValue result; 48917 result = JS_RegExpExec(ctx, rx, str); 48918 if (JS_IsException(result)) 48919 goto exception; 48920 if (JS_IsNull(result)) 48921 break; 48922 if (value_buffer_append(results, result) < 0) 48923 goto exception; 48924 if (!is_global) 48925 break; 48926 JS_FreeValue(ctx, matched); 48927 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 48928 if (JS_IsException(matched)) 48929 goto exception; 48930 if (JS_IsEmptyString(matched)) { 48931 /* always advance of at least one char */ 48932 int64_t thisIndex, nextIndex; 48933 if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) 48934 goto exception; 48935 nextIndex = string_advance_index(sp, thisIndex, fullUnicode); 48936 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) 48937 goto exception; 48938 } 48939 } 48940 nextSourcePosition = 0; 48941 for(j = 0; j < results->len; j++) { 48942 JSValueConst result; 48943 result = results->arr[j]; 48944 if (js_get_length32(ctx, &nCaptures, result) < 0) 48945 goto exception; 48946 JS_FreeValue(ctx, matched); 48947 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 48948 if (JS_IsException(matched)) 48949 goto exception; 48950 if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) 48951 goto exception; 48952 if (position > sp->len) 48953 position = sp->len; 48954 else if (position < 0) 48955 position = 0; 48956 /* ignore substition if going backward (can happen 48957 with custom regexp object) */ 48958 JS_FreeValue(ctx, tab); 48959 tab = JS_NewArray(ctx); 48960 if (JS_IsException(tab)) 48961 goto exception; 48962 if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), 48963 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48964 goto exception; 48965 for(n = 1; n < nCaptures; n++) { 48966 JSValue capN; 48967 capN = JS_GetPropertyInt64(ctx, result, n); 48968 if (JS_IsException(capN)) 48969 goto exception; 48970 if (!JS_IsUndefined(capN)) { 48971 capN = JS_ToStringFree(ctx, capN); 48972 if (JS_IsException(capN)) 48973 goto exception; 48974 } 48975 if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, 48976 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48977 goto exception; 48978 } 48979 JS_FreeValue(ctx, namedCaptures); 48980 namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); 48981 if (JS_IsException(namedCaptures)) 48982 goto exception; 48983 if (functionalReplace) { 48984 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48985 goto exception; 48986 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48987 goto exception; 48988 if (!JS_IsUndefined(namedCaptures)) { 48989 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 48990 goto exception; 48991 } 48992 args[0] = JS_UNDEFINED; 48993 args[1] = tab; 48994 JS_FreeValue(ctx, rep_str); 48995 rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); 48996 } else { 48997 JSValue namedCaptures1; 48998 StringBuffer b1_s, *b1 = &b1_s; 48999 int ret; 49000 49001 if (!JS_IsUndefined(namedCaptures)) { 49002 namedCaptures1 = JS_ToObject(ctx, namedCaptures); 49003 if (JS_IsException(namedCaptures1)) 49004 goto exception; 49005 } else { 49006 namedCaptures1 = JS_UNDEFINED; 49007 } 49008 JS_FreeValue(ctx, rep_str); 49009 49010 string_buffer_init(ctx, b1, 0); 49011 ret = js_string_GetSubstitution(ctx, b1, matched, sp, position, 49012 tab, namedCaptures1, rep_val, 49013 NULL, 0); 49014 rep_str = string_buffer_end(b1); 49015 JS_FreeValue(ctx, namedCaptures1); 49016 if (ret) 49017 goto exception; 49018 } 49019 if (JS_IsException(rep_str)) 49020 goto exception; 49021 if (position >= nextSourcePosition) { 49022 string_buffer_concat(b, sp, nextSourcePosition, position); 49023 string_buffer_concat_value(b, rep_str); 49024 nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; 49025 } 49026 } 49027 string_buffer_concat(b, sp, nextSourcePosition, sp->len); 49028 res = string_buffer_end(b); 49029 goto done1; 49030 49031 exception: 49032 res = JS_EXCEPTION; 49033 done: 49034 string_buffer_free(b); 49035 done1: 49036 value_buffer_free(results); 49037 JS_FreeValue(ctx, rep_val); 49038 JS_FreeValue(ctx, matched); 49039 JS_FreeValue(ctx, flags); 49040 JS_FreeValue(ctx, tab); 49041 JS_FreeValue(ctx, rep_str); 49042 JS_FreeValue(ctx, namedCaptures); 49043 JS_FreeValue(ctx, str); 49044 return res; 49045 } 49046 49047 static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, 49048 int argc, JSValueConst *argv) 49049 { 49050 JSValueConst rx = this_val; 49051 JSValue str, previousLastIndex, currentLastIndex, result, index; 49052 49053 if (!JS_IsObject(rx)) 49054 return JS_ThrowTypeErrorNotAnObject(ctx); 49055 49056 result = JS_UNDEFINED; 49057 currentLastIndex = JS_UNDEFINED; 49058 previousLastIndex = JS_UNDEFINED; 49059 str = JS_ToString(ctx, argv[0]); 49060 if (JS_IsException(str)) 49061 goto exception; 49062 49063 previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); 49064 if (JS_IsException(previousLastIndex)) 49065 goto exception; 49066 49067 if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { 49068 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { 49069 goto exception; 49070 } 49071 } 49072 result = JS_RegExpExec(ctx, rx, str); 49073 if (JS_IsException(result)) 49074 goto exception; 49075 currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); 49076 if (JS_IsException(currentLastIndex)) 49077 goto exception; 49078 if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { 49079 JS_FreeValue(ctx, previousLastIndex); 49080 } else { 49081 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { 49082 previousLastIndex = JS_UNDEFINED; 49083 goto exception; 49084 } 49085 } 49086 JS_FreeValue(ctx, str); 49087 JS_FreeValue(ctx, currentLastIndex); 49088 49089 if (JS_IsNull(result)) { 49090 return JS_NewInt32(ctx, -1); 49091 } else { 49092 index = JS_GetProperty(ctx, result, JS_ATOM_index); 49093 JS_FreeValue(ctx, result); 49094 return index; 49095 } 49096 49097 exception: 49098 JS_FreeValue(ctx, result); 49099 JS_FreeValue(ctx, str); 49100 JS_FreeValue(ctx, currentLastIndex); 49101 JS_FreeValue(ctx, previousLastIndex); 49102 return JS_EXCEPTION; 49103 } 49104 49105 static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, 49106 int argc, JSValueConst *argv) 49107 { 49108 // [Symbol.split](str, limit) 49109 JSValueConst rx = this_val; 49110 JSValueConst args[2]; 49111 JSValue str, ctor, splitter, A, flags, z, sub; 49112 JSString *strp; 49113 uint32_t lim, size, p, q; 49114 int unicodeMatching; 49115 int64_t lengthA, e, numberOfCaptures, i; 49116 49117 if (!JS_IsObject(rx)) 49118 return JS_ThrowTypeErrorNotAnObject(ctx); 49119 49120 ctor = JS_UNDEFINED; 49121 splitter = JS_UNDEFINED; 49122 A = JS_UNDEFINED; 49123 flags = JS_UNDEFINED; 49124 z = JS_UNDEFINED; 49125 str = JS_ToString(ctx, argv[0]); 49126 if (JS_IsException(str)) 49127 goto exception; 49128 ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); 49129 if (JS_IsException(ctor)) 49130 goto exception; 49131 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); 49132 if (JS_IsException(flags)) 49133 goto exception; 49134 strp = JS_VALUE_GET_STRING(flags); 49135 unicodeMatching = (string_indexof_char(strp, 'u', 0) >= 0 || 49136 string_indexof_char(strp, 'v', 0) >= 0); 49137 if (string_indexof_char(strp, 'y', 0) < 0) { 49138 flags = JS_ConcatString3(ctx, "", flags, "y"); 49139 if (JS_IsException(flags)) 49140 goto exception; 49141 } 49142 args[0] = rx; 49143 args[1] = flags; 49144 splitter = JS_CallConstructor(ctx, ctor, 2, args); 49145 if (JS_IsException(splitter)) 49146 goto exception; 49147 A = JS_NewArray(ctx); 49148 if (JS_IsException(A)) 49149 goto exception; 49150 lengthA = 0; 49151 if (JS_IsUndefined(argv[1])) { 49152 lim = 0xffffffff; 49153 } else { 49154 if (JS_ToUint32(ctx, &lim, argv[1]) < 0) 49155 goto exception; 49156 if (lim == 0) 49157 goto done; 49158 } 49159 strp = JS_VALUE_GET_STRING(str); 49160 p = q = 0; 49161 size = strp->len; 49162 if (size == 0) { 49163 z = JS_RegExpExec(ctx, splitter, str); 49164 if (JS_IsException(z)) 49165 goto exception; 49166 if (JS_IsNull(z)) 49167 goto add_tail; 49168 goto done; 49169 } 49170 while (q < size) { 49171 if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) 49172 goto exception; 49173 JS_FreeValue(ctx, z); 49174 z = JS_RegExpExec(ctx, splitter, str); 49175 if (JS_IsException(z)) 49176 goto exception; 49177 if (JS_IsNull(z)) { 49178 q = string_advance_index(strp, q, unicodeMatching); 49179 } else { 49180 if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) 49181 goto exception; 49182 if (e > size) 49183 e = size; 49184 if (e == p) { 49185 q = string_advance_index(strp, q, unicodeMatching); 49186 } else { 49187 sub = js_sub_string(ctx, strp, p, q); 49188 if (JS_IsException(sub)) 49189 goto exception; 49190 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, 49191 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 49192 goto exception; 49193 if (lengthA == lim) 49194 goto done; 49195 p = e; 49196 if (js_get_length64(ctx, &numberOfCaptures, z)) 49197 goto exception; 49198 for(i = 1; i < numberOfCaptures; i++) { 49199 sub = JS_GetPropertyInt64(ctx, z, i); 49200 if (JS_IsException(sub)) 49201 goto exception; 49202 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 49203 goto exception; 49204 if (lengthA == lim) 49205 goto done; 49206 } 49207 q = p; 49208 } 49209 } 49210 } 49211 add_tail: 49212 if (p > size) 49213 p = size; 49214 sub = js_sub_string(ctx, strp, p, size); 49215 if (JS_IsException(sub)) 49216 goto exception; 49217 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 49218 goto exception; 49219 goto done; 49220 exception: 49221 JS_FreeValue(ctx, A); 49222 A = JS_EXCEPTION; 49223 done: 49224 JS_FreeValue(ctx, str); 49225 JS_FreeValue(ctx, ctor); 49226 JS_FreeValue(ctx, splitter); 49227 JS_FreeValue(ctx, flags); 49228 JS_FreeValue(ctx, z); 49229 return A; 49230 } 49231 49232 static const JSCFunctionListEntry js_regexp_funcs[] = { 49233 JS_CFUNC_DEF("escape", 1, js_regexp_escape ), 49234 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 49235 }; 49236 49237 static const JSCFunctionListEntry js_regexp_proto_funcs[] = { 49238 JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), 49239 JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), 49240 JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ), 49241 JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ), 49242 JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ), 49243 JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ), 49244 JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ), 49245 JS_CGETSET_MAGIC_DEF("unicodeSets", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE_SETS ), 49246 JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ), 49247 JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ), 49248 JS_CFUNC_DEF("exec", 1, js_regexp_exec ), 49249 JS_CFUNC_DEF("compile", 2, js_regexp_compile ), 49250 JS_CFUNC_DEF("test", 1, js_regexp_test ), 49251 JS_CFUNC_DEF("toString", 0, js_regexp_toString ), 49252 JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), 49253 JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), 49254 JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), 49255 JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), 49256 JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), 49257 }; 49258 49259 static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { 49260 JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), 49261 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), 49262 }; 49263 49264 void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) 49265 { 49266 ctx->compile_regexp = js_compile_regexp; 49267 } 49268 49269 int JS_AddIntrinsicRegExp(JSContext *ctx) 49270 { 49271 JSValue obj; 49272 49273 JS_AddIntrinsicRegExpCompiler(ctx); 49274 49275 obj = JS_NewCConstructor(ctx, JS_CLASS_REGEXP, "RegExp", 49276 js_regexp_constructor, 2, JS_CFUNC_constructor_or_func, 0, 49277 JS_UNDEFINED, 49278 js_regexp_funcs, countof(js_regexp_funcs), 49279 js_regexp_proto_funcs, countof(js_regexp_proto_funcs), 49280 0); 49281 if (JS_IsException(obj)) 49282 return -1; 49283 ctx->regexp_ctor = obj; 49284 49285 ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = 49286 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 49287 js_regexp_string_iterator_proto_funcs, 49288 countof(js_regexp_string_iterator_proto_funcs)); 49289 if (JS_IsException(ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR])) 49290 return -1; 49291 49292 ctx->regexp_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_REGEXP]), 49293 JS_PROP_INITIAL_HASH_SIZE, 1); 49294 if (!ctx->regexp_shape) 49295 return -1; 49296 if (add_shape_property(ctx, &ctx->regexp_shape, NULL, 49297 JS_ATOM_lastIndex, JS_PROP_WRITABLE)) 49298 return -1; 49299 49300 ctx->regexp_result_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), 49301 JS_PROP_INITIAL_HASH_SIZE, 4); 49302 if (!ctx->regexp_result_shape) 49303 return -1; 49304 if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL, 49305 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH)) 49306 return -1; 49307 if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL, 49308 JS_ATOM_index, JS_PROP_C_W_E)) 49309 return -1; 49310 if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL, 49311 JS_ATOM_input, JS_PROP_C_W_E)) 49312 return -1; 49313 if (add_shape_property(ctx, &ctx->regexp_result_shape, NULL, 49314 JS_ATOM_groups, JS_PROP_C_W_E)) 49315 return -1; 49316 49317 return 0; 49318 } 49319 49320 /* JSON */ 49321 49322 static int json_parse_expect(JSParseState *s, int tok) 49323 { 49324 if (s->token.val != tok) { 49325 /* XXX: dump token correctly in all cases */ 49326 return js_parse_error(s, "expecting '%c'", tok); 49327 } 49328 return json_next_token(s); 49329 } 49330 49331 49332 typedef struct { 49333 int count; 49334 uint32_t hash_size; 49335 struct JSONParseRecordEntry *entries; 49336 uint32_t *hash_table; 49337 } JSONParseRecordObject; 49338 49339 typedef struct JSONParseRecord { 49340 JSValue value; 49341 union { 49342 JSONParseRecordObject obj; 49343 struct { 49344 int count; 49345 struct JSONParseRecord *elements; 49346 } array; 49347 struct { 49348 uint32_t source_pos; 49349 uint32_t source_len; 49350 } primitive; 49351 } u; 49352 } JSONParseRecord; 49353 49354 typedef struct JSONParseRecordEntry { 49355 JSAtom atom; 49356 uint32_t hash_next; 49357 JSONParseRecord parse_record; 49358 } JSONParseRecordEntry; 49359 49360 static void json_parse_record_init_obj(JSContext *ctx, JSONParseRecord *pr, JSValueConst val) 49361 { 49362 pr->value = JS_DupValue(ctx, val); 49363 pr->u.obj.count = 0; 49364 pr->u.obj.entries = NULL; 49365 pr->u.obj.hash_table = NULL; 49366 pr->u.obj.hash_size = 0; 49367 } 49368 49369 static void json_parse_record_init_array(JSContext *ctx, JSONParseRecord *pr, JSValueConst val) 49370 { 49371 pr->value = JS_DupValue(ctx, val); 49372 pr->u.array.count = 0; 49373 pr->u.array.elements = NULL; 49374 } 49375 49376 static void json_parse_record_init_primitive(JSContext *ctx, JSONParseRecord *pr, JSValueConst val, 49377 uint32_t source_pos, uint32_t source_len) 49378 { 49379 pr->value = JS_DupValue(ctx, val); 49380 pr->u.primitive.source_pos = source_pos; 49381 pr->u.primitive.source_len = source_len; 49382 } 49383 49384 static int json_parse_record_resize_hash(JSContext *ctx, JSONParseRecordObject *po, uint32_t new_hash_size) 49385 { 49386 uint32_t i, h, *new_hash_table; 49387 JSONParseRecordEntry *e; 49388 49389 new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); 49390 if (!new_hash_table) 49391 return -1; 49392 js_free(ctx, po->hash_table); 49393 po->hash_table = new_hash_table; 49394 po->hash_size = new_hash_size; 49395 49396 for(i = 0; i < po->hash_size; i++) { 49397 po->hash_table[i] = -1; 49398 } 49399 for(i = 0; i < po->count; i++) { 49400 e = &po->entries[i]; 49401 h = e->atom & (po->hash_size - 1); 49402 e->hash_next = po->hash_table[h]; 49403 po->hash_table[h] = i; 49404 } 49405 return 0; 49406 } 49407 49408 static JSONParseRecord *json_parse_record_add(JSContext *ctx, JSONParseRecord *pr, JSAtom key, int *psize) 49409 { 49410 JSONParseRecordObject *po = &pr->u.obj; 49411 JSONParseRecordEntry *e; 49412 JSONParseRecord *pr1; 49413 uint32_t h; 49414 49415 if (js_resize_array(ctx, (void **)&po->entries, sizeof(po->entries[0]), 49416 psize, po->count + 1)) { 49417 return NULL; 49418 } 49419 /* don't use a hash table when the number of entries is small */ 49420 if (po->count >= 8 && (po->count + 1) > po->hash_size) { 49421 int hash_bits = 32 - clz32(po->count); 49422 if (json_parse_record_resize_hash(ctx, po, 1 << hash_bits)) 49423 return NULL; 49424 } 49425 49426 e = &po->entries[po->count++]; 49427 e->atom = JS_DupAtom(ctx, key); 49428 pr1 = &e->parse_record; 49429 pr1->value = JS_UNDEFINED; 49430 if (po->hash_size != 0) { 49431 h = key & (po->hash_size - 1); 49432 e->hash_next = po->hash_table[h]; 49433 po->hash_table[h] = po->count - 1; 49434 } 49435 return pr1; 49436 } 49437 49438 static JSONParseRecord *json_parse_record_find(JSONParseRecord *pr, JSAtom key) 49439 { 49440 JSONParseRecordObject *po = &pr->u.obj; 49441 JSONParseRecordEntry *e; 49442 uint32_t h, i; 49443 49444 if (po->hash_size == 0) { 49445 for(i = 0; i < po->count; i++) { 49446 if (po->entries[i].atom == key) 49447 return &po->entries[i].parse_record; 49448 } 49449 } else { 49450 h = key & (po->hash_size - 1); 49451 i = po->hash_table[h]; 49452 while (i != -1) { 49453 e = &po->entries[i]; 49454 if (e->atom == key) 49455 return &e->parse_record; 49456 i = e->hash_next; 49457 } 49458 } 49459 return NULL; 49460 } 49461 49462 static void json_free_parse_record(JSContext *ctx, JSONParseRecord *pr) 49463 { 49464 int i; 49465 if (!pr) 49466 return; 49467 if (JS_IsObject(pr->value)) { 49468 if (JS_IsArray(ctx, pr->value)) { 49469 for(i = 0; i < pr->u.array.count; i++) { 49470 json_free_parse_record(ctx, &pr->u.array.elements[i]); 49471 } 49472 js_free(ctx, pr->u.array.elements); 49473 } else { 49474 for(i = 0; i < pr->u.obj.count; i++) { 49475 JS_FreeAtom(ctx, pr->u.obj.entries[i].atom); 49476 json_free_parse_record(ctx, &pr->u.obj.entries[i].parse_record); 49477 } 49478 js_free(ctx, pr->u.obj.entries); 49479 js_free(ctx, pr->u.obj.hash_table); 49480 } 49481 } 49482 JS_FreeValue(ctx, pr->value); 49483 pr->value = JS_UNDEFINED; /* fail safe */ 49484 } 49485 49486 /* 'pr' can be NULL */ 49487 static JSValue json_parse_value(JSParseState *s, JSONParseRecord *pr) 49488 { 49489 JSContext *ctx = s->ctx; 49490 JSValue val = JS_NULL; 49491 int ret; 49492 49493 if (pr) { 49494 pr->value = JS_UNDEFINED; 49495 } 49496 49497 switch(s->token.val) { 49498 case '{': 49499 { 49500 JSValue prop_val; 49501 JSAtom prop_name; 49502 JSONParseRecord *pr1; 49503 int pr_size; 49504 49505 if (json_next_token(s)) 49506 goto fail; 49507 val = JS_NewObject(ctx); 49508 if (JS_IsException(val)) 49509 goto fail; 49510 if (pr) { 49511 json_parse_record_init_obj(ctx, pr, val); 49512 pr_size = 0; 49513 } 49514 if (s->token.val != '}') { 49515 for(;;) { 49516 if (s->token.val == TOK_STRING) { 49517 prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); 49518 if (prop_name == JS_ATOM_NULL) 49519 goto fail; 49520 } else if (s->ext_json && s->token.val == TOK_IDENT) { 49521 prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); 49522 } else { 49523 js_parse_error(s, "expecting property name"); 49524 goto fail; 49525 } 49526 if (json_next_token(s)) 49527 goto fail1; 49528 if (json_parse_expect(s, ':')) 49529 goto fail1; 49530 if (pr) { 49531 pr1 = json_parse_record_add(ctx, pr, prop_name, &pr_size); 49532 if (!pr1) 49533 goto fail1; 49534 } else { 49535 pr1 = NULL; 49536 } 49537 prop_val = json_parse_value(s, pr1); 49538 if (JS_IsException(prop_val)) { 49539 fail1: 49540 JS_FreeAtom(ctx, prop_name); 49541 goto fail; 49542 } 49543 ret = JS_DefinePropertyValue(ctx, val, prop_name, 49544 prop_val, JS_PROP_C_W_E); 49545 JS_FreeAtom(ctx, prop_name); 49546 if (ret < 0) 49547 goto fail; 49548 49549 if (s->token.val != ',') 49550 break; 49551 if (json_next_token(s)) 49552 goto fail; 49553 if (s->ext_json && s->token.val == '}') 49554 break; 49555 } 49556 } 49557 if (json_parse_expect(s, '}')) 49558 goto fail; 49559 } 49560 break; 49561 case '[': 49562 { 49563 JSValue el; 49564 uint32_t idx; 49565 JSONParseRecord *pr1; 49566 int pr_size; 49567 49568 if (json_next_token(s)) 49569 goto fail; 49570 val = JS_NewArray(ctx); 49571 if (JS_IsException(val)) 49572 goto fail; 49573 if (pr) { 49574 json_parse_record_init_array(ctx, pr, val); 49575 pr_size = 0; 49576 } 49577 if (s->token.val != ']') { 49578 idx = 0; 49579 for(;;) { 49580 if (pr) { 49581 if (js_resize_array(ctx, (void **)&pr->u.array.elements, sizeof(pr->u.array.elements[0]), 49582 &pr_size, pr->u.array.count + 1)) 49583 goto fail; 49584 pr1 = &pr->u.array.elements[pr->u.array.count++]; 49585 pr1->value = JS_UNDEFINED; 49586 } else { 49587 pr1 = NULL; 49588 } 49589 el = json_parse_value(s, pr1); 49590 if (JS_IsException(el)) 49591 goto fail; 49592 ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); 49593 if (ret < 0) 49594 goto fail; 49595 if (s->token.val != ',') 49596 break; 49597 if (json_next_token(s)) 49598 goto fail; 49599 idx++; 49600 if (s->ext_json && s->token.val == ']') 49601 break; 49602 } 49603 } 49604 if (json_parse_expect(s, ']')) 49605 goto fail; 49606 } 49607 break; 49608 case TOK_STRING: 49609 val = JS_DupValue(ctx, s->token.u.str.str); 49610 if (pr) { 49611 json_parse_record_init_primitive(ctx, pr, val, 49612 s->token.ptr - s->buf_start, 49613 s->buf_ptr - s->token.ptr); 49614 } 49615 if (json_next_token(s)) 49616 goto fail; 49617 break; 49618 case TOK_NUMBER: 49619 val = s->token.u.num.val; 49620 if (pr) { 49621 json_parse_record_init_primitive(ctx, pr, val, 49622 s->token.ptr - s->buf_start, 49623 s->buf_ptr - s->token.ptr); 49624 } 49625 if (json_next_token(s)) 49626 goto fail; 49627 break; 49628 case TOK_IDENT: 49629 if (s->token.u.ident.atom == JS_ATOM_false || 49630 s->token.u.ident.atom == JS_ATOM_true) { 49631 val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true); 49632 if (pr) { 49633 json_parse_record_init_primitive(ctx, pr, val, 49634 s->token.ptr - s->buf_start, 49635 s->buf_ptr - s->token.ptr); 49636 } 49637 } else if (s->token.u.ident.atom == JS_ATOM_null) { 49638 val = JS_NULL; 49639 if (pr) { 49640 json_parse_record_init_primitive(ctx, pr, val, 49641 s->token.ptr - s->buf_start, 49642 s->buf_ptr - s->token.ptr); 49643 } 49644 } else if (s->token.u.ident.atom == JS_ATOM_NaN && s->ext_json) { 49645 /* Note: json5 identifier handling is ambiguous e.g. is 49646 '{ NaN: 1 }' a valid JSON5 production ? */ 49647 val = JS_NewFloat64(s->ctx, NAN); 49648 } else if (s->token.u.ident.atom == JS_ATOM_Infinity && s->ext_json) { 49649 val = JS_NewFloat64(s->ctx, INFINITY); 49650 } else { 49651 goto def_token; 49652 } 49653 if (json_next_token(s)) 49654 goto fail; 49655 break; 49656 default: 49657 def_token: 49658 if (s->token.val == TOK_EOF) { 49659 js_parse_error(s, "Unexpected end of JSON input"); 49660 } else { 49661 js_parse_error(s, "unexpected token: '%.*s'", 49662 (int)(s->buf_ptr - s->token.ptr), s->token.ptr); 49663 } 49664 goto fail; 49665 } 49666 return val; 49667 fail: 49668 json_free_parse_record(ctx, pr); 49669 JS_FreeValue(ctx, val); 49670 return JS_EXCEPTION; 49671 } 49672 49673 JSValue JS_ParseJSON3(JSContext *ctx, const char *buf, size_t buf_len, 49674 const char *filename, int flags, JSONParseRecord *pr) 49675 { 49676 JSParseState s1, *s = &s1; 49677 JSValue val = JS_UNDEFINED; 49678 49679 js_parse_init(ctx, s, buf, buf_len, filename); 49680 s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); 49681 if (json_next_token(s)) 49682 goto fail; 49683 val = json_parse_value(s, pr); 49684 if (JS_IsException(val)) 49685 goto fail; 49686 if (s->token.val != TOK_EOF) { 49687 if (js_parse_error(s, "unexpected data at the end")) { 49688 json_free_parse_record(ctx, pr); 49689 goto fail; 49690 } 49691 } 49692 return val; 49693 fail: 49694 JS_FreeValue(ctx, val); 49695 free_token(s, &s->token); 49696 return JS_EXCEPTION; 49697 } 49698 49699 JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, 49700 const char *filename, int flags) 49701 { 49702 return JS_ParseJSON3(ctx, buf, buf_len, filename, flags, NULL); 49703 } 49704 49705 JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, 49706 const char *filename) 49707 { 49708 return JS_ParseJSON3(ctx, buf, buf_len, filename, 0, NULL); 49709 } 49710 49711 /* if pr != NULL, then pr->value = holder by construction */ 49712 static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, 49713 JSAtom name, JSValueConst reviver, 49714 const char *text_str, JSONParseRecord *pr) 49715 { 49716 JSValue val, new_el, name_val, res, context; 49717 JSValueConst args[3]; 49718 int ret, is_array; 49719 uint32_t i, len = 0; 49720 JSAtom prop; 49721 JSPropertyEnum *atoms = NULL; 49722 49723 if (js_check_stack_overflow(ctx->rt, 0)) { 49724 return JS_ThrowStackOverflow(ctx); 49725 } 49726 49727 val = JS_GetProperty(ctx, holder, name); 49728 if (JS_IsException(val)) 49729 return val; 49730 49731 if (pr) { 49732 if (JS_IsArray(ctx, pr->value)) { 49733 if (__JS_AtomIsTaggedInt(name)) { 49734 uint32_t idx = __JS_AtomToUInt32(name); 49735 if (idx < pr->u.array.count) { 49736 pr = &pr->u.array.elements[idx]; 49737 } else { 49738 pr = NULL; 49739 } 49740 } 49741 } else { 49742 pr = json_parse_record_find(pr, name); 49743 } 49744 if (pr && !js_same_value(ctx, pr->value, val)) { 49745 pr = NULL; 49746 } 49747 } 49748 49749 context = JS_NewObject(ctx); 49750 if (JS_IsException(context)) 49751 goto fail; 49752 49753 if (JS_IsObject(val)) { 49754 is_array = JS_IsArray(ctx, val); 49755 if (is_array < 0) 49756 goto fail; 49757 if (is_array) { 49758 if (js_get_length32(ctx, &len, val)) 49759 goto fail; 49760 } else { 49761 ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); 49762 if (ret < 0) 49763 goto fail; 49764 } 49765 for(i = 0; i < len; i++) { 49766 if (is_array) { 49767 prop = JS_NewAtomUInt32(ctx, i); 49768 if (prop == JS_ATOM_NULL) 49769 goto fail; 49770 } else { 49771 prop = JS_DupAtom(ctx, atoms[i].atom); 49772 } 49773 new_el = internalize_json_property(ctx, val, prop, reviver, text_str, pr); 49774 if (JS_IsException(new_el)) { 49775 JS_FreeAtom(ctx, prop); 49776 goto fail; 49777 } 49778 if (JS_IsUndefined(new_el)) { 49779 ret = JS_DeleteProperty(ctx, val, prop, 0); 49780 } else { 49781 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); 49782 } 49783 JS_FreeAtom(ctx, prop); 49784 if (ret < 0) 49785 goto fail; 49786 } 49787 } else { 49788 if (pr) { 49789 new_el = JS_NewStringLen(ctx, text_str + pr->u.primitive.source_pos, 49790 pr->u.primitive.source_len); 49791 if (JS_IsException(new_el)) 49792 goto fail; 49793 if (JS_DefinePropertyValue(ctx, context, JS_ATOM_source, new_el, JS_PROP_C_W_E) < 0) 49794 goto fail; 49795 } 49796 } 49797 JS_FreePropertyEnum(ctx, atoms, len); 49798 atoms = NULL; 49799 name_val = JS_AtomToValue(ctx, name); 49800 if (JS_IsException(name_val)) 49801 goto fail; 49802 args[0] = name_val; 49803 args[1] = val; 49804 args[2] = context; 49805 res = JS_Call(ctx, reviver, holder, 3, args); 49806 JS_FreeValue(ctx, name_val); 49807 JS_FreeValue(ctx, val); 49808 JS_FreeValue(ctx, context); 49809 return res; 49810 fail: 49811 JS_FreePropertyEnum(ctx, atoms, len); 49812 JS_FreeValue(ctx, context); 49813 JS_FreeValue(ctx, val); 49814 return JS_EXCEPTION; 49815 } 49816 49817 static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, 49818 int argc, JSValueConst *argv) 49819 { 49820 JSValue obj; 49821 const char *str; 49822 size_t len; 49823 49824 str = JS_ToCStringLen(ctx, &len, argv[0]); 49825 if (!str) 49826 return JS_EXCEPTION; 49827 if (argc > 1 && JS_IsFunction(ctx, argv[1])) { 49828 JSONParseRecord pr_s, *pr = &pr_s, *pr1; 49829 JSValue root; 49830 JSValueConst reviver; 49831 int size; 49832 49833 reviver = argv[1]; 49834 root = JS_NewObject(ctx); 49835 if (JS_IsException(root)) 49836 goto fail; 49837 json_parse_record_init_obj(ctx, pr, root); 49838 size = 0; 49839 pr1 = json_parse_record_add(ctx, pr, JS_ATOM_empty_string, &size); 49840 if (!pr1) 49841 goto fail1; 49842 49843 obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, pr1); 49844 if (JS_IsException(obj)) 49845 goto fail1; 49846 49847 if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, 49848 JS_PROP_C_W_E) < 0) { 49849 JS_FreeValue(ctx, obj); 49850 fail1: 49851 json_free_parse_record(ctx, pr); 49852 JS_FreeValue(ctx, root); 49853 goto fail; 49854 } 49855 49856 obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, 49857 reviver, str, pr); 49858 json_free_parse_record(ctx, pr); 49859 JS_FreeValue(ctx, root); 49860 } else { 49861 obj = JS_ParseJSON3(ctx, str, len, "<input>", 0, NULL); 49862 } 49863 JS_FreeCString(ctx, str); 49864 return obj; 49865 fail: 49866 JS_FreeCString(ctx, str); 49867 return JS_EXCEPTION; 49868 } 49869 49870 static JSValue js_json_isRawJSON(JSContext *ctx, JSValueConst this_val, 49871 int argc, JSValueConst *argv) 49872 { 49873 JSValueConst obj = argv[0]; 49874 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 49875 JSObject *p = JS_VALUE_GET_OBJ(obj); 49876 return JS_NewBool(ctx, p->class_id == JS_CLASS_RAWJSON); 49877 } else { 49878 return JS_FALSE; 49879 } 49880 } 49881 49882 static BOOL is_valid_raw_json_char(int c) 49883 { 49884 return ((c >= 'a' && c <= 'z') || 49885 (c >= '0' && c <= '9') || 49886 c == '-' || 49887 c == '"'); 49888 } 49889 49890 static JSValue js_json_rawJSON(JSContext *ctx, JSValueConst this_val, 49891 int argc, JSValueConst *argv) 49892 { 49893 JSValue str, res, obj; 49894 JSString *p; 49895 str = JS_ToString(ctx, argv[0]); 49896 if (JS_IsException(str)) 49897 return str; 49898 p = JS_VALUE_GET_STRING(str); 49899 if (p->len == 0 || 49900 !is_valid_raw_json_char(string_get(p, 0)) || 49901 !is_valid_raw_json_char(string_get(p, p->len - 1))) { 49902 goto syntax_error; 49903 } 49904 res = js_json_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&str); 49905 if (JS_IsException(res)) { 49906 syntax_error: 49907 JS_ThrowSyntaxError(ctx, "invalid rawJSON string"); 49908 goto fail; 49909 } 49910 JS_FreeValue(ctx, res); 49911 49912 obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_RAWJSON); 49913 if (JS_IsException(obj)) 49914 goto fail; 49915 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_rawJSON, str, JS_PROP_ENUMERABLE) < 0) { 49916 JS_FreeValue(ctx, obj); 49917 return JS_EXCEPTION; 49918 } 49919 JS_PreventExtensions(ctx, obj); 49920 return obj; 49921 fail: 49922 JS_FreeValue(ctx, str); 49923 return JS_EXCEPTION; 49924 } 49925 49926 49927 typedef struct JSONStringifyContext { 49928 JSValueConst replacer_func; 49929 JSValue stack; 49930 JSValue property_list; 49931 JSValue gap; 49932 JSValue empty; 49933 StringBuffer *b; 49934 } JSONStringifyContext; 49935 49936 static int JS_ToQuotedString(JSContext *ctx, StringBuffer *b, JSValueConst val1) 49937 { 49938 JSValue val; 49939 JSString *p; 49940 int i; 49941 uint32_t c; 49942 char buf[16]; 49943 49944 val = JS_ToStringCheckObject(ctx, val1); 49945 if (JS_IsException(val)) 49946 return -1; 49947 p = JS_VALUE_GET_STRING(val); 49948 49949 if (string_buffer_putc8(b, '\"')) 49950 goto fail; 49951 for(i = 0; i < p->len; ) { 49952 c = string_getc(p, &i); 49953 switch(c) { 49954 case '\t': 49955 c = 't'; 49956 goto quote; 49957 case '\r': 49958 c = 'r'; 49959 goto quote; 49960 case '\n': 49961 c = 'n'; 49962 goto quote; 49963 case '\b': 49964 c = 'b'; 49965 goto quote; 49966 case '\f': 49967 c = 'f'; 49968 goto quote; 49969 case '\"': 49970 case '\\': 49971 quote: 49972 if (string_buffer_putc8(b, '\\')) 49973 goto fail; 49974 if (string_buffer_putc8(b, c)) 49975 goto fail; 49976 break; 49977 default: 49978 if (c < 32 || is_surrogate(c)) { 49979 snprintf(buf, sizeof(buf), "\\u%04x", c); 49980 if (string_buffer_puts8(b, buf)) 49981 goto fail; 49982 } else { 49983 if (string_buffer_putc(b, c)) 49984 goto fail; 49985 } 49986 break; 49987 } 49988 } 49989 if (string_buffer_putc8(b, '\"')) 49990 goto fail; 49991 JS_FreeValue(ctx, val); 49992 return 0; 49993 fail: 49994 JS_FreeValue(ctx, val); 49995 return -1; 49996 } 49997 49998 static int JS_ToQuotedStringFree(JSContext *ctx, StringBuffer *b, JSValue val) { 49999 int ret = JS_ToQuotedString(ctx, b, val); 50000 JS_FreeValue(ctx, val); 50001 return ret; 50002 } 50003 50004 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, 50005 JSValueConst holder, JSValue val, JSValueConst key) 50006 { 50007 JSValue v; 50008 JSValueConst args[2]; 50009 50010 /* check for object.toJSON method */ 50011 /* ECMA specifies this is done only for Object and BigInt */ 50012 if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) { 50013 JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); 50014 if (JS_IsException(f)) 50015 goto exception; 50016 if (JS_IsFunction(ctx, f)) { 50017 v = JS_CallFree(ctx, f, val, 1, &key); 50018 JS_FreeValue(ctx, val); 50019 val = v; 50020 if (JS_IsException(val)) 50021 goto exception; 50022 } else { 50023 JS_FreeValue(ctx, f); 50024 } 50025 } 50026 50027 if (!JS_IsUndefined(jsc->replacer_func)) { 50028 args[0] = key; 50029 args[1] = val; 50030 v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); 50031 JS_FreeValue(ctx, val); 50032 val = v; 50033 if (JS_IsException(val)) 50034 goto exception; 50035 } 50036 50037 switch (JS_VALUE_GET_NORM_TAG(val)) { 50038 case JS_TAG_OBJECT: 50039 if (JS_IsFunction(ctx, val)) 50040 break; 50041 case JS_TAG_STRING: 50042 case JS_TAG_STRING_ROPE: 50043 case JS_TAG_INT: 50044 case JS_TAG_FLOAT64: 50045 case JS_TAG_BOOL: 50046 case JS_TAG_NULL: 50047 case JS_TAG_SHORT_BIG_INT: 50048 case JS_TAG_BIG_INT: 50049 case JS_TAG_EXCEPTION: 50050 return val; 50051 default: 50052 break; 50053 } 50054 JS_FreeValue(ctx, val); 50055 return JS_UNDEFINED; 50056 50057 exception: 50058 JS_FreeValue(ctx, val); 50059 return JS_EXCEPTION; 50060 } 50061 50062 static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, 50063 JSValueConst holder, JSValue val, 50064 JSValueConst indent) 50065 { 50066 JSValue indent1, sep, sep1, tab, v, prop; 50067 JSObject *p; 50068 int64_t i, len; 50069 int cl, ret; 50070 BOOL has_content; 50071 50072 indent1 = JS_UNDEFINED; 50073 sep = JS_UNDEFINED; 50074 sep1 = JS_UNDEFINED; 50075 tab = JS_UNDEFINED; 50076 prop = JS_UNDEFINED; 50077 50078 if (js_check_stack_overflow(ctx->rt, 0)) { 50079 JS_ThrowStackOverflow(ctx); 50080 goto exception; 50081 } 50082 50083 if (JS_IsObject(val)) { 50084 p = JS_VALUE_GET_OBJ(val); 50085 cl = p->class_id; 50086 if (cl == JS_CLASS_STRING) { 50087 val = JS_ToStringFree(ctx, val); 50088 if (JS_IsException(val)) 50089 goto exception; 50090 goto concat_primitive; 50091 } else if (cl == JS_CLASS_NUMBER) { 50092 val = JS_ToNumberFree(ctx, val); 50093 if (JS_IsException(val)) 50094 goto exception; 50095 goto concat_primitive; 50096 } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) { 50097 /* This will thow the same error as for the primitive object */ 50098 set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data)); 50099 goto concat_primitive; 50100 } else if (cl == JS_CLASS_RAWJSON) { 50101 JSValue val1; 50102 val1 = JS_GetProperty(ctx, val, JS_ATOM_rawJSON); 50103 if (JS_IsException(val1)) 50104 goto exception; 50105 JS_FreeValue(ctx, val); 50106 val = val1; 50107 goto concat_value; 50108 } 50109 v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); 50110 if (JS_IsException(v)) 50111 goto exception; 50112 if (JS_ToBoolFree(ctx, v)) { 50113 JS_ThrowTypeError(ctx, "circular reference"); 50114 goto exception; 50115 } 50116 indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); 50117 if (JS_IsException(indent1)) 50118 goto exception; 50119 if (!JS_IsEmptyString(jsc->gap)) { 50120 sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); 50121 if (JS_IsException(sep)) 50122 goto exception; 50123 sep1 = js_new_string8(ctx, " "); 50124 if (JS_IsException(sep1)) 50125 goto exception; 50126 } else { 50127 sep = JS_DupValue(ctx, jsc->empty); 50128 sep1 = JS_DupValue(ctx, jsc->empty); 50129 } 50130 v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); 50131 if (check_exception_free(ctx, v)) 50132 goto exception; 50133 ret = JS_IsArray(ctx, val); 50134 if (ret < 0) 50135 goto exception; 50136 if (ret) { 50137 if (js_get_length64(ctx, &len, val)) 50138 goto exception; 50139 string_buffer_putc8(jsc->b, '['); 50140 for(i = 0; i < len; i++) { 50141 if (i > 0) 50142 string_buffer_putc8(jsc->b, ','); 50143 string_buffer_concat_value(jsc->b, sep); 50144 v = JS_GetPropertyInt64(ctx, val, i); 50145 if (JS_IsException(v)) 50146 goto exception; 50147 /* XXX: could do this string conversion only when needed */ 50148 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); 50149 if (JS_IsException(prop)) 50150 goto exception; 50151 v = js_json_check(ctx, jsc, val, v, prop); 50152 JS_FreeValue(ctx, prop); 50153 prop = JS_UNDEFINED; 50154 if (JS_IsException(v)) 50155 goto exception; 50156 if (JS_IsUndefined(v)) 50157 v = JS_NULL; 50158 if (js_json_to_str(ctx, jsc, val, v, indent1)) 50159 goto exception; 50160 } 50161 if (len > 0 && !JS_IsEmptyString(jsc->gap)) { 50162 string_buffer_putc8(jsc->b, '\n'); 50163 string_buffer_concat_value(jsc->b, indent); 50164 } 50165 string_buffer_putc8(jsc->b, ']'); 50166 } else { 50167 if (!JS_IsUndefined(jsc->property_list)) 50168 tab = JS_DupValue(ctx, jsc->property_list); 50169 else 50170 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); 50171 if (JS_IsException(tab)) 50172 goto exception; 50173 if (js_get_length64(ctx, &len, tab)) 50174 goto exception; 50175 string_buffer_putc8(jsc->b, '{'); 50176 has_content = FALSE; 50177 for(i = 0; i < len; i++) { 50178 JS_FreeValue(ctx, prop); 50179 prop = JS_GetPropertyInt64(ctx, tab, i); 50180 if (JS_IsException(prop)) 50181 goto exception; 50182 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); 50183 if (JS_IsException(v)) 50184 goto exception; 50185 v = js_json_check(ctx, jsc, val, v, prop); 50186 if (JS_IsException(v)) 50187 goto exception; 50188 if (!JS_IsUndefined(v)) { 50189 if (has_content) 50190 string_buffer_putc8(jsc->b, ','); 50191 string_buffer_concat_value(jsc->b, sep); 50192 if (JS_ToQuotedString(ctx, jsc->b, prop)) { 50193 JS_FreeValue(ctx, v); 50194 goto exception; 50195 } 50196 string_buffer_putc8(jsc->b, ':'); 50197 string_buffer_concat_value(jsc->b, sep1); 50198 if (js_json_to_str(ctx, jsc, val, v, indent1)) 50199 goto exception; 50200 has_content = TRUE; 50201 } 50202 } 50203 if (has_content && !JS_IsEmptyString(jsc->gap)) { 50204 string_buffer_putc8(jsc->b, '\n'); 50205 string_buffer_concat_value(jsc->b, indent); 50206 } 50207 string_buffer_putc8(jsc->b, '}'); 50208 } 50209 if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) 50210 goto exception; 50211 JS_FreeValue(ctx, val); 50212 JS_FreeValue(ctx, tab); 50213 JS_FreeValue(ctx, sep); 50214 JS_FreeValue(ctx, sep1); 50215 JS_FreeValue(ctx, indent1); 50216 JS_FreeValue(ctx, prop); 50217 return 0; 50218 } 50219 concat_primitive: 50220 switch (JS_VALUE_GET_NORM_TAG(val)) { 50221 case JS_TAG_STRING: 50222 case JS_TAG_STRING_ROPE: 50223 return JS_ToQuotedStringFree(ctx, jsc->b, val); 50224 case JS_TAG_FLOAT64: 50225 if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { 50226 val = JS_NULL; 50227 } 50228 goto concat_value; 50229 case JS_TAG_INT: 50230 case JS_TAG_BOOL: 50231 case JS_TAG_NULL: 50232 concat_value: 50233 return string_buffer_concat_value_free(jsc->b, val); 50234 case JS_TAG_SHORT_BIG_INT: 50235 case JS_TAG_BIG_INT: 50236 /* reject big numbers: use toJSON method to override */ 50237 JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt"); 50238 goto exception; 50239 default: 50240 JS_FreeValue(ctx, val); 50241 return 0; 50242 } 50243 50244 exception: 50245 JS_FreeValue(ctx, val); 50246 JS_FreeValue(ctx, tab); 50247 JS_FreeValue(ctx, sep); 50248 JS_FreeValue(ctx, sep1); 50249 JS_FreeValue(ctx, indent1); 50250 JS_FreeValue(ctx, prop); 50251 return -1; 50252 } 50253 50254 JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, 50255 JSValueConst replacer, JSValueConst space0) 50256 { 50257 StringBuffer b_s; 50258 JSONStringifyContext jsc_s, *jsc = &jsc_s; 50259 JSValue val, v, space, ret, wrapper; 50260 int res; 50261 int64_t i, j, n; 50262 50263 jsc->replacer_func = JS_UNDEFINED; 50264 jsc->stack = JS_UNDEFINED; 50265 jsc->property_list = JS_UNDEFINED; 50266 jsc->gap = JS_UNDEFINED; 50267 jsc->b = &b_s; 50268 jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); 50269 ret = JS_UNDEFINED; 50270 wrapper = JS_UNDEFINED; 50271 50272 string_buffer_init(ctx, jsc->b, 0); 50273 jsc->stack = JS_NewArray(ctx); 50274 if (JS_IsException(jsc->stack)) 50275 goto exception; 50276 if (JS_IsFunction(ctx, replacer)) { 50277 jsc->replacer_func = replacer; 50278 } else { 50279 res = JS_IsArray(ctx, replacer); 50280 if (res < 0) 50281 goto exception; 50282 if (res) { 50283 /* XXX: enumeration is not fully correct */ 50284 jsc->property_list = JS_NewArray(ctx); 50285 if (JS_IsException(jsc->property_list)) 50286 goto exception; 50287 if (js_get_length64(ctx, &n, replacer)) 50288 goto exception; 50289 for (i = j = 0; i < n; i++) { 50290 JSValue present; 50291 v = JS_GetPropertyInt64(ctx, replacer, i); 50292 if (JS_IsException(v)) 50293 goto exception; 50294 if (JS_IsObject(v)) { 50295 JSObject *p = JS_VALUE_GET_OBJ(v); 50296 if (p->class_id == JS_CLASS_STRING || 50297 p->class_id == JS_CLASS_NUMBER) { 50298 v = JS_ToStringFree(ctx, v); 50299 if (JS_IsException(v)) 50300 goto exception; 50301 } else { 50302 JS_FreeValue(ctx, v); 50303 continue; 50304 } 50305 } else if (JS_IsNumber(v)) { 50306 v = JS_ToStringFree(ctx, v); 50307 if (JS_IsException(v)) 50308 goto exception; 50309 } else if (!JS_IsString(v)) { 50310 JS_FreeValue(ctx, v); 50311 continue; 50312 } 50313 present = js_array_includes(ctx, jsc->property_list, 50314 1, (JSValueConst *)&v); 50315 if (JS_IsException(present)) { 50316 JS_FreeValue(ctx, v); 50317 goto exception; 50318 } 50319 if (!JS_ToBoolFree(ctx, present)) { 50320 JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); 50321 } else { 50322 JS_FreeValue(ctx, v); 50323 } 50324 } 50325 } 50326 } 50327 space = JS_DupValue(ctx, space0); 50328 if (JS_IsObject(space)) { 50329 JSObject *p = JS_VALUE_GET_OBJ(space); 50330 if (p->class_id == JS_CLASS_NUMBER) { 50331 space = JS_ToNumberFree(ctx, space); 50332 } else if (p->class_id == JS_CLASS_STRING) { 50333 space = JS_ToStringFree(ctx, space); 50334 } 50335 if (JS_IsException(space)) { 50336 JS_FreeValue(ctx, space); 50337 goto exception; 50338 } 50339 } 50340 if (JS_IsNumber(space)) { 50341 int n; 50342 if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) 50343 goto exception; 50344 jsc->gap = js_new_string8_len(ctx, " ", n); 50345 } else if (JS_IsString(space)) { 50346 JSString *p = JS_VALUE_GET_STRING(space); 50347 jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); 50348 } else { 50349 jsc->gap = JS_DupValue(ctx, jsc->empty); 50350 } 50351 JS_FreeValue(ctx, space); 50352 if (JS_IsException(jsc->gap)) 50353 goto exception; 50354 wrapper = JS_NewObject(ctx); 50355 if (JS_IsException(wrapper)) 50356 goto exception; 50357 if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, 50358 JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) 50359 goto exception; 50360 val = JS_DupValue(ctx, obj); 50361 50362 val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); 50363 if (JS_IsException(val)) 50364 goto exception; 50365 if (JS_IsUndefined(val)) { 50366 ret = JS_UNDEFINED; 50367 goto done1; 50368 } 50369 if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) 50370 goto exception; 50371 50372 ret = string_buffer_end(jsc->b); 50373 goto done; 50374 50375 exception: 50376 ret = JS_EXCEPTION; 50377 done1: 50378 string_buffer_free(jsc->b); 50379 done: 50380 JS_FreeValue(ctx, wrapper); 50381 JS_FreeValue(ctx, jsc->empty); 50382 JS_FreeValue(ctx, jsc->gap); 50383 JS_FreeValue(ctx, jsc->property_list); 50384 JS_FreeValue(ctx, jsc->stack); 50385 return ret; 50386 } 50387 50388 static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, 50389 int argc, JSValueConst *argv) 50390 { 50391 // stringify(val, replacer, space) 50392 return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); 50393 } 50394 50395 static const JSCFunctionListEntry js_json_funcs[] = { 50396 JS_CFUNC_DEF("isRawJSON", 1, js_json_isRawJSON ), 50397 JS_CFUNC_DEF("parse", 2, js_json_parse ), 50398 JS_CFUNC_DEF("rawJSON", 1, js_json_rawJSON ), 50399 JS_CFUNC_DEF("stringify", 3, js_json_stringify ), 50400 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), 50401 }; 50402 50403 static const JSCFunctionListEntry js_json_obj[] = { 50404 JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 50405 }; 50406 50407 int JS_AddIntrinsicJSON(JSContext *ctx) 50408 { 50409 /* add JSON as autoinit object */ 50410 return JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); 50411 } 50412 50413 /* Reflect */ 50414 50415 static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, 50416 int argc, JSValueConst *argv) 50417 { 50418 return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); 50419 } 50420 50421 static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, 50422 int argc, JSValueConst *argv) 50423 { 50424 JSValueConst func, array_arg, new_target; 50425 JSValue *tab, ret; 50426 uint32_t len; 50427 50428 func = argv[0]; 50429 array_arg = argv[1]; 50430 if (argc > 2) { 50431 new_target = argv[2]; 50432 if (!JS_IsConstructor(ctx, new_target)) 50433 return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); 50434 } else { 50435 new_target = func; 50436 } 50437 tab = build_arg_list(ctx, &len, array_arg); 50438 if (!tab) 50439 return JS_EXCEPTION; 50440 ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); 50441 free_arg_list(ctx, tab, len); 50442 return ret; 50443 } 50444 50445 static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, 50446 int argc, JSValueConst *argv) 50447 { 50448 JSValueConst obj; 50449 JSAtom atom; 50450 int ret; 50451 50452 obj = argv[0]; 50453 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 50454 return JS_ThrowTypeErrorNotAnObject(ctx); 50455 atom = JS_ValueToAtom(ctx, argv[1]); 50456 if (unlikely(atom == JS_ATOM_NULL)) 50457 return JS_EXCEPTION; 50458 ret = JS_DeleteProperty(ctx, obj, atom, 0); 50459 JS_FreeAtom(ctx, atom); 50460 if (ret < 0) 50461 return JS_EXCEPTION; 50462 else 50463 return JS_NewBool(ctx, ret); 50464 } 50465 50466 static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, 50467 int argc, JSValueConst *argv) 50468 { 50469 JSValueConst obj, prop, receiver; 50470 JSAtom atom; 50471 JSValue ret; 50472 50473 obj = argv[0]; 50474 prop = argv[1]; 50475 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 50476 return JS_ThrowTypeErrorNotAnObject(ctx); 50477 if (argc > 2) 50478 receiver = argv[2]; 50479 else 50480 receiver = obj; 50481 atom = JS_ValueToAtom(ctx, prop); 50482 if (unlikely(atom == JS_ATOM_NULL)) 50483 return JS_EXCEPTION; 50484 ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); 50485 JS_FreeAtom(ctx, atom); 50486 return ret; 50487 } 50488 50489 static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, 50490 int argc, JSValueConst *argv) 50491 { 50492 JSValueConst obj, prop; 50493 JSAtom atom; 50494 int ret; 50495 50496 obj = argv[0]; 50497 prop = argv[1]; 50498 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 50499 return JS_ThrowTypeErrorNotAnObject(ctx); 50500 atom = JS_ValueToAtom(ctx, prop); 50501 if (unlikely(atom == JS_ATOM_NULL)) 50502 return JS_EXCEPTION; 50503 ret = JS_HasProperty(ctx, obj, atom); 50504 JS_FreeAtom(ctx, atom); 50505 if (ret < 0) 50506 return JS_EXCEPTION; 50507 else 50508 return JS_NewBool(ctx, ret); 50509 } 50510 50511 static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, 50512 int argc, JSValueConst *argv) 50513 { 50514 JSValueConst obj, prop, val, receiver; 50515 int ret; 50516 JSAtom atom; 50517 50518 obj = argv[0]; 50519 prop = argv[1]; 50520 val = argv[2]; 50521 if (argc > 3) 50522 receiver = argv[3]; 50523 else 50524 receiver = obj; 50525 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 50526 return JS_ThrowTypeErrorNotAnObject(ctx); 50527 atom = JS_ValueToAtom(ctx, prop); 50528 if (unlikely(atom == JS_ATOM_NULL)) 50529 return JS_EXCEPTION; 50530 ret = JS_SetPropertyInternal(ctx, obj, atom, 50531 JS_DupValue(ctx, val), receiver, 0); 50532 JS_FreeAtom(ctx, atom); 50533 if (ret < 0) 50534 return JS_EXCEPTION; 50535 else 50536 return JS_NewBool(ctx, ret); 50537 } 50538 50539 static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, 50540 int argc, JSValueConst *argv) 50541 { 50542 int ret; 50543 ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); 50544 if (ret < 0) 50545 return JS_EXCEPTION; 50546 else 50547 return JS_NewBool(ctx, ret); 50548 } 50549 50550 static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, 50551 int argc, JSValueConst *argv) 50552 { 50553 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) 50554 return JS_ThrowTypeErrorNotAnObject(ctx); 50555 return JS_GetOwnPropertyNames2(ctx, argv[0], 50556 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, 50557 JS_ITERATOR_KIND_KEY); 50558 } 50559 50560 static const JSCFunctionListEntry js_reflect_funcs[] = { 50561 JS_CFUNC_DEF("apply", 3, js_reflect_apply ), 50562 JS_CFUNC_DEF("construct", 2, js_reflect_construct ), 50563 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ), 50564 JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ), 50565 JS_CFUNC_DEF("get", 2, js_reflect_get ), 50566 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ), 50567 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ), 50568 JS_CFUNC_DEF("has", 2, js_reflect_has ), 50569 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ), 50570 JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ), 50571 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), 50572 JS_CFUNC_DEF("set", 3, js_reflect_set ), 50573 JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), 50574 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), 50575 }; 50576 50577 static const JSCFunctionListEntry js_reflect_obj[] = { 50578 JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 50579 }; 50580 50581 /* Proxy */ 50582 50583 static void js_proxy_finalizer(JSRuntime *rt, JSValue val) 50584 { 50585 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); 50586 if (s) { 50587 JS_FreeValueRT(rt, s->target); 50588 JS_FreeValueRT(rt, s->handler); 50589 js_free_rt(rt, s); 50590 } 50591 } 50592 50593 static void js_proxy_mark(JSRuntime *rt, JSValueConst val, 50594 JS_MarkFunc *mark_func) 50595 { 50596 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); 50597 if (s) { 50598 JS_MarkValue(rt, s->target, mark_func); 50599 JS_MarkValue(rt, s->handler, mark_func); 50600 } 50601 } 50602 50603 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) 50604 { 50605 return JS_ThrowTypeError(ctx, "revoked proxy"); 50606 } 50607 50608 static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, 50609 JSValueConst obj, JSAtom name) 50610 { 50611 JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); 50612 JSValue method; 50613 50614 /* safer to test recursion in all proxy methods */ 50615 if (js_check_stack_overflow(ctx->rt, 0)) { 50616 JS_ThrowStackOverflow(ctx); 50617 return NULL; 50618 } 50619 50620 /* 's' should never be NULL */ 50621 if (s->is_revoked) { 50622 JS_ThrowTypeErrorRevokedProxy(ctx); 50623 return NULL; 50624 } 50625 method = JS_GetProperty(ctx, s->handler, name); 50626 if (JS_IsException(method)) 50627 return NULL; 50628 if (JS_IsNull(method)) 50629 method = JS_UNDEFINED; 50630 *pmethod = method; 50631 return s; 50632 } 50633 50634 static JSValue js_proxy_get_prototype(JSContext *ctx, JSValueConst obj) 50635 { 50636 JSProxyData *s; 50637 JSValue method, ret, proto1; 50638 int res; 50639 50640 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); 50641 if (!s) 50642 return JS_EXCEPTION; 50643 if (JS_IsUndefined(method)) 50644 return JS_GetPrototype(ctx, s->target); 50645 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 50646 if (JS_IsException(ret)) 50647 return ret; 50648 if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && 50649 JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 50650 goto fail; 50651 } 50652 res = JS_IsExtensible(ctx, s->target); 50653 if (res < 0) { 50654 JS_FreeValue(ctx, ret); 50655 return JS_EXCEPTION; 50656 } 50657 if (!res) { 50658 /* check invariant */ 50659 proto1 = JS_GetPrototype(ctx, s->target); 50660 if (JS_IsException(proto1)) { 50661 JS_FreeValue(ctx, ret); 50662 return JS_EXCEPTION; 50663 } 50664 if (!js_same_value(ctx, proto1, ret)) { 50665 JS_FreeValue(ctx, proto1); 50666 fail: 50667 JS_FreeValue(ctx, ret); 50668 return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); 50669 } 50670 JS_FreeValue(ctx, proto1); 50671 } 50672 return ret; 50673 } 50674 50675 static int js_proxy_set_prototype(JSContext *ctx, JSValueConst obj, 50676 JSValueConst proto_val) 50677 { 50678 JSProxyData *s; 50679 JSValue method, ret, proto1; 50680 JSValueConst args[2]; 50681 BOOL res; 50682 int res2; 50683 50684 s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); 50685 if (!s) 50686 return -1; 50687 if (JS_IsUndefined(method)) 50688 return JS_SetPrototypeInternal(ctx, s->target, proto_val, FALSE); 50689 args[0] = s->target; 50690 args[1] = proto_val; 50691 ret = JS_CallFree(ctx, method, s->handler, 2, args); 50692 if (JS_IsException(ret)) 50693 return -1; 50694 res = JS_ToBoolFree(ctx, ret); 50695 if (!res) 50696 return FALSE; 50697 res2 = JS_IsExtensible(ctx, s->target); 50698 if (res2 < 0) 50699 return -1; 50700 if (!res2) { 50701 proto1 = JS_GetPrototype(ctx, s->target); 50702 if (JS_IsException(proto1)) 50703 return -1; 50704 if (!js_same_value(ctx, proto_val, proto1)) { 50705 JS_FreeValue(ctx, proto1); 50706 JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); 50707 return -1; 50708 } 50709 JS_FreeValue(ctx, proto1); 50710 } 50711 return TRUE; 50712 } 50713 50714 static int js_proxy_is_extensible(JSContext *ctx, JSValueConst obj) 50715 { 50716 JSProxyData *s; 50717 JSValue method, ret; 50718 BOOL res; 50719 int res2; 50720 50721 s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); 50722 if (!s) 50723 return -1; 50724 if (JS_IsUndefined(method)) 50725 return JS_IsExtensible(ctx, s->target); 50726 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 50727 if (JS_IsException(ret)) 50728 return -1; 50729 res = JS_ToBoolFree(ctx, ret); 50730 res2 = JS_IsExtensible(ctx, s->target); 50731 if (res2 < 0) 50732 return res2; 50733 if (res != res2) { 50734 JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); 50735 return -1; 50736 } 50737 return res; 50738 } 50739 50740 static int js_proxy_prevent_extensions(JSContext *ctx, JSValueConst obj) 50741 { 50742 JSProxyData *s; 50743 JSValue method, ret; 50744 BOOL res; 50745 int res2; 50746 50747 s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); 50748 if (!s) 50749 return -1; 50750 if (JS_IsUndefined(method)) 50751 return JS_PreventExtensions(ctx, s->target); 50752 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 50753 if (JS_IsException(ret)) 50754 return -1; 50755 res = JS_ToBoolFree(ctx, ret); 50756 if (res) { 50757 res2 = JS_IsExtensible(ctx, s->target); 50758 if (res2 < 0) 50759 return res2; 50760 if (res2) { 50761 JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); 50762 return -1; 50763 } 50764 } 50765 return res; 50766 } 50767 50768 static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) 50769 { 50770 JSProxyData *s; 50771 JSValue method, ret1, atom_val; 50772 int ret, res; 50773 JSObject *p; 50774 JSValueConst args[2]; 50775 BOOL res2; 50776 50777 s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); 50778 if (!s) 50779 return -1; 50780 if (JS_IsUndefined(method)) 50781 return JS_HasProperty(ctx, s->target, atom); 50782 atom_val = JS_AtomToValue(ctx, atom); 50783 if (JS_IsException(atom_val)) { 50784 JS_FreeValue(ctx, method); 50785 return -1; 50786 } 50787 args[0] = s->target; 50788 args[1] = atom_val; 50789 ret1 = JS_CallFree(ctx, method, s->handler, 2, args); 50790 JS_FreeValue(ctx, atom_val); 50791 if (JS_IsException(ret1)) 50792 return -1; 50793 ret = JS_ToBoolFree(ctx, ret1); 50794 if (!ret) { 50795 JSPropertyDescriptor desc; 50796 p = JS_VALUE_GET_OBJ(s->target); 50797 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 50798 if (res < 0) 50799 return -1; 50800 if (res) { 50801 res2 = !(desc.flags & JS_PROP_CONFIGURABLE); 50802 js_free_desc(ctx, &desc); 50803 if (res2 || !p->extensible) { 50804 JS_ThrowTypeError(ctx, "proxy: inconsistent has"); 50805 return -1; 50806 } 50807 } 50808 } 50809 return ret; 50810 } 50811 50812 static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, 50813 JSValueConst receiver) 50814 { 50815 JSProxyData *s; 50816 JSValue method, ret, atom_val; 50817 int res; 50818 JSValueConst args[3]; 50819 JSPropertyDescriptor desc; 50820 50821 s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); 50822 if (!s) 50823 return JS_EXCEPTION; 50824 /* Note: recursion is possible thru the prototype of s->target */ 50825 if (JS_IsUndefined(method)) 50826 return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); 50827 atom_val = JS_AtomToValue(ctx, atom); 50828 if (JS_IsException(atom_val)) { 50829 JS_FreeValue(ctx, method); 50830 return JS_EXCEPTION; 50831 } 50832 args[0] = s->target; 50833 args[1] = atom_val; 50834 args[2] = receiver; 50835 ret = JS_CallFree(ctx, method, s->handler, 3, args); 50836 JS_FreeValue(ctx, atom_val); 50837 if (JS_IsException(ret)) 50838 return JS_EXCEPTION; 50839 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 50840 if (res < 0) { 50841 JS_FreeValue(ctx, ret); 50842 return JS_EXCEPTION; 50843 } 50844 if (res) { 50845 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { 50846 if (!js_same_value(ctx, desc.value, ret)) { 50847 goto fail; 50848 } 50849 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { 50850 if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { 50851 fail: 50852 js_free_desc(ctx, &desc); 50853 JS_FreeValue(ctx, ret); 50854 return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); 50855 } 50856 } 50857 js_free_desc(ctx, &desc); 50858 } 50859 return ret; 50860 } 50861 50862 static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, 50863 JSValueConst value, JSValueConst receiver, int flags) 50864 { 50865 JSProxyData *s; 50866 JSValue method, ret1, atom_val; 50867 int ret, res; 50868 JSValueConst args[4]; 50869 50870 s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); 50871 if (!s) 50872 return -1; 50873 if (JS_IsUndefined(method)) { 50874 return JS_SetPropertyInternal(ctx, s->target, atom, 50875 JS_DupValue(ctx, value), receiver, 50876 flags); 50877 } 50878 atom_val = JS_AtomToValue(ctx, atom); 50879 if (JS_IsException(atom_val)) { 50880 JS_FreeValue(ctx, method); 50881 return -1; 50882 } 50883 args[0] = s->target; 50884 args[1] = atom_val; 50885 args[2] = value; 50886 args[3] = receiver; 50887 ret1 = JS_CallFree(ctx, method, s->handler, 4, args); 50888 JS_FreeValue(ctx, atom_val); 50889 if (JS_IsException(ret1)) 50890 return -1; 50891 ret = JS_ToBoolFree(ctx, ret1); 50892 if (ret) { 50893 JSPropertyDescriptor desc; 50894 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 50895 if (res < 0) 50896 return -1; 50897 if (res) { 50898 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { 50899 if (!js_same_value(ctx, desc.value, value)) { 50900 goto fail; 50901 } 50902 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { 50903 fail: 50904 js_free_desc(ctx, &desc); 50905 JS_ThrowTypeError(ctx, "proxy: inconsistent set"); 50906 return -1; 50907 } 50908 js_free_desc(ctx, &desc); 50909 } 50910 } else { 50911 if ((flags & JS_PROP_THROW) || 50912 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 50913 JS_ThrowTypeError(ctx, "proxy: cannot set property"); 50914 return -1; 50915 } 50916 } 50917 return ret; 50918 } 50919 50920 static JSValue js_create_desc(JSContext *ctx, JSValueConst val, 50921 JSValueConst getter, JSValueConst setter, 50922 int flags) 50923 { 50924 JSValue ret; 50925 ret = JS_NewObject(ctx); 50926 if (JS_IsException(ret)) 50927 return ret; 50928 if (flags & JS_PROP_HAS_GET) { 50929 JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), 50930 JS_PROP_C_W_E); 50931 } 50932 if (flags & JS_PROP_HAS_SET) { 50933 JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), 50934 JS_PROP_C_W_E); 50935 } 50936 if (flags & JS_PROP_HAS_VALUE) { 50937 JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), 50938 JS_PROP_C_W_E); 50939 } 50940 if (flags & JS_PROP_HAS_WRITABLE) { 50941 JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, 50942 JS_NewBool(ctx, flags & JS_PROP_WRITABLE), 50943 JS_PROP_C_W_E); 50944 } 50945 if (flags & JS_PROP_HAS_ENUMERABLE) { 50946 JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, 50947 JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE), 50948 JS_PROP_C_W_E); 50949 } 50950 if (flags & JS_PROP_HAS_CONFIGURABLE) { 50951 JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, 50952 JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE), 50953 JS_PROP_C_W_E); 50954 } 50955 return ret; 50956 } 50957 50958 static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, 50959 JSValueConst obj, JSAtom prop) 50960 { 50961 JSProxyData *s; 50962 JSValue method, trap_result_obj, prop_val; 50963 int res, target_desc_ret, ret; 50964 JSObject *p; 50965 JSValueConst args[2]; 50966 JSPropertyDescriptor result_desc, target_desc; 50967 50968 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); 50969 if (!s) 50970 return -1; 50971 p = JS_VALUE_GET_OBJ(s->target); 50972 if (JS_IsUndefined(method)) { 50973 return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); 50974 } 50975 prop_val = JS_AtomToValue(ctx, prop); 50976 if (JS_IsException(prop_val)) { 50977 JS_FreeValue(ctx, method); 50978 return -1; 50979 } 50980 args[0] = s->target; 50981 args[1] = prop_val; 50982 trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); 50983 JS_FreeValue(ctx, prop_val); 50984 if (JS_IsException(trap_result_obj)) 50985 return -1; 50986 if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { 50987 JS_FreeValue(ctx, trap_result_obj); 50988 goto fail; 50989 } 50990 target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); 50991 if (target_desc_ret < 0) { 50992 JS_FreeValue(ctx, trap_result_obj); 50993 return -1; 50994 } 50995 if (target_desc_ret) 50996 js_free_desc(ctx, &target_desc); 50997 if (JS_IsUndefined(trap_result_obj)) { 50998 if (target_desc_ret) { 50999 if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) 51000 goto fail; 51001 } 51002 ret = FALSE; 51003 } else { 51004 int flags1, extensible_target; 51005 extensible_target = JS_IsExtensible(ctx, s->target); 51006 if (extensible_target < 0) { 51007 JS_FreeValue(ctx, trap_result_obj); 51008 return -1; 51009 } 51010 res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); 51011 JS_FreeValue(ctx, trap_result_obj); 51012 if (res < 0) 51013 return -1; 51014 51015 /* convert the result_desc.flags to property flags */ 51016 if (result_desc.flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 51017 result_desc.flags |= JS_PROP_GETSET; 51018 } else { 51019 result_desc.flags |= JS_PROP_NORMAL; 51020 } 51021 result_desc.flags &= (JS_PROP_C_W_E | JS_PROP_TMASK); 51022 51023 if (target_desc_ret) { 51024 /* convert result_desc.flags to defineProperty flags */ 51025 flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; 51026 if (result_desc.flags & JS_PROP_GETSET) 51027 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; 51028 else 51029 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; 51030 /* XXX: not complete check: need to compare value & 51031 getter/setter as in defineproperty */ 51032 if (!check_define_prop_flags(target_desc.flags, flags1)) 51033 goto fail1; 51034 } else { 51035 if (!extensible_target) 51036 goto fail1; 51037 } 51038 if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { 51039 if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) 51040 goto fail1; 51041 if ((result_desc.flags & 51042 (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && 51043 target_desc_ret && 51044 (target_desc.flags & JS_PROP_WRITABLE) != 0) { 51045 /* proxy-missing-checks */ 51046 fail1: 51047 js_free_desc(ctx, &result_desc); 51048 fail: 51049 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); 51050 return -1; 51051 } 51052 } 51053 ret = TRUE; 51054 if (pdesc) { 51055 *pdesc = result_desc; 51056 } else { 51057 js_free_desc(ctx, &result_desc); 51058 } 51059 } 51060 return ret; 51061 } 51062 51063 static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, 51064 JSAtom prop, JSValueConst val, 51065 JSValueConst getter, JSValueConst setter, 51066 int flags) 51067 { 51068 JSProxyData *s; 51069 JSValue method, ret1, prop_val, desc_val; 51070 int res, ret; 51071 JSObject *p; 51072 JSValueConst args[3]; 51073 JSPropertyDescriptor desc; 51074 BOOL setting_not_configurable; 51075 51076 s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); 51077 if (!s) 51078 return -1; 51079 if (JS_IsUndefined(method)) { 51080 return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); 51081 } 51082 prop_val = JS_AtomToValue(ctx, prop); 51083 if (JS_IsException(prop_val)) { 51084 JS_FreeValue(ctx, method); 51085 return -1; 51086 } 51087 desc_val = js_create_desc(ctx, val, getter, setter, flags); 51088 if (JS_IsException(desc_val)) { 51089 JS_FreeValue(ctx, prop_val); 51090 JS_FreeValue(ctx, method); 51091 return -1; 51092 } 51093 args[0] = s->target; 51094 args[1] = prop_val; 51095 args[2] = desc_val; 51096 ret1 = JS_CallFree(ctx, method, s->handler, 3, args); 51097 JS_FreeValue(ctx, prop_val); 51098 JS_FreeValue(ctx, desc_val); 51099 if (JS_IsException(ret1)) 51100 return -1; 51101 ret = JS_ToBoolFree(ctx, ret1); 51102 if (!ret) { 51103 if (flags & JS_PROP_THROW) { 51104 JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); 51105 return -1; 51106 } else { 51107 return 0; 51108 } 51109 } 51110 p = JS_VALUE_GET_OBJ(s->target); 51111 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 51112 if (res < 0) 51113 return -1; 51114 setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | 51115 JS_PROP_CONFIGURABLE)) == 51116 JS_PROP_HAS_CONFIGURABLE); 51117 if (!res) { 51118 if (!p->extensible || setting_not_configurable) 51119 goto fail; 51120 } else { 51121 if (!check_define_prop_flags(desc.flags, flags)) 51122 goto fail1; 51123 /* do the missing check from check_define_prop_flags() */ 51124 if (!(desc.flags & JS_PROP_CONFIGURABLE)) { 51125 if ((desc.flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 51126 if ((flags & JS_PROP_HAS_GET) && 51127 !js_same_value(ctx, getter, desc.getter)) { 51128 goto fail1; 51129 } 51130 if ((flags & JS_PROP_HAS_SET) && 51131 !js_same_value(ctx, setter, desc.setter)) { 51132 goto fail1; 51133 } 51134 } else if (!(desc.flags & JS_PROP_WRITABLE)) { 51135 if ((flags & JS_PROP_HAS_VALUE) && 51136 !js_same_value(ctx, val, desc.value)) { 51137 goto fail1; 51138 } 51139 } 51140 } 51141 51142 /* additional checks */ 51143 if ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable) 51144 goto fail1; 51145 51146 if ((desc.flags & JS_PROP_TMASK) != JS_PROP_GETSET && 51147 (desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == JS_PROP_WRITABLE && 51148 (flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { 51149 fail1: 51150 js_free_desc(ctx, &desc); 51151 fail: 51152 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); 51153 return -1; 51154 } 51155 js_free_desc(ctx, &desc); 51156 } 51157 return 1; 51158 } 51159 51160 static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, 51161 JSAtom atom) 51162 { 51163 JSProxyData *s; 51164 JSValue method, ret, atom_val; 51165 int res, res2, is_extensible; 51166 JSValueConst args[2]; 51167 51168 s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); 51169 if (!s) 51170 return -1; 51171 if (JS_IsUndefined(method)) { 51172 return JS_DeleteProperty(ctx, s->target, atom, 0); 51173 } 51174 atom_val = JS_AtomToValue(ctx, atom);; 51175 if (JS_IsException(atom_val)) { 51176 JS_FreeValue(ctx, method); 51177 return -1; 51178 } 51179 args[0] = s->target; 51180 args[1] = atom_val; 51181 ret = JS_CallFree(ctx, method, s->handler, 2, args); 51182 JS_FreeValue(ctx, atom_val); 51183 if (JS_IsException(ret)) 51184 return -1; 51185 res = JS_ToBoolFree(ctx, ret); 51186 if (res) { 51187 JSPropertyDescriptor desc; 51188 res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 51189 if (res2 < 0) 51190 return -1; 51191 if (res2) { 51192 if (!(desc.flags & JS_PROP_CONFIGURABLE)) 51193 goto fail; 51194 is_extensible = JS_IsExtensible(ctx, s->target); 51195 if (is_extensible < 0) 51196 goto fail1; 51197 if (!is_extensible) { 51198 /* proxy-missing-checks */ 51199 fail: 51200 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); 51201 fail1: 51202 js_free_desc(ctx, &desc); 51203 return -1; 51204 } 51205 js_free_desc(ctx, &desc); 51206 } 51207 } 51208 return res; 51209 } 51210 51211 /* return the index of the property or -1 if not found */ 51212 static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) 51213 { 51214 int i; 51215 for(i = 0; i < n; i++) { 51216 if (tab[i].atom == atom) 51217 return i; 51218 } 51219 return -1; 51220 } 51221 51222 static int js_proxy_get_own_property_names(JSContext *ctx, 51223 JSPropertyEnum **ptab, 51224 uint32_t *plen, 51225 JSValueConst obj) 51226 { 51227 JSProxyData *s; 51228 JSValue method, prop_array, val; 51229 uint32_t len, i, len2; 51230 JSPropertyEnum *tab, *tab2; 51231 JSAtom atom; 51232 JSPropertyDescriptor desc; 51233 int res, is_extensible, idx; 51234 51235 s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); 51236 if (!s) 51237 return -1; 51238 if (JS_IsUndefined(method)) { 51239 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, 51240 JS_VALUE_GET_OBJ(s->target), 51241 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); 51242 } 51243 prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 51244 if (JS_IsException(prop_array)) 51245 return -1; 51246 tab = NULL; 51247 len = 0; 51248 tab2 = NULL; 51249 len2 = 0; 51250 if (js_get_length32(ctx, &len, prop_array)) 51251 goto fail; 51252 if (len > 0) { 51253 tab = js_mallocz(ctx, sizeof(tab[0]) * len); 51254 if (!tab) 51255 goto fail; 51256 } 51257 for(i = 0; i < len; i++) { 51258 val = JS_GetPropertyUint32(ctx, prop_array, i); 51259 if (JS_IsException(val)) 51260 goto fail; 51261 if (!JS_IsString(val) && !JS_IsSymbol(val)) { 51262 JS_FreeValue(ctx, val); 51263 JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); 51264 goto fail; 51265 } 51266 atom = JS_ValueToAtom(ctx, val); 51267 JS_FreeValue(ctx, val); 51268 if (atom == JS_ATOM_NULL) 51269 goto fail; 51270 tab[i].atom = atom; 51271 tab[i].is_enumerable = FALSE; /* XXX: redundant? */ 51272 } 51273 51274 /* check duplicate properties (XXX: inefficient, could store the 51275 * properties an a temporary object to use the hash) */ 51276 for(i = 1; i < len; i++) { 51277 if (find_prop_key(tab, i, tab[i].atom) >= 0) { 51278 JS_ThrowTypeError(ctx, "proxy: duplicate property"); 51279 goto fail; 51280 } 51281 } 51282 51283 is_extensible = JS_IsExtensible(ctx, s->target); 51284 if (is_extensible < 0) 51285 goto fail; 51286 51287 /* check if there are non configurable properties */ 51288 if (s->is_revoked) { 51289 JS_ThrowTypeErrorRevokedProxy(ctx); 51290 goto fail; 51291 } 51292 if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), 51293 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) 51294 goto fail; 51295 for(i = 0; i < len2; i++) { 51296 if (s->is_revoked) { 51297 JS_ThrowTypeErrorRevokedProxy(ctx); 51298 goto fail; 51299 } 51300 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), 51301 tab2[i].atom); 51302 if (res < 0) 51303 goto fail; 51304 if (res) { /* safety, property should be found */ 51305 js_free_desc(ctx, &desc); 51306 if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { 51307 idx = find_prop_key(tab, len, tab2[i].atom); 51308 if (idx < 0) { 51309 JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); 51310 goto fail; 51311 } 51312 /* mark the property as found */ 51313 if (!is_extensible) 51314 tab[idx].is_enumerable = TRUE; 51315 } 51316 } 51317 } 51318 if (!is_extensible) { 51319 /* check that all property in 'tab' were checked */ 51320 for(i = 0; i < len; i++) { 51321 if (!tab[i].is_enumerable) { 51322 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); 51323 goto fail; 51324 } 51325 } 51326 } 51327 51328 JS_FreePropertyEnum(ctx, tab2, len2); 51329 JS_FreeValue(ctx, prop_array); 51330 *ptab = tab; 51331 *plen = len; 51332 return 0; 51333 fail: 51334 JS_FreePropertyEnum(ctx, tab2, len2); 51335 JS_FreePropertyEnum(ctx, tab, len); 51336 JS_FreeValue(ctx, prop_array); 51337 return -1; 51338 } 51339 51340 static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, 51341 JSValueConst new_target, 51342 int argc, JSValueConst *argv) 51343 { 51344 JSProxyData *s; 51345 JSValue method, arg_array, ret; 51346 JSValueConst args[3]; 51347 51348 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); 51349 if (!s) 51350 return JS_EXCEPTION; 51351 if (!JS_IsConstructor(ctx, s->target)) 51352 return JS_ThrowTypeErrorNotAConstructor(ctx, s->target); 51353 if (JS_IsUndefined(method)) 51354 return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); 51355 arg_array = js_create_array(ctx, argc, argv); 51356 if (JS_IsException(arg_array)) { 51357 ret = JS_EXCEPTION; 51358 goto fail; 51359 } 51360 args[0] = s->target; 51361 args[1] = arg_array; 51362 args[2] = new_target; 51363 ret = JS_Call(ctx, method, s->handler, 3, args); 51364 if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 51365 JS_FreeValue(ctx, ret); 51366 ret = JS_ThrowTypeErrorNotAnObject(ctx); 51367 } 51368 fail: 51369 JS_FreeValue(ctx, method); 51370 JS_FreeValue(ctx, arg_array); 51371 return ret; 51372 } 51373 51374 static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, 51375 JSValueConst this_obj, 51376 int argc, JSValueConst *argv, int flags) 51377 { 51378 JSProxyData *s; 51379 JSValue method, arg_array, ret; 51380 JSValueConst args[3]; 51381 51382 if (flags & JS_CALL_FLAG_CONSTRUCTOR) 51383 return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); 51384 51385 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); 51386 if (!s) 51387 return JS_EXCEPTION; 51388 if (!s->is_func) { 51389 JS_FreeValue(ctx, method); 51390 return JS_ThrowTypeError(ctx, "not a function"); 51391 } 51392 if (JS_IsUndefined(method)) 51393 return JS_Call(ctx, s->target, this_obj, argc, argv); 51394 arg_array = js_create_array(ctx, argc, argv); 51395 if (JS_IsException(arg_array)) { 51396 ret = JS_EXCEPTION; 51397 goto fail; 51398 } 51399 args[0] = s->target; 51400 args[1] = this_obj; 51401 args[2] = arg_array; 51402 ret = JS_Call(ctx, method, s->handler, 3, args); 51403 fail: 51404 JS_FreeValue(ctx, method); 51405 JS_FreeValue(ctx, arg_array); 51406 return ret; 51407 } 51408 51409 /* `js_resolve_proxy`: resolve the proxy chain 51410 `*pval` is updated with to ultimate proxy target 51411 `throw_exception` controls whether exceptions are thown or not 51412 - return -1 in case of error 51413 - otherwise return 0 51414 */ 51415 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) { 51416 int depth = 0; 51417 JSObject *p; 51418 JSProxyData *s; 51419 51420 while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) { 51421 p = JS_VALUE_GET_OBJ(*pval); 51422 if (p->class_id != JS_CLASS_PROXY) 51423 break; 51424 if (depth++ > 1000) { 51425 if (throw_exception) 51426 JS_ThrowStackOverflow(ctx); 51427 return -1; 51428 } 51429 s = p->u.opaque; 51430 if (s->is_revoked) { 51431 if (throw_exception) 51432 JS_ThrowTypeErrorRevokedProxy(ctx); 51433 return -1; 51434 } 51435 *pval = s->target; 51436 } 51437 return 0; 51438 } 51439 51440 static const JSClassExoticMethods js_proxy_exotic_methods = { 51441 .get_own_property = js_proxy_get_own_property, 51442 .define_own_property = js_proxy_define_own_property, 51443 .delete_property = js_proxy_delete_property, 51444 .get_own_property_names = js_proxy_get_own_property_names, 51445 .has_property = js_proxy_has, 51446 .get_property = js_proxy_get, 51447 .set_property = js_proxy_set, 51448 .get_prototype = js_proxy_get_prototype, 51449 .set_prototype = js_proxy_set_prototype, 51450 .is_extensible = js_proxy_is_extensible, 51451 .prevent_extensions = js_proxy_prevent_extensions, 51452 }; 51453 51454 static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, 51455 int argc, JSValueConst *argv) 51456 { 51457 JSValueConst target, handler; 51458 JSValue obj; 51459 JSProxyData *s; 51460 51461 target = argv[0]; 51462 handler = argv[1]; 51463 if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || 51464 JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) 51465 return JS_ThrowTypeErrorNotAnObject(ctx); 51466 51467 obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); 51468 if (JS_IsException(obj)) 51469 return obj; 51470 s = js_malloc(ctx, sizeof(JSProxyData)); 51471 if (!s) { 51472 JS_FreeValue(ctx, obj); 51473 return JS_EXCEPTION; 51474 } 51475 s->target = JS_DupValue(ctx, target); 51476 s->handler = JS_DupValue(ctx, handler); 51477 s->is_func = JS_IsFunction(ctx, target); 51478 s->is_revoked = FALSE; 51479 JS_SetOpaque(obj, s); 51480 JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); 51481 return obj; 51482 } 51483 51484 static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, 51485 int argc, JSValueConst *argv, int magic, 51486 JSValue *func_data) 51487 { 51488 JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); 51489 if (s) { 51490 /* We do not free the handler and target in case they are 51491 referenced as constants in the C call stack */ 51492 s->is_revoked = TRUE; 51493 JS_FreeValue(ctx, func_data[0]); 51494 func_data[0] = JS_NULL; 51495 } 51496 return JS_UNDEFINED; 51497 } 51498 51499 static JSValue js_proxy_revoke_constructor(JSContext *ctx, 51500 JSValueConst proxy_obj) 51501 { 51502 return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); 51503 } 51504 51505 static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, 51506 int argc, JSValueConst *argv) 51507 { 51508 JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; 51509 51510 proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); 51511 if (JS_IsException(proxy_obj)) 51512 goto fail; 51513 revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); 51514 if (JS_IsException(revoke_obj)) 51515 goto fail; 51516 obj = JS_NewObject(ctx); 51517 if (JS_IsException(obj)) 51518 goto fail; 51519 // XXX: exceptions? 51520 JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); 51521 JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); 51522 return obj; 51523 fail: 51524 JS_FreeValue(ctx, proxy_obj); 51525 JS_FreeValue(ctx, revoke_obj); 51526 return JS_EXCEPTION; 51527 } 51528 51529 static const JSCFunctionListEntry js_proxy_funcs[] = { 51530 JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), 51531 }; 51532 51533 static const JSClassShortDef js_proxy_class_def[] = { 51534 { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ 51535 }; 51536 51537 int JS_AddIntrinsicProxy(JSContext *ctx) 51538 { 51539 JSRuntime *rt = ctx->rt; 51540 JSValue obj1; 51541 51542 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { 51543 if (init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, 51544 countof(js_proxy_class_def))) 51545 return -1; 51546 rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; 51547 rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; 51548 } 51549 51550 /* additional fields: name, length */ 51551 obj1 = JS_NewCFunction3(ctx, js_proxy_constructor, "Proxy", 2, 51552 JS_CFUNC_constructor, 0, 51553 ctx->function_proto, countof(js_proxy_funcs) + 2); 51554 if (JS_IsException(obj1)) 51555 return -1; 51556 JS_SetConstructorBit(ctx, obj1, TRUE); 51557 if (JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, 51558 countof(js_proxy_funcs))) 51559 goto fail; 51560 if (JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", 51561 obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0) 51562 goto fail; 51563 return 0; 51564 fail: 51565 JS_FreeValue(ctx, obj1); 51566 return -1; 51567 } 51568 51569 /* Symbol */ 51570 51571 static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, 51572 int argc, JSValueConst *argv) 51573 { 51574 JSValue str; 51575 JSString *p; 51576 51577 if (!JS_IsUndefined(new_target)) 51578 return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); 51579 if (argc == 0 || JS_IsUndefined(argv[0])) { 51580 p = NULL; 51581 } else { 51582 str = JS_ToString(ctx, argv[0]); 51583 if (JS_IsException(str)) 51584 return JS_EXCEPTION; 51585 p = JS_VALUE_GET_STRING(str); 51586 } 51587 return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); 51588 } 51589 51590 static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) 51591 { 51592 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) 51593 return JS_DupValue(ctx, this_val); 51594 51595 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 51596 JSObject *p = JS_VALUE_GET_OBJ(this_val); 51597 if (p->class_id == JS_CLASS_SYMBOL) { 51598 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) 51599 return JS_DupValue(ctx, p->u.object_data); 51600 } 51601 } 51602 return JS_ThrowTypeError(ctx, "not a symbol"); 51603 } 51604 51605 static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, 51606 int argc, JSValueConst *argv) 51607 { 51608 JSValue val, ret; 51609 val = js_thisSymbolValue(ctx, this_val); 51610 if (JS_IsException(val)) 51611 return val; 51612 /* XXX: use JS_ToStringInternal() with a flags */ 51613 ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); 51614 JS_FreeValue(ctx, val); 51615 return ret; 51616 } 51617 51618 static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, 51619 int argc, JSValueConst *argv) 51620 { 51621 return js_thisSymbolValue(ctx, this_val); 51622 } 51623 51624 static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) 51625 { 51626 JSValue val, ret; 51627 JSAtomStruct *p; 51628 51629 val = js_thisSymbolValue(ctx, this_val); 51630 if (JS_IsException(val)) 51631 return val; 51632 p = JS_VALUE_GET_PTR(val); 51633 if (p->len == 0 && p->is_wide_char != 0) { 51634 ret = JS_UNDEFINED; 51635 } else { 51636 ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); 51637 } 51638 JS_FreeValue(ctx, val); 51639 return ret; 51640 } 51641 51642 static const JSCFunctionListEntry js_symbol_proto_funcs[] = { 51643 JS_CFUNC_DEF("toString", 0, js_symbol_toString ), 51644 JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ), 51645 // XXX: should have writable: false 51646 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ), 51647 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ), 51648 JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), 51649 }; 51650 51651 static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, 51652 int argc, JSValueConst *argv) 51653 { 51654 JSValue str; 51655 51656 str = JS_ToString(ctx, argv[0]); 51657 if (JS_IsException(str)) 51658 return JS_EXCEPTION; 51659 return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); 51660 } 51661 51662 static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, 51663 int argc, JSValueConst *argv) 51664 { 51665 JSAtomStruct *p; 51666 51667 if (!JS_IsSymbol(argv[0])) 51668 return JS_ThrowTypeError(ctx, "not a symbol"); 51669 p = JS_VALUE_GET_PTR(argv[0]); 51670 if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) 51671 return JS_UNDEFINED; 51672 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 51673 } 51674 51675 static const JSCFunctionListEntry js_symbol_funcs[] = { 51676 JS_CFUNC_DEF("for", 1, js_symbol_for ), 51677 JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ), 51678 JS_PROP_ATOM_DEF("toPrimitive", JS_ATOM_Symbol_toPrimitive, 0), 51679 JS_PROP_ATOM_DEF("iterator", JS_ATOM_Symbol_iterator, 0), 51680 JS_PROP_ATOM_DEF("match", JS_ATOM_Symbol_match, 0), 51681 JS_PROP_ATOM_DEF("matchAll", JS_ATOM_Symbol_matchAll, 0), 51682 JS_PROP_ATOM_DEF("replace", JS_ATOM_Symbol_replace, 0), 51683 JS_PROP_ATOM_DEF("search", JS_ATOM_Symbol_search, 0), 51684 JS_PROP_ATOM_DEF("split", JS_ATOM_Symbol_split, 0), 51685 JS_PROP_ATOM_DEF("toStringTag", JS_ATOM_Symbol_toStringTag, 0), 51686 JS_PROP_ATOM_DEF("isConcatSpreadable", JS_ATOM_Symbol_isConcatSpreadable, 0), 51687 JS_PROP_ATOM_DEF("hasInstance", JS_ATOM_Symbol_hasInstance, 0), 51688 JS_PROP_ATOM_DEF("species", JS_ATOM_Symbol_species, 0), 51689 JS_PROP_ATOM_DEF("unscopables", JS_ATOM_Symbol_unscopables, 0), 51690 JS_PROP_ATOM_DEF("asyncIterator", JS_ATOM_Symbol_asyncIterator, 0), 51691 }; 51692 51693 /* Set/Map/WeakSet/WeakMap */ 51694 51695 static BOOL js_weakref_is_target(JSValueConst val) 51696 { 51697 switch (JS_VALUE_GET_TAG(val)) { 51698 case JS_TAG_OBJECT: 51699 return TRUE; 51700 case JS_TAG_SYMBOL: 51701 { 51702 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 51703 if (p->atom_type == JS_ATOM_TYPE_SYMBOL && 51704 p->hash != JS_ATOM_HASH_PRIVATE) 51705 return TRUE; 51706 } 51707 break; 51708 default: 51709 break; 51710 } 51711 return FALSE; 51712 } 51713 51714 /* JS_UNDEFINED is considered as a live weakref */ 51715 /* XXX: add a specific JSWeakRef value type ? */ 51716 static BOOL js_weakref_is_live(JSValueConst val) 51717 { 51718 void *p; 51719 if (JS_IsUndefined(val)) 51720 return TRUE; 51721 p = JS_VALUE_GET_PTR(val); 51722 return (js_rc(p)->ref_count != 0); 51723 } 51724 51725 /* 'val' can be JS_UNDEFINED */ 51726 static void js_weakref_free(JSRuntime *rt, JSValue val) 51727 { 51728 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { 51729 JSObject *p = JS_VALUE_GET_OBJ(val); 51730 assert(p->weakref_count >= 1); 51731 p->weakref_count--; 51732 /* 'mark' is tested to avoid freeing the object structure when 51733 it is about to be freed in a cycle or in 51734 free_zero_refcount() */ 51735 if (p->weakref_count == 0 && js_rc(p)->ref_count == 0 && 51736 js_rc(p)->mark == 0) { 51737 js_free_rt(rt, p); 51738 } 51739 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) { 51740 JSString *p = JS_VALUE_GET_STRING(val); 51741 assert(p->hash >= 1); 51742 p->hash--; 51743 if (p->hash == 0 && js_rc(p)->ref_count == 0) { 51744 /* can remove the dummy structure */ 51745 js_free_rt(rt, p); 51746 } 51747 } 51748 } 51749 51750 /* val must be an object, a symbol or undefined (see 51751 js_weakref_is_target). */ 51752 static JSValue js_weakref_new(JSContext *ctx, JSValueConst val) 51753 { 51754 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { 51755 JSObject *p = JS_VALUE_GET_OBJ(val); 51756 p->weakref_count++; 51757 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) { 51758 JSString *p = JS_VALUE_GET_STRING(val); 51759 /* XXX: could return an exception if too many references */ 51760 assert(p->hash < JS_ATOM_HASH_MASK - 2); 51761 p->hash++; 51762 } else { 51763 assert(JS_IsUndefined(val)); 51764 } 51765 return (JSValue)val; 51766 } 51767 51768 #define MAGIC_SET (1 << 0) 51769 #define MAGIC_WEAK (1 << 1) 51770 51771 static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, 51772 int argc, JSValueConst *argv, int magic) 51773 { 51774 JSMapState *s; 51775 JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; 51776 JSValueConst arr; 51777 BOOL is_set, is_weak; 51778 51779 is_set = magic & MAGIC_SET; 51780 is_weak = ((magic & MAGIC_WEAK) != 0); 51781 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); 51782 if (JS_IsException(obj)) 51783 return JS_EXCEPTION; 51784 s = js_mallocz(ctx, sizeof(*s)); 51785 if (!s) 51786 goto fail; 51787 init_list_head(&s->records); 51788 s->is_weak = is_weak; 51789 if (is_weak) { 51790 s->weakref_header.weakref_type = JS_WEAKREF_TYPE_MAP; 51791 list_add_tail(&s->weakref_header.link, &ctx->rt->weakref_list); 51792 } 51793 JS_SetOpaque(obj, s); 51794 s->hash_bits = 1; 51795 s->hash_size = 1U << s->hash_bits; 51796 s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size); 51797 if (!s->hash_table) 51798 goto fail; 51799 s->record_count_threshold = 4; 51800 51801 arr = JS_UNDEFINED; 51802 if (argc > 0) 51803 arr = argv[0]; 51804 if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { 51805 JSValue item, ret; 51806 BOOL done; 51807 51808 adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); 51809 if (JS_IsException(adder)) 51810 goto fail; 51811 if (!JS_IsFunction(ctx, adder)) { 51812 JS_ThrowTypeError(ctx, "set/add is not a function"); 51813 goto fail; 51814 } 51815 51816 iter = JS_GetIterator(ctx, arr, FALSE); 51817 if (JS_IsException(iter)) 51818 goto fail; 51819 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 51820 if (JS_IsException(next_method)) 51821 goto fail; 51822 51823 for(;;) { 51824 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 51825 if (JS_IsException(item)) 51826 goto fail; 51827 if (done) 51828 break; 51829 if (is_set) { 51830 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); 51831 if (JS_IsException(ret)) { 51832 JS_FreeValue(ctx, item); 51833 goto fail_close; 51834 } 51835 } else { 51836 JSValue key, value; 51837 JSValueConst args[2]; 51838 key = JS_UNDEFINED; 51839 value = JS_UNDEFINED; 51840 if (!JS_IsObject(item)) { 51841 JS_ThrowTypeErrorNotAnObject(ctx); 51842 goto fail1; 51843 } 51844 key = JS_GetPropertyUint32(ctx, item, 0); 51845 if (JS_IsException(key)) 51846 goto fail1; 51847 value = JS_GetPropertyUint32(ctx, item, 1); 51848 if (JS_IsException(value)) 51849 goto fail1; 51850 args[0] = key; 51851 args[1] = value; 51852 ret = JS_Call(ctx, adder, obj, 2, args); 51853 if (JS_IsException(ret)) { 51854 fail1: 51855 JS_FreeValue(ctx, item); 51856 JS_FreeValue(ctx, key); 51857 JS_FreeValue(ctx, value); 51858 goto fail_close; 51859 } 51860 JS_FreeValue(ctx, key); 51861 JS_FreeValue(ctx, value); 51862 } 51863 JS_FreeValue(ctx, ret); 51864 JS_FreeValue(ctx, item); 51865 } 51866 JS_FreeValue(ctx, next_method); 51867 JS_FreeValue(ctx, iter); 51868 JS_FreeValue(ctx, adder); 51869 } 51870 return obj; 51871 fail_close: 51872 /* close the iterator object, preserving pending exception */ 51873 JS_IteratorClose(ctx, iter, TRUE); 51874 fail: 51875 JS_FreeValue(ctx, next_method); 51876 JS_FreeValue(ctx, iter); 51877 JS_FreeValue(ctx, adder); 51878 JS_FreeValue(ctx, obj); 51879 return JS_EXCEPTION; 51880 } 51881 51882 /* XXX: could normalize strings to speed up comparison */ 51883 static JSValue map_normalize_key(JSContext *ctx, JSValue key) 51884 { 51885 uint32_t tag = JS_VALUE_GET_TAG(key); 51886 /* convert -0.0 to +0.0 */ 51887 if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { 51888 key = JS_NewInt32(ctx, 0); 51889 } 51890 return key; 51891 } 51892 51893 static JSValueConst map_normalize_key_const(JSContext *ctx, JSValueConst key) 51894 { 51895 return (JSValueConst)map_normalize_key(ctx, (JSValue)key); 51896 } 51897 51898 /* hash multipliers, same as the Linux kernel (see Knuth vol 3, 51899 section 6.4, exercise 9) */ 51900 #define HASH_MUL32 0x61C88647 51901 #define HASH_MUL64 UINT64_C(0x61C8864680B583EB) 51902 51903 static uint32_t map_hash32(uint32_t a, int hash_bits) 51904 { 51905 return (a * HASH_MUL32) >> (32 - hash_bits); 51906 } 51907 51908 static uint32_t map_hash64(uint64_t a, int hash_bits) 51909 { 51910 return (a * HASH_MUL64) >> (64 - hash_bits); 51911 } 51912 51913 static uint32_t map_hash_pointer(uintptr_t a, int hash_bits) 51914 { 51915 #ifdef JS_PTR64 51916 return map_hash64(a, hash_bits); 51917 #else 51918 return map_hash32(a, hash_bits); 51919 #endif 51920 } 51921 51922 /* XXX: better hash ? */ 51923 /* precondition: 1 <= hash_bits <= 32 */ 51924 static uint32_t map_hash_key(JSValueConst key, int hash_bits) 51925 { 51926 uint32_t tag = JS_VALUE_GET_NORM_TAG(key); 51927 uint32_t h; 51928 double d; 51929 JSBigInt *p; 51930 JSBigIntBuf buf; 51931 51932 switch(tag) { 51933 case JS_TAG_BOOL: 51934 h = map_hash32(JS_VALUE_GET_INT(key) ^ JS_TAG_BOOL, hash_bits); 51935 break; 51936 case JS_TAG_STRING: 51937 h = map_hash32(hash_string(JS_VALUE_GET_STRING(key), 0) ^ JS_TAG_STRING, hash_bits); 51938 break; 51939 case JS_TAG_STRING_ROPE: 51940 h = map_hash32(hash_string_rope(key, 0) ^ JS_TAG_STRING, hash_bits); 51941 break; 51942 case JS_TAG_OBJECT: 51943 case JS_TAG_SYMBOL: 51944 h = map_hash_pointer((uintptr_t)JS_VALUE_GET_PTR(key) ^ tag, hash_bits); 51945 break; 51946 case JS_TAG_INT: 51947 d = JS_VALUE_GET_INT(key); 51948 goto hash_float64; 51949 case JS_TAG_FLOAT64: 51950 d = JS_VALUE_GET_FLOAT64(key); 51951 /* normalize the NaN */ 51952 if (isnan(d)) 51953 d = JS_FLOAT64_NAN; 51954 hash_float64: 51955 h = map_hash64(float64_as_uint64(d) ^ JS_TAG_FLOAT64, hash_bits); 51956 break; 51957 case JS_TAG_SHORT_BIG_INT: 51958 p = js_bigint_set_short(&buf, key); 51959 goto hash_bigint; 51960 case JS_TAG_BIG_INT: 51961 p = JS_VALUE_GET_PTR(key); 51962 hash_bigint: 51963 { 51964 int i; 51965 h = 1; 51966 for(i = p->len - 1; i >= 0; i--) { 51967 h = h * 263 + p->tab[i]; 51968 } 51969 /* the final step is necessary otherwise h mod n only 51970 depends of p->tab[i] mod n */ 51971 h = map_hash32(h ^ JS_TAG_BIG_INT, hash_bits); 51972 } 51973 break; 51974 default: 51975 h = 0; 51976 break; 51977 } 51978 return h; 51979 } 51980 51981 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, 51982 JSValueConst key) 51983 { 51984 JSMapRecord *mr; 51985 uint32_t h; 51986 h = map_hash_key(key, s->hash_bits); 51987 for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) { 51988 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { 51989 /* cannot match */ 51990 } else { 51991 if (js_same_value_zero(ctx, mr->key, key)) 51992 return mr; 51993 } 51994 } 51995 return NULL; 51996 } 51997 51998 static void map_hash_resize(JSContext *ctx, JSMapState *s) 51999 { 52000 uint32_t new_hash_size, h; 52001 int new_hash_bits; 52002 struct list_head *el; 52003 JSMapRecord *mr, **new_hash_table; 52004 52005 /* XXX: no reporting of memory allocation failure */ 52006 new_hash_bits = min_int(s->hash_bits + 1, 31); 52007 new_hash_size = 1U << new_hash_bits; 52008 new_hash_table = js_realloc(ctx, s->hash_table, 52009 sizeof(new_hash_table[0]) * new_hash_size); 52010 if (!new_hash_table) 52011 return; 52012 52013 memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size); 52014 52015 list_for_each(el, &s->records) { 52016 mr = list_entry(el, JSMapRecord, link); 52017 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { 52018 } else { 52019 h = map_hash_key(mr->key, new_hash_bits); 52020 mr->hash_next = new_hash_table[h]; 52021 new_hash_table[h] = mr; 52022 } 52023 } 52024 s->hash_table = new_hash_table; 52025 s->hash_bits = new_hash_bits; 52026 s->hash_size = new_hash_size; 52027 s->record_count_threshold = new_hash_size * 2; 52028 } 52029 52030 static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, 52031 JSValueConst key) 52032 { 52033 uint32_t h; 52034 JSMapRecord *mr; 52035 52036 mr = js_malloc(ctx, sizeof(*mr)); 52037 if (!mr) 52038 return NULL; 52039 mr->ref_count = 1; 52040 mr->empty = FALSE; 52041 if (s->is_weak) { 52042 mr->key = js_weakref_new(ctx, key); 52043 } else { 52044 mr->key = JS_DupValue(ctx, key); 52045 } 52046 h = map_hash_key(key, s->hash_bits); 52047 mr->hash_next = s->hash_table[h]; 52048 s->hash_table[h] = mr; 52049 list_add_tail(&mr->link, &s->records); 52050 s->record_count++; 52051 if (s->record_count >= s->record_count_threshold) { 52052 map_hash_resize(ctx, s); 52053 } 52054 return mr; 52055 } 52056 52057 static JSMapRecord *set_add_record(JSContext *ctx, JSMapState *s, 52058 JSValueConst key) 52059 { 52060 JSMapRecord *mr; 52061 mr = map_add_record(ctx, s, key); 52062 if (!mr) 52063 return NULL; 52064 mr->value = JS_UNDEFINED; 52065 return mr; 52066 } 52067 52068 /* warning: the record must be removed from the hash table before */ 52069 static void map_delete_record_internal(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) 52070 { 52071 if (mr->empty) 52072 return; 52073 52074 if (s->is_weak) { 52075 js_weakref_free(rt, mr->key); 52076 } else { 52077 JS_FreeValueRT(rt, mr->key); 52078 } 52079 JS_FreeValueRT(rt, mr->value); 52080 if (--mr->ref_count == 0) { 52081 list_del(&mr->link); 52082 js_free_rt(rt, mr); 52083 } else { 52084 /* keep a zombie record for iterators */ 52085 mr->empty = TRUE; 52086 mr->key = JS_UNDEFINED; 52087 mr->value = JS_UNDEFINED; 52088 } 52089 s->record_count--; 52090 } 52091 52092 static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) 52093 { 52094 if (--mr->ref_count == 0) { 52095 /* the record can be safely removed */ 52096 assert(mr->empty); 52097 list_del(&mr->link); 52098 js_free_rt(rt, mr); 52099 } 52100 } 52101 52102 static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh) 52103 { 52104 JSMapState *s = container_of(wh, JSMapState, weakref_header); 52105 struct list_head *el, *el1; 52106 JSMapRecord *mr1, **pmr; 52107 uint32_t h; 52108 52109 list_for_each_safe(el, el1, &s->records) { 52110 JSMapRecord *mr = list_entry(el, JSMapRecord, link); 52111 if (!js_weakref_is_live(mr->key)) { 52112 52113 /* even if key is not live it can be hashed as a pointer */ 52114 h = map_hash_key(mr->key, s->hash_bits); 52115 pmr = &s->hash_table[h]; 52116 for(;;) { 52117 mr1 = *pmr; 52118 /* the entry may already be removed from the hash 52119 table if the map was resized */ 52120 if (mr1 == NULL) 52121 goto done; 52122 if (mr1 == mr) 52123 break; 52124 pmr = &mr1->hash_next; 52125 } 52126 /* remove from the hash table */ 52127 *pmr = mr1->hash_next; 52128 done: 52129 map_delete_record_internal(rt, s, mr); 52130 } 52131 } 52132 } 52133 52134 static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, 52135 int argc, JSValueConst *argv, int magic) 52136 { 52137 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52138 JSMapRecord *mr; 52139 JSValueConst key, value; 52140 52141 if (!s) 52142 return JS_EXCEPTION; 52143 key = map_normalize_key_const(ctx, argv[0]); 52144 if (s->is_weak && !js_weakref_is_target(key)) 52145 return JS_ThrowTypeError(ctx, "invalid value used as %s key", (magic & MAGIC_SET) ? "WeakSet" : "WeakMap"); 52146 if (magic & MAGIC_SET) 52147 value = JS_UNDEFINED; 52148 else 52149 value = argv[1]; 52150 mr = map_find_record(ctx, s, key); 52151 if (mr) { 52152 JS_FreeValue(ctx, mr->value); 52153 } else { 52154 mr = map_add_record(ctx, s, key); 52155 if (!mr) 52156 return JS_EXCEPTION; 52157 } 52158 mr->value = JS_DupValue(ctx, value); 52159 return JS_DupValue(ctx, this_val); 52160 } 52161 52162 static JSValue js_map_get(JSContext *ctx, JSValueConst this_val, 52163 int argc, JSValueConst *argv, int magic) 52164 { 52165 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52166 JSMapRecord *mr; 52167 JSValueConst key; 52168 52169 if (!s) 52170 return JS_EXCEPTION; 52171 key = map_normalize_key_const(ctx, argv[0]); 52172 mr = map_find_record(ctx, s, key); 52173 if (!mr) 52174 return JS_UNDEFINED; 52175 else 52176 return JS_DupValue(ctx, mr->value); 52177 } 52178 52179 /* return JS_TRUE or JS_FALSE */ 52180 static JSValue map_delete_record(JSContext *ctx, JSMapState *s, JSValueConst key) 52181 { 52182 JSMapRecord *mr, **pmr; 52183 uint32_t h; 52184 52185 key = map_normalize_key_const(ctx, key); 52186 52187 h = map_hash_key(key, s->hash_bits); 52188 pmr = &s->hash_table[h]; 52189 for(;;) { 52190 mr = *pmr; 52191 if (mr == NULL) 52192 return JS_FALSE; 52193 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { 52194 /* not valid */ 52195 } else { 52196 if (js_same_value_zero(ctx, mr->key, key)) 52197 break; 52198 } 52199 pmr = &mr->hash_next; 52200 } 52201 52202 /* remove from the hash table */ 52203 *pmr = mr->hash_next; 52204 52205 map_delete_record_internal(ctx->rt, s, mr); 52206 return JS_TRUE; 52207 } 52208 52209 static JSValue js_map_getOrInsert(JSContext *ctx, JSValueConst this_val, 52210 int argc, JSValueConst *argv, int magic) 52211 { 52212 BOOL computed = magic & 1; 52213 JSClassID class_id = magic >> 1; 52214 JSMapState *s = JS_GetOpaque2(ctx, this_val, class_id); 52215 JSMapRecord *mr; 52216 JSValueConst key; 52217 JSValue value; 52218 52219 if (!s) 52220 return JS_EXCEPTION; 52221 if (computed && !JS_IsFunction(ctx, argv[1])) 52222 return JS_ThrowTypeError(ctx, "not a function"); 52223 key = map_normalize_key_const(ctx, argv[0]); 52224 if (s->is_weak && !js_weakref_is_target(key)) 52225 return JS_ThrowTypeError(ctx, "invalid value used as WeakMap key"); 52226 mr = map_find_record(ctx, s, key); 52227 if (!mr) { 52228 if (computed) { 52229 value = JS_Call(ctx, argv[1], JS_UNDEFINED, 1, &key); 52230 if (JS_IsException(value)) 52231 return JS_EXCEPTION; 52232 map_delete_record(ctx, s, key); 52233 } else { 52234 value = JS_DupValue(ctx, argv[1]); 52235 } 52236 mr = map_add_record(ctx, s, key); 52237 if (!mr) { 52238 JS_FreeValue(ctx, value); 52239 return JS_EXCEPTION; 52240 } 52241 mr->value = value; 52242 } 52243 return JS_DupValue(ctx, mr->value); 52244 } 52245 52246 static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, 52247 int argc, JSValueConst *argv, int magic) 52248 { 52249 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52250 JSMapRecord *mr; 52251 JSValueConst key; 52252 52253 if (!s) 52254 return JS_EXCEPTION; 52255 key = map_normalize_key_const(ctx, argv[0]); 52256 mr = map_find_record(ctx, s, key); 52257 return JS_NewBool(ctx, mr != NULL); 52258 } 52259 52260 static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, 52261 int argc, JSValueConst *argv, int magic) 52262 { 52263 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52264 if (!s) 52265 return JS_EXCEPTION; 52266 return map_delete_record(ctx, s, argv[0]); 52267 } 52268 52269 static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, 52270 int argc, JSValueConst *argv, int magic) 52271 { 52272 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52273 struct list_head *el, *el1; 52274 JSMapRecord *mr; 52275 52276 if (!s) 52277 return JS_EXCEPTION; 52278 52279 /* remove from the hash table */ 52280 memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size); 52281 52282 list_for_each_safe(el, el1, &s->records) { 52283 mr = list_entry(el, JSMapRecord, link); 52284 map_delete_record_internal(ctx->rt, s, mr); 52285 } 52286 return JS_UNDEFINED; 52287 } 52288 52289 static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) 52290 { 52291 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52292 if (!s) 52293 return JS_EXCEPTION; 52294 return JS_NewUint32(ctx, s->record_count); 52295 } 52296 52297 static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, 52298 int argc, JSValueConst *argv, int magic) 52299 { 52300 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52301 JSValueConst func, this_arg; 52302 JSValue ret, args[3]; 52303 struct list_head *el; 52304 JSMapRecord *mr; 52305 52306 if (!s) 52307 return JS_EXCEPTION; 52308 func = argv[0]; 52309 if (argc > 1) 52310 this_arg = argv[1]; 52311 else 52312 this_arg = JS_UNDEFINED; 52313 if (check_function(ctx, func)) 52314 return JS_EXCEPTION; 52315 /* Note: the list can be modified while traversing it, but the 52316 current element is locked */ 52317 el = s->records.next; 52318 while (el != &s->records) { 52319 mr = list_entry(el, JSMapRecord, link); 52320 if (!mr->empty) { 52321 mr->ref_count++; 52322 /* must duplicate in case the record is deleted */ 52323 args[1] = JS_DupValue(ctx, mr->key); 52324 if (magic) 52325 args[0] = args[1]; 52326 else 52327 args[0] = JS_DupValue(ctx, mr->value); 52328 args[2] = (JSValue)this_val; 52329 ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); 52330 JS_FreeValue(ctx, args[0]); 52331 if (!magic) 52332 JS_FreeValue(ctx, args[1]); 52333 el = el->next; 52334 map_decref_record(ctx->rt, mr); 52335 if (JS_IsException(ret)) 52336 return ret; 52337 JS_FreeValue(ctx, ret); 52338 } else { 52339 el = el->next; 52340 } 52341 } 52342 return JS_UNDEFINED; 52343 } 52344 52345 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, 52346 int argc, JSValueConst *argv, int is_map) 52347 { 52348 JSValueConst cb, args[2]; 52349 JSValue res, iter, next, groups, key, v, prop; 52350 JSAtom key_atom = JS_ATOM_NULL; 52351 int64_t idx; 52352 BOOL done; 52353 52354 // "is function?" check must be observed before argv[0] is accessed 52355 cb = argv[1]; 52356 if (check_function(ctx, cb)) 52357 return JS_EXCEPTION; 52358 52359 iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); 52360 if (JS_IsException(iter)) 52361 return JS_EXCEPTION; 52362 52363 key = JS_UNDEFINED; 52364 key_atom = JS_ATOM_NULL; 52365 v = JS_UNDEFINED; 52366 prop = JS_UNDEFINED; 52367 groups = JS_UNDEFINED; 52368 52369 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 52370 if (JS_IsException(next)) 52371 goto exception; 52372 52373 if (is_map) { 52374 groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0); 52375 } else { 52376 groups = JS_NewObjectProto(ctx, JS_NULL); 52377 } 52378 if (JS_IsException(groups)) 52379 goto exception; 52380 52381 for (idx = 0; ; idx++) { 52382 if (idx >= MAX_SAFE_INTEGER) { 52383 JS_ThrowTypeError(ctx, "too many elements"); 52384 goto iterator_close_exception; 52385 } 52386 v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 52387 if (JS_IsException(v)) 52388 goto exception; 52389 if (done) 52390 break; // v is JS_UNDEFINED 52391 52392 args[0] = v; 52393 args[1] = JS_NewInt64(ctx, idx); 52394 key = JS_Call(ctx, cb, ctx->global_obj, 2, args); 52395 if (JS_IsException(key)) 52396 goto iterator_close_exception; 52397 52398 if (is_map) { 52399 prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0); 52400 } else { 52401 key_atom = JS_ValueToAtom(ctx, key); 52402 JS_FreeValue(ctx, key); 52403 key = JS_UNDEFINED; 52404 if (key_atom == JS_ATOM_NULL) 52405 goto iterator_close_exception; 52406 prop = JS_GetProperty(ctx, groups, key_atom); 52407 } 52408 if (JS_IsException(prop)) 52409 goto exception; 52410 52411 if (JS_IsUndefined(prop)) { 52412 prop = JS_NewArray(ctx); 52413 if (JS_IsException(prop)) 52414 goto exception; 52415 if (is_map) { 52416 args[0] = key; 52417 args[1] = prop; 52418 res = js_map_set(ctx, groups, 2, args, 0); 52419 if (JS_IsException(res)) 52420 goto exception; 52421 JS_FreeValue(ctx, res); 52422 } else { 52423 prop = JS_DupValue(ctx, prop); 52424 if (JS_DefinePropertyValue(ctx, groups, key_atom, prop, 52425 JS_PROP_C_W_E) < 0) { 52426 goto exception; 52427 } 52428 } 52429 } 52430 res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0); 52431 if (JS_IsException(res)) 52432 goto exception; 52433 // res is an int64 52434 52435 JS_FreeValue(ctx, prop); 52436 JS_FreeValue(ctx, key); 52437 JS_FreeAtom(ctx, key_atom); 52438 JS_FreeValue(ctx, v); 52439 prop = JS_UNDEFINED; 52440 key = JS_UNDEFINED; 52441 key_atom = JS_ATOM_NULL; 52442 v = JS_UNDEFINED; 52443 } 52444 52445 JS_FreeValue(ctx, iter); 52446 JS_FreeValue(ctx, next); 52447 return groups; 52448 52449 iterator_close_exception: 52450 JS_IteratorClose(ctx, iter, TRUE); 52451 exception: 52452 JS_FreeAtom(ctx, key_atom); 52453 JS_FreeValue(ctx, prop); 52454 JS_FreeValue(ctx, key); 52455 JS_FreeValue(ctx, v); 52456 JS_FreeValue(ctx, groups); 52457 JS_FreeValue(ctx, iter); 52458 JS_FreeValue(ctx, next); 52459 return JS_EXCEPTION; 52460 } 52461 52462 static void js_map_finalizer(JSRuntime *rt, JSValue val) 52463 { 52464 JSObject *p; 52465 JSMapState *s; 52466 struct list_head *el, *el1; 52467 JSMapRecord *mr; 52468 52469 p = JS_VALUE_GET_OBJ(val); 52470 s = p->u.map_state; 52471 if (s) { 52472 /* if the object is deleted we are sure that no iterator is 52473 using it */ 52474 list_for_each_safe(el, el1, &s->records) { 52475 mr = list_entry(el, JSMapRecord, link); 52476 if (!mr->empty) { 52477 if (s->is_weak) 52478 js_weakref_free(rt, mr->key); 52479 else 52480 JS_FreeValueRT(rt, mr->key); 52481 JS_FreeValueRT(rt, mr->value); 52482 } 52483 js_free_rt(rt, mr); 52484 } 52485 js_free_rt(rt, s->hash_table); 52486 if (s->is_weak) { 52487 list_del(&s->weakref_header.link); 52488 } 52489 js_free_rt(rt, s); 52490 } 52491 } 52492 52493 static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) 52494 { 52495 JSObject *p = JS_VALUE_GET_OBJ(val); 52496 JSMapState *s; 52497 struct list_head *el; 52498 JSMapRecord *mr; 52499 52500 s = p->u.map_state; 52501 if (s) { 52502 list_for_each(el, &s->records) { 52503 mr = list_entry(el, JSMapRecord, link); 52504 if (!s->is_weak) 52505 JS_MarkValue(rt, mr->key, mark_func); 52506 JS_MarkValue(rt, mr->value, mark_func); 52507 } 52508 } 52509 } 52510 52511 /* Map Iterator */ 52512 52513 typedef struct JSMapIteratorData { 52514 JSValue obj; 52515 JSIteratorKindEnum kind; 52516 JSMapRecord *cur_record; 52517 } JSMapIteratorData; 52518 52519 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) 52520 { 52521 JSObject *p; 52522 JSMapIteratorData *it; 52523 52524 p = JS_VALUE_GET_OBJ(val); 52525 it = p->u.map_iterator_data; 52526 if (it) { 52527 /* During the GC sweep phase the Map finalizer may be 52528 called before the Map iterator finalizer */ 52529 if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { 52530 map_decref_record(rt, it->cur_record); 52531 } 52532 JS_FreeValueRT(rt, it->obj); 52533 js_free_rt(rt, it); 52534 } 52535 } 52536 52537 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, 52538 JS_MarkFunc *mark_func) 52539 { 52540 JSObject *p = JS_VALUE_GET_OBJ(val); 52541 JSMapIteratorData *it; 52542 it = p->u.map_iterator_data; 52543 if (it) { 52544 /* the record is already marked by the object */ 52545 JS_MarkValue(rt, it->obj, mark_func); 52546 } 52547 } 52548 52549 static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, 52550 int argc, JSValueConst *argv, int magic) 52551 { 52552 JSIteratorKindEnum kind; 52553 JSMapState *s; 52554 JSMapIteratorData *it; 52555 JSValue enum_obj; 52556 52557 kind = magic >> 2; 52558 magic &= 3; 52559 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 52560 if (!s) 52561 return JS_EXCEPTION; 52562 enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); 52563 if (JS_IsException(enum_obj)) 52564 goto fail; 52565 it = js_malloc(ctx, sizeof(*it)); 52566 if (!it) { 52567 JS_FreeValue(ctx, enum_obj); 52568 goto fail; 52569 } 52570 it->obj = JS_DupValue(ctx, this_val); 52571 it->kind = kind; 52572 it->cur_record = NULL; 52573 JS_SetOpaque(enum_obj, it); 52574 return enum_obj; 52575 fail: 52576 return JS_EXCEPTION; 52577 } 52578 52579 static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, 52580 int argc, JSValueConst *argv, 52581 BOOL *pdone, int magic) 52582 { 52583 JSMapIteratorData *it; 52584 JSMapState *s; 52585 JSMapRecord *mr; 52586 struct list_head *el; 52587 52588 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); 52589 if (!it) { 52590 *pdone = FALSE; 52591 return JS_EXCEPTION; 52592 } 52593 if (JS_IsUndefined(it->obj)) 52594 goto done; 52595 s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); 52596 assert(s != NULL); 52597 if (!it->cur_record) { 52598 el = s->records.next; 52599 } else { 52600 mr = it->cur_record; 52601 el = mr->link.next; 52602 map_decref_record(ctx->rt, mr); /* the record can be freed here */ 52603 } 52604 for(;;) { 52605 if (el == &s->records) { 52606 /* no more record */ 52607 it->cur_record = NULL; 52608 JS_FreeValue(ctx, it->obj); 52609 it->obj = JS_UNDEFINED; 52610 done: 52611 /* end of enumeration */ 52612 *pdone = TRUE; 52613 return JS_UNDEFINED; 52614 } 52615 mr = list_entry(el, JSMapRecord, link); 52616 if (!mr->empty) 52617 break; 52618 /* get the next record */ 52619 el = mr->link.next; 52620 } 52621 52622 /* lock the record so that it won't be freed */ 52623 mr->ref_count++; 52624 it->cur_record = mr; 52625 *pdone = FALSE; 52626 52627 if (it->kind == JS_ITERATOR_KIND_KEY) { 52628 return JS_DupValue(ctx, mr->key); 52629 } else { 52630 JSValueConst args[2]; 52631 args[0] = mr->key; 52632 if (magic) 52633 args[1] = mr->key; 52634 else 52635 args[1] = mr->value; 52636 if (it->kind == JS_ITERATOR_KIND_VALUE) { 52637 return JS_DupValue(ctx, args[1]); 52638 } else { 52639 return js_create_array(ctx, 2, args); 52640 } 52641 } 52642 } 52643 52644 static int get_set_record(JSContext *ctx, JSValueConst obj, 52645 int64_t *psize, JSValue *phas, JSValue *pkeys) 52646 { 52647 JSMapState *s; 52648 int64_t size; 52649 JSValue has = JS_UNDEFINED, keys = JS_UNDEFINED; 52650 52651 s = JS_GetOpaque(obj, JS_CLASS_SET); 52652 if (s) { 52653 size = s->record_count; 52654 } else { 52655 JSValue v; 52656 double d; 52657 52658 v = JS_GetProperty(ctx, obj, JS_ATOM_size); 52659 if (JS_IsException(v)) 52660 goto exception; 52661 if (JS_ToFloat64Free(ctx, &d, v) < 0) 52662 goto exception; 52663 if (isnan(d)) { 52664 JS_ThrowTypeError(ctx, ".size is not a number"); 52665 goto exception; 52666 } 52667 if (d < INT64_MIN) 52668 size = INT64_MIN; 52669 else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ 52670 size = INT64_MAX; 52671 else 52672 size = (int64_t)d; 52673 if (size < 0) { 52674 JS_ThrowRangeError(ctx, ".size must be positive"); 52675 goto exception; 52676 } 52677 } 52678 52679 has = JS_GetProperty(ctx, obj, JS_ATOM_has); 52680 if (JS_IsException(has)) 52681 goto exception; 52682 if (JS_IsUndefined(has)) { 52683 JS_ThrowTypeError(ctx, ".has is undefined"); 52684 goto exception; 52685 } 52686 if (!JS_IsFunction(ctx, has)) { 52687 JS_ThrowTypeError(ctx, ".has is not a function"); 52688 goto exception; 52689 } 52690 52691 keys = JS_GetProperty(ctx, obj, JS_ATOM_keys); 52692 if (JS_IsException(keys)) 52693 goto exception; 52694 if (JS_IsUndefined(keys)) { 52695 JS_ThrowTypeError(ctx, ".keys is undefined"); 52696 goto exception; 52697 } 52698 if (!JS_IsFunction(ctx, keys)) { 52699 JS_ThrowTypeError(ctx, ".keys is not a function"); 52700 goto exception; 52701 } 52702 *psize = size; 52703 *phas = has; 52704 *pkeys = keys; 52705 return 0; 52706 52707 exception: 52708 JS_FreeValue(ctx, has); 52709 JS_FreeValue(ctx, keys); 52710 *psize = 0; 52711 *phas = JS_UNDEFINED; 52712 *pkeys = JS_UNDEFINED; 52713 return -1; 52714 } 52715 52716 /* copy 'this_val' in a new set without side effects */ 52717 static JSValue js_copy_set(JSContext *ctx, JSValueConst this_val) 52718 { 52719 JSValue newset; 52720 JSMapState *s, *t; 52721 struct list_head *el; 52722 JSMapRecord *mr; 52723 52724 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 52725 if (!s) 52726 return JS_EXCEPTION; 52727 52728 newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); 52729 if (JS_IsException(newset)) 52730 return JS_EXCEPTION; 52731 t = JS_GetOpaque(newset, JS_CLASS_SET); 52732 52733 // can't clone this_val using js_map_constructor(), 52734 // test262 mandates we don't call the .add method 52735 list_for_each(el, &s->records) { 52736 mr = list_entry(el, JSMapRecord, link); 52737 if (mr->empty) 52738 continue; 52739 if (!set_add_record(ctx, t, mr->key)) 52740 goto exception; 52741 } 52742 return newset; 52743 exception: 52744 JS_FreeValue(ctx, newset); 52745 return JS_EXCEPTION; 52746 } 52747 52748 static JSValue js_set_isDisjointFrom(JSContext *ctx, JSValueConst this_val, 52749 int argc, JSValueConst *argv) 52750 { 52751 JSValue item, iter, keys, has, next, rv, rval; 52752 int done; 52753 BOOL found; 52754 JSMapState *s; 52755 int64_t size; 52756 int ok; 52757 52758 iter = JS_UNDEFINED; 52759 next = JS_UNDEFINED; 52760 rval = JS_EXCEPTION; 52761 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 52762 if (!s) 52763 return JS_EXCEPTION; 52764 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 52765 goto exception; 52766 if (s->record_count <= size) { 52767 iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); 52768 if (JS_IsException(iter)) 52769 goto exception; 52770 found = FALSE; 52771 do { 52772 item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); 52773 if (JS_IsException(item)) 52774 goto exception; 52775 if (done) // item is JS_UNDEFINED 52776 break; 52777 rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item); 52778 JS_FreeValue(ctx, item); 52779 ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION 52780 if (ok < 0) 52781 goto exception; 52782 found = (ok > 0); 52783 } while (!found); 52784 } else { 52785 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 52786 if (JS_IsException(iter)) 52787 goto exception; 52788 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 52789 if (JS_IsException(next)) 52790 goto exception; 52791 found = FALSE; 52792 for(;;) { 52793 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 52794 if (JS_IsException(item)) 52795 goto exception; 52796 if (done) // item is JS_UNDEFINED 52797 break; 52798 item = map_normalize_key(ctx, item); 52799 found = (NULL != map_find_record(ctx, s, item)); 52800 JS_FreeValue(ctx, item); 52801 if (found) { 52802 JS_IteratorClose(ctx, iter, FALSE); 52803 break; 52804 } 52805 } 52806 } 52807 rval = !found ? JS_TRUE : JS_FALSE; 52808 exception: 52809 JS_FreeValue(ctx, has); 52810 JS_FreeValue(ctx, keys); 52811 JS_FreeValue(ctx, iter); 52812 JS_FreeValue(ctx, next); 52813 return rval; 52814 } 52815 52816 static JSValue js_set_isSubsetOf(JSContext *ctx, JSValueConst this_val, 52817 int argc, JSValueConst *argv) 52818 { 52819 JSValue item, iter, keys, has, next, rv, rval; 52820 BOOL found; 52821 JSMapState *s; 52822 int64_t size; 52823 int done, ok; 52824 52825 iter = JS_UNDEFINED; 52826 next = JS_UNDEFINED; 52827 rval = JS_EXCEPTION; 52828 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 52829 if (!s) 52830 return JS_EXCEPTION; 52831 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 52832 goto exception; 52833 found = FALSE; 52834 if (s->record_count > size) 52835 goto fini; 52836 iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); 52837 if (JS_IsException(iter)) 52838 goto exception; 52839 found = TRUE; 52840 do { 52841 item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); 52842 if (JS_IsException(item)) 52843 goto exception; 52844 if (done) // item is JS_UNDEFINED 52845 break; 52846 rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item); 52847 JS_FreeValue(ctx, item); 52848 ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION 52849 if (ok < 0) 52850 goto exception; 52851 found = (ok > 0); 52852 } while (found); 52853 fini: 52854 rval = found ? JS_TRUE : JS_FALSE; 52855 exception: 52856 JS_FreeValue(ctx, has); 52857 JS_FreeValue(ctx, keys); 52858 JS_FreeValue(ctx, iter); 52859 JS_FreeValue(ctx, next); 52860 return rval; 52861 } 52862 52863 static JSValue js_set_isSupersetOf(JSContext *ctx, JSValueConst this_val, 52864 int argc, JSValueConst *argv) 52865 { 52866 JSValue item, iter, keys, has, next, rval; 52867 int done; 52868 BOOL found; 52869 JSMapState *s; 52870 int64_t size; 52871 52872 iter = JS_UNDEFINED; 52873 next = JS_UNDEFINED; 52874 rval = JS_EXCEPTION; 52875 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 52876 if (!s) 52877 return JS_EXCEPTION; 52878 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 52879 goto exception; 52880 found = FALSE; 52881 if (s->record_count < size) 52882 goto fini; 52883 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 52884 if (JS_IsException(iter)) 52885 goto exception; 52886 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 52887 if (JS_IsException(next)) 52888 goto exception; 52889 found = TRUE; 52890 for(;;) { 52891 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 52892 if (JS_IsException(item)) 52893 goto exception; 52894 if (done) // item is JS_UNDEFINED 52895 break; 52896 item = map_normalize_key(ctx, item); 52897 found = (NULL != map_find_record(ctx, s, item)); 52898 JS_FreeValue(ctx, item); 52899 if (!found) { 52900 JS_IteratorClose(ctx, iter, FALSE); 52901 break; 52902 } 52903 } 52904 fini: 52905 rval = found ? JS_TRUE : JS_FALSE; 52906 exception: 52907 JS_FreeValue(ctx, has); 52908 JS_FreeValue(ctx, keys); 52909 JS_FreeValue(ctx, iter); 52910 JS_FreeValue(ctx, next); 52911 return rval; 52912 } 52913 52914 static JSValue js_set_intersection(JSContext *ctx, JSValueConst this_val, 52915 int argc, JSValueConst *argv) 52916 { 52917 JSValue newset, item, iter, keys, has, next, rv; 52918 JSMapState *s, *t; 52919 JSMapRecord *mr; 52920 int64_t size; 52921 int done, ok; 52922 52923 iter = JS_UNDEFINED; 52924 next = JS_UNDEFINED; 52925 newset = JS_UNDEFINED; 52926 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 52927 if (!s) 52928 return JS_EXCEPTION; 52929 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 52930 goto exception; 52931 if (s->record_count > size) { 52932 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 52933 if (JS_IsException(iter)) 52934 goto exception; 52935 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 52936 if (JS_IsException(next)) 52937 goto exception; 52938 newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); 52939 if (JS_IsException(newset)) 52940 goto exception; 52941 t = JS_GetOpaque(newset, JS_CLASS_SET); 52942 for (;;) { 52943 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 52944 if (JS_IsException(item)) 52945 goto exception; 52946 if (done) // item is JS_UNDEFINED 52947 break; 52948 item = map_normalize_key(ctx, item); 52949 if (!map_find_record(ctx, s, item)) { 52950 JS_FreeValue(ctx, item); 52951 } else if (map_find_record(ctx, t, item)) { 52952 JS_FreeValue(ctx, item); // no duplicates 52953 } else { 52954 mr = set_add_record(ctx, t, item); 52955 JS_FreeValue(ctx, item); 52956 if (!mr) 52957 goto exception; 52958 } 52959 } 52960 } else { 52961 iter = js_create_map_iterator(ctx, this_val, 0, NULL, MAGIC_SET); 52962 if (JS_IsException(iter)) 52963 goto exception; 52964 newset = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, MAGIC_SET); 52965 if (JS_IsException(newset)) 52966 goto exception; 52967 t = JS_GetOpaque(newset, JS_CLASS_SET); 52968 for (;;) { 52969 item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); 52970 if (JS_IsException(item)) 52971 goto exception; 52972 if (done) // item is JS_UNDEFINED 52973 break; 52974 rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item); 52975 ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION 52976 if (ok > 0) { 52977 item = map_normalize_key(ctx, item); 52978 if (map_find_record(ctx, t, item)) { 52979 JS_FreeValue(ctx, item); // no duplicates 52980 } else { 52981 mr = set_add_record(ctx, t, item); 52982 JS_FreeValue(ctx, item); 52983 if (!mr) 52984 goto exception; 52985 } 52986 } else { 52987 JS_FreeValue(ctx, item); 52988 if (ok < 0) 52989 goto exception; 52990 } 52991 } 52992 } 52993 goto fini; 52994 exception: 52995 JS_FreeValue(ctx, newset); 52996 newset = JS_EXCEPTION; 52997 fini: 52998 JS_FreeValue(ctx, has); 52999 JS_FreeValue(ctx, keys); 53000 JS_FreeValue(ctx, iter); 53001 JS_FreeValue(ctx, next); 53002 return newset; 53003 } 53004 53005 static JSValue js_set_difference(JSContext *ctx, JSValueConst this_val, 53006 int argc, JSValueConst *argv) 53007 { 53008 JSValue newset, item, iter, keys, has, next, rv; 53009 JSMapState *s, *t; 53010 int64_t size; 53011 int done; 53012 int ok; 53013 53014 iter = JS_UNDEFINED; 53015 next = JS_UNDEFINED; 53016 newset = JS_UNDEFINED; 53017 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 53018 if (!s) 53019 return JS_EXCEPTION; 53020 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 53021 goto exception; 53022 53023 newset = js_copy_set(ctx, this_val); 53024 if (JS_IsException(newset)) 53025 goto exception; 53026 t = JS_GetOpaque(newset, JS_CLASS_SET); 53027 53028 if (s->record_count <= size) { 53029 iter = js_create_map_iterator(ctx, newset, 0, NULL, MAGIC_SET); 53030 if (JS_IsException(iter)) 53031 goto exception; 53032 for (;;) { 53033 item = js_map_iterator_next(ctx, iter, 0, NULL, &done, MAGIC_SET); 53034 if (JS_IsException(item)) 53035 goto exception; 53036 if (done) // item is JS_UNDEFINED 53037 break; 53038 rv = JS_Call(ctx, has, argv[0], 1, (JSValueConst *)&item); 53039 ok = JS_ToBoolFree(ctx, rv); // returns -1 if rv is JS_EXCEPTION 53040 if (ok < 0) { 53041 JS_FreeValue(ctx, item); 53042 goto exception; 53043 } 53044 if (ok) { 53045 map_delete_record(ctx, t, item); 53046 } 53047 JS_FreeValue(ctx, item); 53048 } 53049 } else { 53050 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 53051 if (JS_IsException(iter)) 53052 goto exception; 53053 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 53054 if (JS_IsException(next)) 53055 goto exception; 53056 for (;;) { 53057 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 53058 if (JS_IsException(item)) 53059 goto exception; 53060 if (done) // item is JS_UNDEFINED 53061 break; 53062 map_delete_record(ctx, t, item); 53063 JS_FreeValue(ctx, item); 53064 } 53065 } 53066 goto fini; 53067 exception: 53068 JS_FreeValue(ctx, newset); 53069 newset = JS_EXCEPTION; 53070 fini: 53071 JS_FreeValue(ctx, has); 53072 JS_FreeValue(ctx, keys); 53073 JS_FreeValue(ctx, iter); 53074 JS_FreeValue(ctx, next); 53075 return newset; 53076 } 53077 53078 static JSValue js_set_symmetricDifference(JSContext *ctx, JSValueConst this_val, 53079 int argc, JSValueConst *argv) 53080 { 53081 JSValue newset, item, iter, next, has, keys; 53082 JSMapState *s, *t; 53083 JSMapRecord *mr; 53084 int64_t size; 53085 int done; 53086 BOOL present; 53087 53088 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 53089 if (!s) 53090 return JS_EXCEPTION; 53091 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 53092 return JS_EXCEPTION; 53093 JS_FreeValue(ctx, has); 53094 53095 next = JS_UNDEFINED; 53096 newset = JS_UNDEFINED; 53097 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 53098 if (JS_IsException(iter)) 53099 goto exception; 53100 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 53101 if (JS_IsException(next)) 53102 goto exception; 53103 newset = js_copy_set(ctx, this_val); 53104 if (JS_IsException(newset)) 53105 goto exception; 53106 t = JS_GetOpaque(newset, JS_CLASS_SET); 53107 for (;;) { 53108 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 53109 if (JS_IsException(item)) 53110 goto exception; 53111 if (done) // item is JS_UNDEFINED 53112 break; 53113 // note the subtlety here: due to mutating iterators, it's 53114 // possible for keys to disappear during iteration; test262 53115 // still expects us to maintain insertion order though, so 53116 // we first check |this|, then |new|; |new| is a copy of |this| 53117 // - if item exists in |this|, delete (if it exists) from |new| 53118 // - if item misses in |this| and |new|, add to |new| 53119 // - if item exists in |new| but misses in |this|, *don't* add it, 53120 // mutating iterator erased it 53121 item = map_normalize_key(ctx, item); 53122 present = (NULL != map_find_record(ctx, s, item)); 53123 mr = map_find_record(ctx, t, item); 53124 if (present) { 53125 map_delete_record(ctx, t, item); 53126 JS_FreeValue(ctx, item); 53127 } else if (mr) { 53128 JS_FreeValue(ctx, item); 53129 } else { 53130 mr = set_add_record(ctx, t, item); 53131 JS_FreeValue(ctx, item); 53132 if (!mr) 53133 goto exception; 53134 } 53135 } 53136 goto fini; 53137 exception: 53138 JS_FreeValue(ctx, newset); 53139 newset = JS_EXCEPTION; 53140 fini: 53141 JS_FreeValue(ctx, next); 53142 JS_FreeValue(ctx, iter); 53143 JS_FreeValue(ctx, keys); 53144 return newset; 53145 } 53146 53147 static JSValue js_set_union(JSContext *ctx, JSValueConst this_val, 53148 int argc, JSValueConst *argv) 53149 { 53150 JSValue newset, item, iter, next, has, keys, rv; 53151 JSMapState *s; 53152 int64_t size; 53153 int done; 53154 53155 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_SET); 53156 if (!s) 53157 return JS_EXCEPTION; 53158 if (get_set_record(ctx, argv[0], &size, &has, &keys) < 0) 53159 return JS_EXCEPTION; 53160 JS_FreeValue(ctx, has); 53161 53162 next = JS_UNDEFINED; 53163 newset = JS_UNDEFINED; 53164 iter = JS_Call(ctx, keys, argv[0], 0, NULL); 53165 if (JS_IsException(iter)) 53166 goto exception; 53167 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 53168 if (JS_IsException(next)) 53169 goto exception; 53170 53171 newset = js_copy_set(ctx, this_val); 53172 if (JS_IsException(newset)) 53173 goto exception; 53174 53175 for (;;) { 53176 item = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 53177 if (JS_IsException(item)) 53178 goto exception; 53179 if (done) // item is JS_UNDEFINED 53180 break; 53181 rv = js_map_set(ctx, newset, 1, (JSValueConst *)&item, MAGIC_SET); 53182 JS_FreeValue(ctx, item); 53183 if (JS_IsException(rv)) 53184 goto exception; 53185 JS_FreeValue(ctx, rv); 53186 } 53187 goto fini; 53188 exception: 53189 JS_FreeValue(ctx, newset); 53190 newset = JS_EXCEPTION; 53191 fini: 53192 JS_FreeValue(ctx, next); 53193 JS_FreeValue(ctx, iter); 53194 JS_FreeValue(ctx, keys); 53195 return newset; 53196 } 53197 53198 static const JSCFunctionListEntry js_map_funcs[] = { 53199 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), 53200 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 53201 }; 53202 53203 static const JSCFunctionListEntry js_map_proto_funcs[] = { 53204 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), 53205 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), 53206 JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert, 53207 (JS_CLASS_MAP << 1) | /*computed*/FALSE ), 53208 JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert, 53209 (JS_CLASS_MAP << 1) | /*computed*/TRUE ), 53210 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), 53211 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), 53212 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), 53213 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), 53214 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), 53215 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), 53216 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), 53217 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), 53218 JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), 53219 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), 53220 }; 53221 53222 static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { 53223 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), 53224 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), 53225 }; 53226 53227 static const JSCFunctionListEntry js_set_proto_funcs[] = { 53228 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), 53229 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), 53230 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), 53231 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), 53232 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), 53233 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), 53234 JS_CFUNC_DEF("isDisjointFrom", 1, js_set_isDisjointFrom ), 53235 JS_CFUNC_DEF("isSubsetOf", 1, js_set_isSubsetOf ), 53236 JS_CFUNC_DEF("isSupersetOf", 1, js_set_isSupersetOf ), 53237 JS_CFUNC_DEF("intersection", 1, js_set_intersection ), 53238 JS_CFUNC_DEF("difference", 1, js_set_difference ), 53239 JS_CFUNC_DEF("symmetricDifference", 1, js_set_symmetricDifference ), 53240 JS_CFUNC_DEF("union", 1, js_set_union ), 53241 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), 53242 JS_ALIAS_DEF("keys", "values" ), 53243 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 53244 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), 53245 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), 53246 }; 53247 53248 static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { 53249 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), 53250 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), 53251 }; 53252 53253 static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { 53254 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), 53255 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), 53256 JS_CFUNC_MAGIC_DEF("getOrInsert", 2, js_map_getOrInsert, 53257 (JS_CLASS_WEAKMAP << 1) | /*computed*/FALSE ), 53258 JS_CFUNC_MAGIC_DEF("getOrInsertComputed", 2, js_map_getOrInsert, 53259 (JS_CLASS_WEAKMAP << 1) | /*computed*/TRUE ), 53260 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), 53261 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), 53262 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), 53263 }; 53264 53265 static const JSCFunctionListEntry js_weak_set_proto_funcs[] = { 53266 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), 53267 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), 53268 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), 53269 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), 53270 }; 53271 53272 static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { 53273 js_map_proto_funcs, 53274 js_set_proto_funcs, 53275 js_weak_map_proto_funcs, 53276 js_weak_set_proto_funcs, 53277 js_map_iterator_proto_funcs, 53278 js_set_iterator_proto_funcs, 53279 }; 53280 53281 static const uint8_t js_map_proto_funcs_count[6] = { 53282 countof(js_map_proto_funcs), 53283 countof(js_set_proto_funcs), 53284 countof(js_weak_map_proto_funcs), 53285 countof(js_weak_set_proto_funcs), 53286 countof(js_map_iterator_proto_funcs), 53287 countof(js_set_iterator_proto_funcs), 53288 }; 53289 53290 int JS_AddIntrinsicMapSet(JSContext *ctx) 53291 { 53292 int i; 53293 JSValue obj1; 53294 char buf[ATOM_GET_STR_BUF_SIZE]; 53295 53296 for(i = 0; i < 4; i++) { 53297 JSCFunctionType ft; 53298 const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), 53299 JS_ATOM_Map + i); 53300 ft.constructor_magic = js_map_constructor; 53301 obj1 = JS_NewCConstructor(ctx, JS_CLASS_MAP + i, name, 53302 ft.generic, 0, JS_CFUNC_constructor_magic, i, 53303 JS_UNDEFINED, 53304 js_map_funcs, i < 2 ? countof(js_map_funcs) : 0, 53305 js_map_proto_funcs_ptr[i], js_map_proto_funcs_count[i], 53306 0); 53307 if (JS_IsException(obj1)) 53308 return -1; 53309 JS_FreeValue(ctx, obj1); 53310 } 53311 53312 for(i = 0; i < 2; i++) { 53313 ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = 53314 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 53315 js_map_proto_funcs_ptr[i + 4], 53316 js_map_proto_funcs_count[i + 4]); 53317 if (JS_IsException(ctx->class_proto[JS_CLASS_MAP_ITERATOR + i])) 53318 return -1; 53319 } 53320 return 0; 53321 } 53322 53323 /* Generator */ 53324 static const JSCFunctionListEntry js_generator_function_proto_funcs[] = { 53325 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE), 53326 }; 53327 53328 static const JSCFunctionListEntry js_generator_proto_funcs[] = { 53329 JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ), 53330 JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ), 53331 JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ), 53332 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE), 53333 }; 53334 53335 /* Promise */ 53336 53337 typedef struct JSPromiseData { 53338 JSPromiseStateEnum promise_state; 53339 /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ 53340 struct list_head promise_reactions[2]; 53341 BOOL is_handled; /* Note: only useful to debug */ 53342 JSValue promise_result; 53343 } JSPromiseData; 53344 53345 typedef struct JSPromiseFunctionDataResolved { 53346 int ref_count; 53347 BOOL already_resolved; 53348 } JSPromiseFunctionDataResolved; 53349 53350 typedef struct JSPromiseFunctionData { 53351 JSValue promise; 53352 JSPromiseFunctionDataResolved *presolved; 53353 } JSPromiseFunctionData; 53354 53355 typedef struct JSPromiseReactionData { 53356 struct list_head link; /* not used in promise_reaction_job */ 53357 JSValue resolving_funcs[2]; 53358 JSValue handler; 53359 } JSPromiseReactionData; 53360 53361 JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) 53362 { 53363 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 53364 if (!s) 53365 return -1; 53366 return s->promise_state; 53367 } 53368 53369 JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) 53370 { 53371 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 53372 if (!s) 53373 return JS_UNDEFINED; 53374 return JS_DupValue(ctx, s->promise_result); 53375 } 53376 53377 static int js_create_resolving_functions(JSContext *ctx, JSValue *args, 53378 JSValueConst promise); 53379 53380 static void promise_reaction_data_free(JSRuntime *rt, 53381 JSPromiseReactionData *rd) 53382 { 53383 JS_FreeValueRT(rt, rd->resolving_funcs[0]); 53384 JS_FreeValueRT(rt, rd->resolving_funcs[1]); 53385 JS_FreeValueRT(rt, rd->handler); 53386 js_free_rt(rt, rd); 53387 } 53388 53389 static JSValue promise_reaction_job(JSContext *ctx, int argc, 53390 JSValueConst *argv) 53391 { 53392 JSValueConst handler, arg, func; 53393 JSValue res, res2; 53394 BOOL is_reject; 53395 53396 assert(argc == 5); 53397 handler = argv[2]; 53398 is_reject = JS_ToBool(ctx, argv[3]); 53399 arg = argv[4]; 53400 #ifdef DUMP_PROMISE 53401 printf("promise_reaction_job: is_reject=%d\n", is_reject); 53402 #endif 53403 53404 if (JS_IsUndefined(handler)) { 53405 if (is_reject) { 53406 res = JS_Throw(ctx, JS_DupValue(ctx, arg)); 53407 } else { 53408 res = JS_DupValue(ctx, arg); 53409 } 53410 } else { 53411 res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); 53412 } 53413 is_reject = JS_IsException(res); 53414 if (is_reject) 53415 res = JS_GetException(ctx); 53416 func = argv[is_reject]; 53417 /* as an extension, we support undefined as value to avoid 53418 creating a dummy promise in the 'await' implementation of async 53419 functions */ 53420 if (!JS_IsUndefined(func)) { 53421 res2 = JS_Call(ctx, func, JS_UNDEFINED, 53422 1, (JSValueConst *)&res); 53423 } else { 53424 res2 = JS_UNDEFINED; 53425 } 53426 JS_FreeValue(ctx, res); 53427 53428 return res2; 53429 } 53430 53431 void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, 53432 JSHostPromiseRejectionTracker *cb, 53433 void *opaque) 53434 { 53435 rt->host_promise_rejection_tracker = cb; 53436 rt->host_promise_rejection_tracker_opaque = opaque; 53437 } 53438 53439 static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, 53440 JSValueConst value, BOOL is_reject) 53441 { 53442 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 53443 struct list_head *el, *el1; 53444 JSPromiseReactionData *rd; 53445 JSValueConst args[5]; 53446 53447 if (!s || s->promise_state != JS_PROMISE_PENDING) 53448 return; /* should never happen */ 53449 set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); 53450 s->promise_state = JS_PROMISE_FULFILLED + is_reject; 53451 #ifdef DUMP_PROMISE 53452 printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); 53453 #endif 53454 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { 53455 JSRuntime *rt = ctx->rt; 53456 if (rt->host_promise_rejection_tracker) { 53457 rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, 53458 rt->host_promise_rejection_tracker_opaque); 53459 } 53460 } 53461 53462 list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { 53463 rd = list_entry(el, JSPromiseReactionData, link); 53464 args[0] = rd->resolving_funcs[0]; 53465 args[1] = rd->resolving_funcs[1]; 53466 args[2] = rd->handler; 53467 args[3] = JS_NewBool(ctx, is_reject); 53468 args[4] = value; 53469 JS_EnqueueJob(ctx, promise_reaction_job, 5, args); 53470 list_del(&rd->link); 53471 promise_reaction_data_free(ctx->rt, rd); 53472 } 53473 53474 list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { 53475 rd = list_entry(el, JSPromiseReactionData, link); 53476 list_del(&rd->link); 53477 promise_reaction_data_free(ctx->rt, rd); 53478 } 53479 } 53480 53481 static void reject_promise(JSContext *ctx, JSValueConst promise, 53482 JSValueConst value) 53483 { 53484 fulfill_or_reject_promise(ctx, promise, value, TRUE); 53485 } 53486 53487 static JSValue js_promise_resolve_thenable_job(JSContext *ctx, 53488 int argc, JSValueConst *argv) 53489 { 53490 JSValueConst promise, thenable, then; 53491 JSValue args[2], res; 53492 53493 #ifdef DUMP_PROMISE 53494 printf("js_promise_resolve_thenable_job\n"); 53495 #endif 53496 assert(argc == 3); 53497 promise = argv[0]; 53498 thenable = argv[1]; 53499 then = argv[2]; 53500 if (js_create_resolving_functions(ctx, args, promise) < 0) 53501 return JS_EXCEPTION; 53502 res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); 53503 if (JS_IsException(res)) { 53504 JSValue error = JS_GetException(ctx); 53505 res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); 53506 JS_FreeValue(ctx, error); 53507 } 53508 JS_FreeValue(ctx, args[0]); 53509 JS_FreeValue(ctx, args[1]); 53510 return res; 53511 } 53512 53513 static void js_promise_resolve_function_free_resolved(JSRuntime *rt, 53514 JSPromiseFunctionDataResolved *sr) 53515 { 53516 if (--sr->ref_count == 0) { 53517 js_free_rt(rt, sr); 53518 } 53519 } 53520 53521 static int js_create_resolving_functions(JSContext *ctx, 53522 JSValue *resolving_funcs, 53523 JSValueConst promise) 53524 53525 { 53526 JSValue obj; 53527 JSPromiseFunctionData *s; 53528 JSPromiseFunctionDataResolved *sr; 53529 int i, ret; 53530 53531 sr = js_malloc(ctx, sizeof(*sr)); 53532 if (!sr) 53533 return -1; 53534 sr->ref_count = 1; 53535 sr->already_resolved = FALSE; /* must be shared between the two functions */ 53536 ret = 0; 53537 for(i = 0; i < 2; i++) { 53538 obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 53539 JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); 53540 if (JS_IsException(obj)) 53541 goto fail; 53542 s = js_malloc(ctx, sizeof(*s)); 53543 if (!s) { 53544 JS_FreeValue(ctx, obj); 53545 fail: 53546 53547 if (i != 0) 53548 JS_FreeValue(ctx, resolving_funcs[0]); 53549 ret = -1; 53550 break; 53551 } 53552 sr->ref_count++; 53553 s->presolved = sr; 53554 s->promise = JS_DupValue(ctx, promise); 53555 JS_SetOpaque(obj, s); 53556 js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); 53557 resolving_funcs[i] = obj; 53558 } 53559 js_promise_resolve_function_free_resolved(ctx->rt, sr); 53560 return ret; 53561 } 53562 53563 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) 53564 { 53565 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; 53566 if (s) { 53567 js_promise_resolve_function_free_resolved(rt, s->presolved); 53568 JS_FreeValueRT(rt, s->promise); 53569 js_free_rt(rt, s); 53570 } 53571 } 53572 53573 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, 53574 JS_MarkFunc *mark_func) 53575 { 53576 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; 53577 if (s) { 53578 JS_MarkValue(rt, s->promise, mark_func); 53579 } 53580 } 53581 53582 static JSValue js_promise_resolve_function_call(JSContext *ctx, 53583 JSValueConst func_obj, 53584 JSValueConst this_val, 53585 int argc, JSValueConst *argv, 53586 int flags) 53587 { 53588 JSObject *p = JS_VALUE_GET_OBJ(func_obj); 53589 JSPromiseFunctionData *s; 53590 JSValueConst resolution, args[3]; 53591 JSValue then; 53592 BOOL is_reject; 53593 53594 s = p->u.promise_function_data; 53595 if (!s || s->presolved->already_resolved) 53596 return JS_UNDEFINED; 53597 s->presolved->already_resolved = TRUE; 53598 is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; 53599 if (argc > 0) 53600 resolution = argv[0]; 53601 else 53602 resolution = JS_UNDEFINED; 53603 #ifdef DUMP_PROMISE 53604 printf("js_promise_resolving_function_call: is_reject=%d ", is_reject); 53605 JS_DumpValue(ctx, "resolution", resolution); 53606 printf("\n"); 53607 #endif 53608 if (is_reject || !JS_IsObject(resolution)) { 53609 goto done; 53610 } else if (js_same_value(ctx, resolution, s->promise)) { 53611 JS_ThrowTypeError(ctx, "promise self resolution"); 53612 goto fail_reject; 53613 } 53614 then = JS_GetProperty(ctx, resolution, JS_ATOM_then); 53615 if (JS_IsException(then)) { 53616 JSValue error; 53617 fail_reject: 53618 error = JS_GetException(ctx); 53619 reject_promise(ctx, s->promise, error); 53620 JS_FreeValue(ctx, error); 53621 } else if (!JS_IsFunction(ctx, then)) { 53622 JS_FreeValue(ctx, then); 53623 done: 53624 fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); 53625 } else { 53626 args[0] = s->promise; 53627 args[1] = resolution; 53628 args[2] = then; 53629 JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); 53630 JS_FreeValue(ctx, then); 53631 } 53632 return JS_UNDEFINED; 53633 } 53634 53635 static void js_promise_finalizer(JSRuntime *rt, JSValue val) 53636 { 53637 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); 53638 struct list_head *el, *el1; 53639 int i; 53640 53641 if (!s) 53642 return; 53643 for(i = 0; i < 2; i++) { 53644 list_for_each_safe(el, el1, &s->promise_reactions[i]) { 53645 JSPromiseReactionData *rd = 53646 list_entry(el, JSPromiseReactionData, link); 53647 promise_reaction_data_free(rt, rd); 53648 } 53649 } 53650 JS_FreeValueRT(rt, s->promise_result); 53651 js_free_rt(rt, s); 53652 } 53653 53654 static void js_promise_mark(JSRuntime *rt, JSValueConst val, 53655 JS_MarkFunc *mark_func) 53656 { 53657 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); 53658 struct list_head *el; 53659 int i; 53660 53661 if (!s) 53662 return; 53663 for(i = 0; i < 2; i++) { 53664 list_for_each(el, &s->promise_reactions[i]) { 53665 JSPromiseReactionData *rd = 53666 list_entry(el, JSPromiseReactionData, link); 53667 JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); 53668 JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); 53669 JS_MarkValue(rt, rd->handler, mark_func); 53670 } 53671 } 53672 JS_MarkValue(rt, s->promise_result, mark_func); 53673 } 53674 53675 static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, 53676 int argc, JSValueConst *argv) 53677 { 53678 JSValueConst executor; 53679 JSValue obj; 53680 JSPromiseData *s; 53681 JSValue args[2], ret; 53682 int i; 53683 53684 executor = argv[0]; 53685 if (check_function(ctx, executor)) 53686 return JS_EXCEPTION; 53687 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); 53688 if (JS_IsException(obj)) 53689 return JS_EXCEPTION; 53690 s = js_mallocz(ctx, sizeof(*s)); 53691 if (!s) 53692 goto fail; 53693 s->promise_state = JS_PROMISE_PENDING; 53694 s->is_handled = FALSE; 53695 for(i = 0; i < 2; i++) 53696 init_list_head(&s->promise_reactions[i]); 53697 s->promise_result = JS_UNDEFINED; 53698 JS_SetOpaque(obj, s); 53699 if (js_create_resolving_functions(ctx, args, obj)) 53700 goto fail; 53701 ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); 53702 if (JS_IsException(ret)) { 53703 JSValue ret2, error; 53704 error = JS_GetException(ctx); 53705 ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); 53706 JS_FreeValue(ctx, error); 53707 if (JS_IsException(ret2)) 53708 goto fail1; 53709 JS_FreeValue(ctx, ret2); 53710 } 53711 JS_FreeValue(ctx, ret); 53712 JS_FreeValue(ctx, args[0]); 53713 JS_FreeValue(ctx, args[1]); 53714 return obj; 53715 fail1: 53716 JS_FreeValue(ctx, args[0]); 53717 JS_FreeValue(ctx, args[1]); 53718 fail: 53719 JS_FreeValue(ctx, obj); 53720 return JS_EXCEPTION; 53721 } 53722 53723 static JSValue js_promise_executor(JSContext *ctx, 53724 JSValueConst this_val, 53725 int argc, JSValueConst *argv, 53726 int magic, JSValue *func_data) 53727 { 53728 int i; 53729 53730 for(i = 0; i < 2; i++) { 53731 if (!JS_IsUndefined(func_data[i])) 53732 return JS_ThrowTypeError(ctx, "resolving function already set"); 53733 func_data[i] = JS_DupValue(ctx, argv[i]); 53734 } 53735 return JS_UNDEFINED; 53736 } 53737 53738 static JSValue js_promise_executor_new(JSContext *ctx) 53739 { 53740 JSValueConst func_data[2]; 53741 53742 func_data[0] = JS_UNDEFINED; 53743 func_data[1] = JS_UNDEFINED; 53744 return JS_NewCFunctionData(ctx, js_promise_executor, 2, 53745 0, 2, func_data); 53746 } 53747 53748 static JSValue js_new_promise_capability(JSContext *ctx, 53749 JSValue *resolving_funcs, 53750 JSValueConst ctor) 53751 { 53752 JSValue executor, result_promise; 53753 JSCFunctionDataRecord *s; 53754 int i; 53755 53756 executor = js_promise_executor_new(ctx); 53757 if (JS_IsException(executor)) 53758 return executor; 53759 53760 if (JS_IsUndefined(ctor)) { 53761 result_promise = js_promise_constructor(ctx, ctor, 1, 53762 (JSValueConst *)&executor); 53763 } else { 53764 result_promise = JS_CallConstructor(ctx, ctor, 1, 53765 (JSValueConst *)&executor); 53766 } 53767 if (JS_IsException(result_promise)) 53768 goto fail; 53769 s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); 53770 for(i = 0; i < 2; i++) { 53771 if (check_function(ctx, s->data[i])) 53772 goto fail; 53773 } 53774 for(i = 0; i < 2; i++) 53775 resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); 53776 JS_FreeValue(ctx, executor); 53777 return result_promise; 53778 fail: 53779 JS_FreeValue(ctx, executor); 53780 JS_FreeValue(ctx, result_promise); 53781 return JS_EXCEPTION; 53782 } 53783 53784 JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) 53785 { 53786 return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); 53787 } 53788 53789 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, 53790 int argc, JSValueConst *argv, int magic) 53791 { 53792 JSValue result_promise, resolving_funcs[2], ret; 53793 BOOL is_reject = magic; 53794 53795 if (!JS_IsObject(this_val)) 53796 return JS_ThrowTypeErrorNotAnObject(ctx); 53797 if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { 53798 JSValue ctor; 53799 BOOL is_same; 53800 ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); 53801 if (JS_IsException(ctor)) 53802 return ctor; 53803 is_same = js_same_value(ctx, ctor, this_val); 53804 JS_FreeValue(ctx, ctor); 53805 if (is_same) 53806 return JS_DupValue(ctx, argv[0]); 53807 } 53808 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 53809 if (JS_IsException(result_promise)) 53810 return result_promise; 53811 ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); 53812 JS_FreeValue(ctx, resolving_funcs[0]); 53813 JS_FreeValue(ctx, resolving_funcs[1]); 53814 if (JS_IsException(ret)) { 53815 JS_FreeValue(ctx, result_promise); 53816 return ret; 53817 } 53818 JS_FreeValue(ctx, ret); 53819 return result_promise; 53820 } 53821 53822 static JSValue js_promise_withResolvers(JSContext *ctx, 53823 JSValueConst this_val, 53824 int argc, JSValueConst *argv) 53825 { 53826 JSValue result_promise, resolving_funcs[2], obj; 53827 if (!JS_IsObject(this_val)) 53828 return JS_ThrowTypeErrorNotAnObject(ctx); 53829 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 53830 if (JS_IsException(result_promise)) 53831 return result_promise; 53832 obj = JS_NewObject(ctx); 53833 if (JS_IsException(obj)) 53834 goto exception; 53835 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, 53836 JS_PROP_C_W_E) < 0) { 53837 goto exception; 53838 } 53839 result_promise = JS_UNDEFINED; 53840 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], 53841 JS_PROP_C_W_E) < 0) { 53842 goto exception; 53843 } 53844 resolving_funcs[0] = JS_UNDEFINED; 53845 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], 53846 JS_PROP_C_W_E) < 0) { 53847 goto exception; 53848 } 53849 return obj; 53850 exception: 53851 JS_FreeValue(ctx, resolving_funcs[0]); 53852 JS_FreeValue(ctx, resolving_funcs[1]); 53853 JS_FreeValue(ctx, result_promise); 53854 JS_FreeValue(ctx, obj); 53855 return JS_EXCEPTION; 53856 } 53857 53858 static JSValue js_promise_try(JSContext *ctx, JSValueConst this_val, 53859 int argc, JSValueConst *argv) 53860 { 53861 JSValue result_promise, resolving_funcs[2], ret, ret2; 53862 BOOL is_reject = 0; 53863 53864 if (!JS_IsObject(this_val)) 53865 return JS_ThrowTypeErrorNotAnObject(ctx); 53866 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 53867 if (JS_IsException(result_promise)) 53868 return result_promise; 53869 ret = JS_Call(ctx, argv[0], JS_UNDEFINED, argc - 1, argv + 1); 53870 if (JS_IsException(ret)) { 53871 is_reject = 1; 53872 ret = JS_GetException(ctx); 53873 } 53874 ret2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, (JSValueConst *)&ret); 53875 JS_FreeValue(ctx, resolving_funcs[0]); 53876 JS_FreeValue(ctx, resolving_funcs[1]); 53877 JS_FreeValue(ctx, ret); 53878 if (JS_IsException(ret2)) { 53879 JS_FreeValue(ctx, result_promise); 53880 return ret2; 53881 } 53882 JS_FreeValue(ctx, ret2); 53883 return result_promise; 53884 } 53885 53886 static __exception int remainingElementsCount_add(JSContext *ctx, 53887 JSValueConst resolve_element_env, 53888 int addend) 53889 { 53890 JSValue val; 53891 int remainingElementsCount; 53892 53893 val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); 53894 if (JS_IsException(val)) 53895 return -1; 53896 if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) 53897 return -1; 53898 remainingElementsCount += addend; 53899 if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, 53900 JS_NewInt32(ctx, remainingElementsCount)) < 0) 53901 return -1; 53902 return (remainingElementsCount == 0); 53903 } 53904 53905 #define PROMISE_MAGIC_all 0 53906 #define PROMISE_MAGIC_allSettled 1 53907 #define PROMISE_MAGIC_any 2 53908 53909 static JSValue js_promise_all_resolve_element(JSContext *ctx, 53910 JSValueConst this_val, 53911 int argc, JSValueConst *argv, 53912 int magic, 53913 JSValue *func_data) 53914 { 53915 int resolve_type = magic & 3; 53916 int is_reject = magic & 4; 53917 BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); 53918 JSValueConst values = func_data[2]; 53919 JSValueConst resolve = func_data[3]; 53920 JSValueConst resolve_element_env = func_data[4]; 53921 JSValue ret, obj; 53922 int is_zero, index; 53923 53924 if (JS_ToInt32(ctx, &index, func_data[1])) 53925 return JS_EXCEPTION; 53926 if (alreadyCalled) 53927 return JS_UNDEFINED; 53928 func_data[0] = JS_NewBool(ctx, TRUE); 53929 53930 if (resolve_type == PROMISE_MAGIC_allSettled) { 53931 JSValue str; 53932 53933 obj = JS_NewObject(ctx); 53934 if (JS_IsException(obj)) 53935 return JS_EXCEPTION; 53936 str = js_new_string8(ctx, is_reject ? "rejected" : "fulfilled"); 53937 if (JS_IsException(str)) 53938 goto fail1; 53939 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, 53940 str, 53941 JS_PROP_C_W_E) < 0) 53942 goto fail1; 53943 if (JS_DefinePropertyValue(ctx, obj, 53944 is_reject ? JS_ATOM_reason : JS_ATOM_value, 53945 JS_DupValue(ctx, argv[0]), 53946 JS_PROP_C_W_E) < 0) { 53947 fail1: 53948 JS_FreeValue(ctx, obj); 53949 return JS_EXCEPTION; 53950 } 53951 } else { 53952 obj = JS_DupValue(ctx, argv[0]); 53953 } 53954 if (JS_DefinePropertyValueUint32(ctx, values, index, 53955 obj, JS_PROP_C_W_E) < 0) 53956 return JS_EXCEPTION; 53957 53958 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); 53959 if (is_zero < 0) 53960 return JS_EXCEPTION; 53961 if (is_zero) { 53962 if (resolve_type == PROMISE_MAGIC_any) { 53963 JSValue error; 53964 error = js_aggregate_error_constructor(ctx, values); 53965 if (JS_IsException(error)) 53966 return JS_EXCEPTION; 53967 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); 53968 JS_FreeValue(ctx, error); 53969 } else { 53970 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); 53971 } 53972 if (JS_IsException(ret)) 53973 return ret; 53974 JS_FreeValue(ctx, ret); 53975 } 53976 return JS_UNDEFINED; 53977 } 53978 53979 /* magic = 0: Promise.all 1: Promise.allSettled */ 53980 static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, 53981 int argc, JSValueConst *argv, int magic) 53982 { 53983 JSValue result_promise, resolving_funcs[2], item, next_promise, ret; 53984 JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; 53985 JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; 53986 JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; 53987 JSValueConst then_args[2], resolve_element_data[5]; 53988 BOOL done; 53989 int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); 53990 53991 if (!JS_IsObject(this_val)) 53992 return JS_ThrowTypeErrorNotAnObject(ctx); 53993 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 53994 if (JS_IsException(result_promise)) 53995 return result_promise; 53996 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); 53997 if (JS_IsException(promise_resolve) || 53998 check_function(ctx, promise_resolve)) 53999 goto fail_reject; 54000 iter = JS_GetIterator(ctx, argv[0], FALSE); 54001 if (JS_IsException(iter)) { 54002 JSValue error; 54003 fail_reject: 54004 error = JS_GetException(ctx); 54005 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, 54006 (JSValueConst *)&error); 54007 JS_FreeValue(ctx, error); 54008 if (JS_IsException(ret)) 54009 goto fail; 54010 JS_FreeValue(ctx, ret); 54011 } else { 54012 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 54013 if (JS_IsException(next_method)) 54014 goto fail_reject; 54015 values = JS_NewArray(ctx); 54016 if (JS_IsException(values)) 54017 goto fail_reject; 54018 resolve_element_env = JS_NewArray(ctx); 54019 if (JS_IsException(resolve_element_env)) 54020 goto fail_reject; 54021 /* remainingElementsCount field */ 54022 if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, 54023 JS_NewInt32(ctx, 1), 54024 JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) 54025 goto fail_reject; 54026 54027 index = 0; 54028 for(;;) { 54029 /* XXX: conformance: should close the iterator if error on 'done' 54030 access, but not on 'value' access */ 54031 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 54032 if (JS_IsException(item)) 54033 goto fail_reject; 54034 if (done) 54035 break; 54036 next_promise = JS_Call(ctx, promise_resolve, 54037 this_val, 1, (JSValueConst *)&item); 54038 JS_FreeValue(ctx, item); 54039 if (JS_IsException(next_promise)) { 54040 fail_reject1: 54041 JS_IteratorClose(ctx, iter, TRUE); 54042 goto fail_reject; 54043 } 54044 resolve_element_data[0] = JS_NewBool(ctx, FALSE); 54045 resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); 54046 resolve_element_data[2] = values; 54047 resolve_element_data[3] = resolving_funcs[is_promise_any]; 54048 resolve_element_data[4] = resolve_element_env; 54049 resolve_element = 54050 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, 54051 magic, 5, resolve_element_data); 54052 if (JS_IsException(resolve_element)) { 54053 JS_FreeValue(ctx, next_promise); 54054 goto fail_reject1; 54055 } 54056 54057 if (magic == PROMISE_MAGIC_allSettled) { 54058 reject_element = 54059 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, 54060 magic | 4, 5, resolve_element_data); 54061 if (JS_IsException(reject_element)) { 54062 JS_FreeValue(ctx, next_promise); 54063 goto fail_reject1; 54064 } 54065 } else if (magic == PROMISE_MAGIC_any) { 54066 if (JS_DefinePropertyValueUint32(ctx, values, index, 54067 JS_UNDEFINED, JS_PROP_C_W_E) < 0) 54068 goto fail_reject1; 54069 reject_element = resolve_element; 54070 resolve_element = JS_DupValue(ctx, resolving_funcs[0]); 54071 } else { 54072 reject_element = JS_DupValue(ctx, resolving_funcs[1]); 54073 } 54074 54075 if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { 54076 JS_FreeValue(ctx, next_promise); 54077 JS_FreeValue(ctx, resolve_element); 54078 JS_FreeValue(ctx, reject_element); 54079 goto fail_reject1; 54080 } 54081 54082 then_args[0] = resolve_element; 54083 then_args[1] = reject_element; 54084 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); 54085 JS_FreeValue(ctx, resolve_element); 54086 JS_FreeValue(ctx, reject_element); 54087 if (check_exception_free(ctx, ret)) 54088 goto fail_reject1; 54089 index++; 54090 } 54091 54092 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); 54093 if (is_zero < 0) 54094 goto fail_reject; 54095 if (is_zero) { 54096 if (magic == PROMISE_MAGIC_any) { 54097 JSValue error; 54098 error = js_aggregate_error_constructor(ctx, values); 54099 if (JS_IsException(error)) 54100 goto fail_reject; 54101 JS_FreeValue(ctx, values); 54102 values = error; 54103 } 54104 ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, 54105 1, (JSValueConst *)&values); 54106 if (check_exception_free(ctx, ret)) 54107 goto fail_reject; 54108 } 54109 } 54110 done: 54111 JS_FreeValue(ctx, promise_resolve); 54112 JS_FreeValue(ctx, resolve_element_env); 54113 JS_FreeValue(ctx, values); 54114 JS_FreeValue(ctx, next_method); 54115 JS_FreeValue(ctx, iter); 54116 JS_FreeValue(ctx, resolving_funcs[0]); 54117 JS_FreeValue(ctx, resolving_funcs[1]); 54118 return result_promise; 54119 fail: 54120 JS_FreeValue(ctx, result_promise); 54121 result_promise = JS_EXCEPTION; 54122 goto done; 54123 } 54124 54125 static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, 54126 int argc, JSValueConst *argv) 54127 { 54128 JSValue result_promise, resolving_funcs[2], item, next_promise, ret; 54129 JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; 54130 JSValue promise_resolve = JS_UNDEFINED; 54131 BOOL done; 54132 54133 if (!JS_IsObject(this_val)) 54134 return JS_ThrowTypeErrorNotAnObject(ctx); 54135 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 54136 if (JS_IsException(result_promise)) 54137 return result_promise; 54138 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); 54139 if (JS_IsException(promise_resolve) || 54140 check_function(ctx, promise_resolve)) 54141 goto fail_reject; 54142 iter = JS_GetIterator(ctx, argv[0], FALSE); 54143 if (JS_IsException(iter)) { 54144 JSValue error; 54145 fail_reject: 54146 error = JS_GetException(ctx); 54147 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, 54148 (JSValueConst *)&error); 54149 JS_FreeValue(ctx, error); 54150 if (JS_IsException(ret)) 54151 goto fail; 54152 JS_FreeValue(ctx, ret); 54153 } else { 54154 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 54155 if (JS_IsException(next_method)) 54156 goto fail_reject; 54157 54158 for(;;) { 54159 /* XXX: conformance: should close the iterator if error on 'done' 54160 access, but not on 'value' access */ 54161 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 54162 if (JS_IsException(item)) 54163 goto fail_reject; 54164 if (done) 54165 break; 54166 next_promise = JS_Call(ctx, promise_resolve, 54167 this_val, 1, (JSValueConst *)&item); 54168 JS_FreeValue(ctx, item); 54169 if (JS_IsException(next_promise)) { 54170 fail_reject1: 54171 JS_IteratorClose(ctx, iter, TRUE); 54172 goto fail_reject; 54173 } 54174 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, 54175 (JSValueConst *)resolving_funcs); 54176 if (check_exception_free(ctx, ret)) 54177 goto fail_reject1; 54178 } 54179 } 54180 done: 54181 JS_FreeValue(ctx, promise_resolve); 54182 JS_FreeValue(ctx, next_method); 54183 JS_FreeValue(ctx, iter); 54184 JS_FreeValue(ctx, resolving_funcs[0]); 54185 JS_FreeValue(ctx, resolving_funcs[1]); 54186 return result_promise; 54187 fail: 54188 //JS_FreeValue(ctx, next_method); // why not??? 54189 JS_FreeValue(ctx, result_promise); 54190 result_promise = JS_EXCEPTION; 54191 goto done; 54192 } 54193 54194 static __exception int perform_promise_then(JSContext *ctx, 54195 JSValueConst promise, 54196 JSValueConst *resolve_reject, 54197 JSValueConst *cap_resolving_funcs) 54198 { 54199 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 54200 JSPromiseReactionData *rd_array[2], *rd; 54201 int i, j; 54202 54203 rd_array[0] = NULL; 54204 rd_array[1] = NULL; 54205 for(i = 0; i < 2; i++) { 54206 JSValueConst handler; 54207 rd = js_mallocz(ctx, sizeof(*rd)); 54208 if (!rd) { 54209 if (i == 1) 54210 promise_reaction_data_free(ctx->rt, rd_array[0]); 54211 return -1; 54212 } 54213 for(j = 0; j < 2; j++) 54214 rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); 54215 handler = resolve_reject[i]; 54216 if (!JS_IsFunction(ctx, handler)) 54217 handler = JS_UNDEFINED; 54218 rd->handler = JS_DupValue(ctx, handler); 54219 rd_array[i] = rd; 54220 } 54221 54222 if (s->promise_state == JS_PROMISE_PENDING) { 54223 for(i = 0; i < 2; i++) 54224 list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); 54225 } else { 54226 JSValueConst args[5]; 54227 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { 54228 JSRuntime *rt = ctx->rt; 54229 if (rt->host_promise_rejection_tracker) { 54230 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, 54231 TRUE, rt->host_promise_rejection_tracker_opaque); 54232 } 54233 } 54234 i = s->promise_state - JS_PROMISE_FULFILLED; 54235 rd = rd_array[i]; 54236 args[0] = rd->resolving_funcs[0]; 54237 args[1] = rd->resolving_funcs[1]; 54238 args[2] = rd->handler; 54239 args[3] = JS_NewBool(ctx, i); 54240 args[4] = s->promise_result; 54241 JS_EnqueueJob(ctx, promise_reaction_job, 5, args); 54242 for(i = 0; i < 2; i++) 54243 promise_reaction_data_free(ctx->rt, rd_array[i]); 54244 } 54245 s->is_handled = TRUE; 54246 return 0; 54247 } 54248 54249 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, 54250 int argc, JSValueConst *argv) 54251 { 54252 JSValue ctor, result_promise, resolving_funcs[2]; 54253 JSPromiseData *s; 54254 int i, ret; 54255 54256 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); 54257 if (!s) 54258 return JS_EXCEPTION; 54259 54260 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 54261 if (JS_IsException(ctor)) 54262 return ctor; 54263 result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); 54264 JS_FreeValue(ctx, ctor); 54265 if (JS_IsException(result_promise)) 54266 return result_promise; 54267 ret = perform_promise_then(ctx, this_val, argv, 54268 (JSValueConst *)resolving_funcs); 54269 for(i = 0; i < 2; i++) 54270 JS_FreeValue(ctx, resolving_funcs[i]); 54271 if (ret) { 54272 JS_FreeValue(ctx, result_promise); 54273 return JS_EXCEPTION; 54274 } 54275 return result_promise; 54276 } 54277 54278 static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, 54279 int argc, JSValueConst *argv) 54280 { 54281 JSValueConst args[2]; 54282 args[0] = JS_UNDEFINED; 54283 args[1] = argv[0]; 54284 return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); 54285 } 54286 54287 static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, 54288 int argc, JSValueConst *argv, 54289 int magic, JSValue *func_data) 54290 { 54291 return JS_DupValue(ctx, func_data[0]); 54292 } 54293 54294 static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, 54295 int argc, JSValueConst *argv, 54296 int magic, JSValue *func_data) 54297 { 54298 return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); 54299 } 54300 54301 static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, 54302 int argc, JSValueConst *argv, 54303 int magic, JSValue *func_data) 54304 { 54305 JSValueConst ctor = func_data[0]; 54306 JSValueConst onFinally = func_data[1]; 54307 JSValue res, promise, ret, then_func; 54308 54309 res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); 54310 if (JS_IsException(res)) 54311 return res; 54312 promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); 54313 JS_FreeValue(ctx, res); 54314 if (JS_IsException(promise)) 54315 return promise; 54316 if (magic == 0) { 54317 then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, 54318 0, 1, argv); 54319 } else { 54320 then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, 54321 0, 1, argv); 54322 } 54323 if (JS_IsException(then_func)) { 54324 JS_FreeValue(ctx, promise); 54325 return then_func; 54326 } 54327 ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); 54328 JS_FreeValue(ctx, then_func); 54329 return ret; 54330 } 54331 54332 static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, 54333 int argc, JSValueConst *argv) 54334 { 54335 JSValueConst onFinally = argv[0]; 54336 JSValue ctor, ret; 54337 JSValue then_funcs[2]; 54338 JSValueConst func_data[2]; 54339 int i; 54340 54341 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 54342 if (JS_IsException(ctor)) 54343 return ctor; 54344 if (!JS_IsFunction(ctx, onFinally)) { 54345 then_funcs[0] = JS_DupValue(ctx, onFinally); 54346 then_funcs[1] = JS_DupValue(ctx, onFinally); 54347 } else { 54348 func_data[0] = ctor; 54349 func_data[1] = onFinally; 54350 for(i = 0; i < 2; i++) { 54351 then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); 54352 if (JS_IsException(then_funcs[i])) { 54353 if (i == 1) 54354 JS_FreeValue(ctx, then_funcs[0]); 54355 JS_FreeValue(ctx, ctor); 54356 return JS_EXCEPTION; 54357 } 54358 } 54359 } 54360 JS_FreeValue(ctx, ctor); 54361 ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); 54362 JS_FreeValue(ctx, then_funcs[0]); 54363 JS_FreeValue(ctx, then_funcs[1]); 54364 return ret; 54365 } 54366 54367 static const JSCFunctionListEntry js_promise_funcs[] = { 54368 JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), 54369 JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), 54370 JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), 54371 JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), 54372 JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), 54373 JS_CFUNC_DEF("try", 1, js_promise_try ), 54374 JS_CFUNC_DEF("race", 1, js_promise_race ), 54375 JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), 54376 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), 54377 }; 54378 54379 static const JSCFunctionListEntry js_promise_proto_funcs[] = { 54380 JS_CFUNC_DEF("then", 2, js_promise_then ), 54381 JS_CFUNC_DEF("catch", 1, js_promise_catch ), 54382 JS_CFUNC_DEF("finally", 1, js_promise_finally ), 54383 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), 54384 }; 54385 54386 /* AsyncFunction */ 54387 static const JSCFunctionListEntry js_async_function_proto_funcs[] = { 54388 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), 54389 }; 54390 54391 /* AsyncIteratorPrototype */ 54392 54393 static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { 54394 JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), 54395 }; 54396 54397 /* AsyncFromSyncIteratorPrototype */ 54398 54399 typedef struct JSAsyncFromSyncIteratorData { 54400 JSValue sync_iter; 54401 JSValue next_method; 54402 } JSAsyncFromSyncIteratorData; 54403 54404 static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) 54405 { 54406 JSAsyncFromSyncIteratorData *s = 54407 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 54408 if (s) { 54409 JS_FreeValueRT(rt, s->sync_iter); 54410 JS_FreeValueRT(rt, s->next_method); 54411 js_free_rt(rt, s); 54412 } 54413 } 54414 54415 static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, 54416 JS_MarkFunc *mark_func) 54417 { 54418 JSAsyncFromSyncIteratorData *s = 54419 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 54420 if (s) { 54421 JS_MarkValue(rt, s->sync_iter, mark_func); 54422 JS_MarkValue(rt, s->next_method, mark_func); 54423 } 54424 } 54425 54426 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, 54427 JSValueConst sync_iter) 54428 { 54429 JSValue async_iter, next_method; 54430 JSAsyncFromSyncIteratorData *s; 54431 54432 next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); 54433 if (JS_IsException(next_method)) 54434 return JS_EXCEPTION; 54435 async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 54436 if (JS_IsException(async_iter)) { 54437 JS_FreeValue(ctx, next_method); 54438 return async_iter; 54439 } 54440 s = js_mallocz(ctx, sizeof(*s)); 54441 if (!s) { 54442 JS_FreeValue(ctx, async_iter); 54443 JS_FreeValue(ctx, next_method); 54444 return JS_EXCEPTION; 54445 } 54446 s->sync_iter = JS_DupValue(ctx, sync_iter); 54447 s->next_method = next_method; 54448 JS_SetOpaque(async_iter, s); 54449 return async_iter; 54450 } 54451 54452 static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, 54453 JSValueConst this_val, 54454 int argc, JSValueConst *argv, 54455 int magic, JSValue *func_data) 54456 { 54457 return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), 54458 JS_ToBool(ctx, func_data[0])); 54459 } 54460 54461 static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, 54462 BOOL done) 54463 { 54464 JSValueConst func_data[1]; 54465 54466 func_data[0] = (JSValueConst)JS_NewBool(ctx, done); 54467 return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, 54468 1, 0, 1, func_data); 54469 } 54470 54471 static JSValue js_async_from_sync_iterator_close_wrap(JSContext *ctx, 54472 JSValueConst this_val, 54473 int argc, JSValueConst *argv, 54474 int magic, JSValue *func_data) 54475 { 54476 JS_Throw(ctx, JS_DupValue(ctx, argv[0])); 54477 JS_IteratorClose(ctx, func_data[0], TRUE); 54478 return JS_EXCEPTION; 54479 } 54480 54481 static JSValue js_async_from_sync_iterator_close_wrap_func_create(JSContext *ctx, JSValueConst sync_iter) 54482 { 54483 return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_close_wrap, 54484 1, 0, 1, &sync_iter); 54485 } 54486 54487 static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, 54488 int argc, JSValueConst *argv, 54489 int magic) 54490 { 54491 JSValue promise, resolving_funcs[2], value, err, method; 54492 JSAsyncFromSyncIteratorData *s; 54493 int done; 54494 int is_reject; 54495 54496 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 54497 if (JS_IsException(promise)) 54498 return JS_EXCEPTION; 54499 s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 54500 if (!s) { 54501 JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); 54502 goto reject; 54503 } 54504 54505 if (magic == GEN_MAGIC_NEXT) { 54506 method = JS_DupValue(ctx, s->next_method); 54507 } else { 54508 method = JS_GetProperty(ctx, s->sync_iter, 54509 magic == GEN_MAGIC_RETURN ? JS_ATOM_return : 54510 JS_ATOM_throw); 54511 if (JS_IsException(method)) 54512 goto reject; 54513 if (JS_IsUndefined(method) || JS_IsNull(method)) { 54514 if (magic == GEN_MAGIC_RETURN) { 54515 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); 54516 is_reject = 0; 54517 goto done_resolve; 54518 } else { 54519 if (JS_IteratorClose(ctx, s->sync_iter, FALSE)) 54520 goto reject; 54521 JS_ThrowTypeError(ctx, "throw is not a method"); 54522 goto reject; 54523 } 54524 } 54525 } 54526 value = JS_IteratorNext2(ctx, s->sync_iter, method, 54527 argc >= 1 ? 1 : 0, argv, &done); 54528 JS_FreeValue(ctx, method); 54529 if (JS_IsException(value)) 54530 goto reject; 54531 if (done == 2) { 54532 JSValue obj = value; 54533 value = JS_IteratorGetCompleteValue(ctx, obj, &done); 54534 JS_FreeValue(ctx, obj); 54535 if (JS_IsException(value)) 54536 goto reject; 54537 } 54538 54539 if (JS_IsException(value)) 54540 goto reject; 54541 { 54542 JSValue value_wrapper_promise, resolve_reject[2]; 54543 int res; 54544 54545 value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, 54546 1, (JSValueConst *)&value, 0); 54547 if (JS_IsException(value_wrapper_promise)) { 54548 JSValue res2; 54549 JS_FreeValue(ctx, value); 54550 if (magic != GEN_MAGIC_RETURN && !done) { 54551 JS_IteratorClose(ctx, s->sync_iter, TRUE); 54552 } 54553 reject: 54554 err = JS_GetException(ctx); 54555 is_reject = 1; 54556 done_resolve: 54557 res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 54558 1, (JSValueConst *)&err); 54559 JS_FreeValue(ctx, err); 54560 JS_FreeValue(ctx, res2); 54561 JS_FreeValue(ctx, resolving_funcs[0]); 54562 JS_FreeValue(ctx, resolving_funcs[1]); 54563 return promise; 54564 } 54565 54566 resolve_reject[0] = 54567 js_async_from_sync_iterator_unwrap_func_create(ctx, done); 54568 if (JS_IsException(resolve_reject[0])) { 54569 JS_FreeValue(ctx, value_wrapper_promise); 54570 goto fail; 54571 } 54572 if (done || magic == GEN_MAGIC_RETURN) { 54573 resolve_reject[1] = JS_UNDEFINED; 54574 } else { 54575 resolve_reject[1] = 54576 js_async_from_sync_iterator_close_wrap_func_create(ctx, s->sync_iter); 54577 if (JS_IsException(resolve_reject[1])) { 54578 JS_FreeValue(ctx, value_wrapper_promise); 54579 JS_FreeValue(ctx, resolve_reject[0]); 54580 goto fail; 54581 } 54582 } 54583 JS_FreeValue(ctx, value); 54584 res = perform_promise_then(ctx, value_wrapper_promise, 54585 (JSValueConst *)resolve_reject, 54586 (JSValueConst *)resolving_funcs); 54587 JS_FreeValue(ctx, resolve_reject[0]); 54588 JS_FreeValue(ctx, resolve_reject[1]); 54589 JS_FreeValue(ctx, value_wrapper_promise); 54590 JS_FreeValue(ctx, resolving_funcs[0]); 54591 JS_FreeValue(ctx, resolving_funcs[1]); 54592 if (res) { 54593 JS_FreeValue(ctx, promise); 54594 return JS_EXCEPTION; 54595 } 54596 } 54597 return promise; 54598 fail: 54599 JS_FreeValue(ctx, value); 54600 JS_FreeValue(ctx, resolving_funcs[0]); 54601 JS_FreeValue(ctx, resolving_funcs[1]); 54602 JS_FreeValue(ctx, promise); 54603 return JS_EXCEPTION; 54604 } 54605 54606 static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { 54607 JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), 54608 JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), 54609 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), 54610 }; 54611 54612 /* AsyncGeneratorFunction */ 54613 54614 static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { 54615 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), 54616 }; 54617 54618 /* AsyncGenerator prototype */ 54619 54620 static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { 54621 JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), 54622 JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), 54623 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), 54624 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), 54625 }; 54626 54627 static JSClassShortDef const js_async_class_def[] = { 54628 { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ 54629 { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ 54630 { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ 54631 { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ 54632 { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ 54633 { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ 54634 { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ 54635 { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ 54636 { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ 54637 }; 54638 54639 int JS_AddIntrinsicPromise(JSContext *ctx) 54640 { 54641 JSRuntime *rt = ctx->rt; 54642 JSValue obj1; 54643 JSCFunctionType ft; 54644 54645 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { 54646 if (init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, 54647 countof(js_async_class_def))) 54648 return -1; 54649 rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; 54650 rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; 54651 rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; 54652 rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; 54653 rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; 54654 rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; 54655 } 54656 54657 /* Promise */ 54658 obj1 = JS_NewCConstructor(ctx, JS_CLASS_PROMISE, "Promise", 54659 js_promise_constructor, 1, JS_CFUNC_constructor, 0, 54660 JS_UNDEFINED, 54661 js_promise_funcs, countof(js_promise_funcs), 54662 js_promise_proto_funcs, countof(js_promise_proto_funcs), 54663 0); 54664 if (JS_IsException(obj1)) 54665 return -1; 54666 ctx->promise_ctor = obj1; 54667 54668 /* AsyncFunction */ 54669 ft.generic_magic = js_function_constructor; 54670 obj1 = JS_NewCConstructor(ctx, JS_CLASS_ASYNC_FUNCTION, "AsyncFunction", 54671 ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, 54672 ctx->function_ctor, 54673 NULL, 0, 54674 js_async_function_proto_funcs, countof(js_async_function_proto_funcs), 54675 JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY); 54676 if (JS_IsException(obj1)) 54677 return -1; 54678 JS_FreeValue(ctx, obj1); 54679 54680 /* AsyncIteratorPrototype */ 54681 ctx->async_iterator_proto = 54682 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_OBJECT], 54683 js_async_iterator_proto_funcs, 54684 countof(js_async_iterator_proto_funcs)); 54685 if (JS_IsException(ctx->async_iterator_proto)) 54686 return -1; 54687 54688 /* AsyncFromSyncIteratorPrototype */ 54689 ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = 54690 JS_NewObjectProtoList(ctx, ctx->async_iterator_proto, 54691 js_async_from_sync_iterator_proto_funcs, 54692 countof(js_async_from_sync_iterator_proto_funcs)); 54693 if (JS_IsException(ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR])) 54694 return -1; 54695 54696 /* AsyncGeneratorPrototype */ 54697 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = 54698 JS_NewObjectProtoList(ctx, ctx->async_iterator_proto, 54699 js_async_generator_proto_funcs, 54700 countof(js_async_generator_proto_funcs)); 54701 if (JS_IsException(ctx->class_proto[JS_CLASS_ASYNC_GENERATOR])) 54702 return -1; 54703 54704 /* AsyncGeneratorFunction */ 54705 ft.generic_magic = js_function_constructor; 54706 obj1 = JS_NewCConstructor(ctx, JS_CLASS_ASYNC_GENERATOR_FUNCTION, "AsyncGeneratorFunction", 54707 ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC_GENERATOR, 54708 ctx->function_ctor, 54709 NULL, 0, 54710 js_async_generator_function_proto_funcs, countof(js_async_generator_function_proto_funcs), 54711 JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY); 54712 if (JS_IsException(obj1)) 54713 return -1; 54714 JS_FreeValue(ctx, obj1); 54715 54716 return JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], 54717 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], 54718 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); 54719 } 54720 54721 /* URI handling */ 54722 54723 static int string_get_hex(JSString *p, int k, int n) { 54724 int c = 0, h; 54725 while (n-- > 0) { 54726 if ((h = from_hex(string_get(p, k++))) < 0) 54727 return -1; 54728 c = (c << 4) | h; 54729 } 54730 return c; 54731 } 54732 54733 static int isURIReserved(int c) { 54734 return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; 54735 } 54736 54737 static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) 54738 { 54739 va_list ap; 54740 54741 va_start(ap, fmt); 54742 JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap); 54743 va_end(ap); 54744 return -1; 54745 } 54746 54747 static int hex_decode(JSContext *ctx, JSString *p, int k) { 54748 int c; 54749 54750 if (k >= p->len || string_get(p, k) != '%') 54751 return js_throw_URIError(ctx, "expecting %%"); 54752 if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) 54753 return js_throw_URIError(ctx, "expecting hex digit"); 54754 54755 return c; 54756 } 54757 54758 static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, 54759 int argc, JSValueConst *argv, int isComponent) 54760 { 54761 JSValue str; 54762 StringBuffer b_s, *b = &b_s; 54763 JSString *p; 54764 int k, c, c1, n, c_min; 54765 54766 str = JS_ToString(ctx, argv[0]); 54767 if (JS_IsException(str)) 54768 return str; 54769 54770 string_buffer_init(ctx, b, 0); 54771 54772 p = JS_VALUE_GET_STRING(str); 54773 for (k = 0; k < p->len;) { 54774 c = string_get(p, k); 54775 if (c == '%') { 54776 c = hex_decode(ctx, p, k); 54777 if (c < 0) 54778 goto fail; 54779 k += 3; 54780 if (c < 0x80) { 54781 if (!isComponent && isURIReserved(c)) { 54782 c = '%'; 54783 k -= 2; 54784 } 54785 } else { 54786 /* Decode URI-encoded UTF-8 sequence */ 54787 if (c >= 0xc0 && c <= 0xdf) { 54788 n = 1; 54789 c_min = 0x80; 54790 c &= 0x1f; 54791 } else if (c >= 0xe0 && c <= 0xef) { 54792 n = 2; 54793 c_min = 0x800; 54794 c &= 0xf; 54795 } else if (c >= 0xf0 && c <= 0xf7) { 54796 n = 3; 54797 c_min = 0x10000; 54798 c &= 0x7; 54799 } else { 54800 n = 0; 54801 c_min = 1; 54802 c = 0; 54803 } 54804 while (n-- > 0) { 54805 c1 = hex_decode(ctx, p, k); 54806 if (c1 < 0) 54807 goto fail; 54808 k += 3; 54809 if ((c1 & 0xc0) != 0x80) { 54810 c = 0; 54811 break; 54812 } 54813 c = (c << 6) | (c1 & 0x3f); 54814 } 54815 if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { 54816 js_throw_URIError(ctx, "malformed UTF-8"); 54817 goto fail; 54818 } 54819 } 54820 } else { 54821 k++; 54822 } 54823 string_buffer_putc(b, c); 54824 } 54825 JS_FreeValue(ctx, str); 54826 return string_buffer_end(b); 54827 54828 fail: 54829 JS_FreeValue(ctx, str); 54830 string_buffer_free(b); 54831 return JS_EXCEPTION; 54832 } 54833 54834 static int isUnescaped(int c) { 54835 static char const unescaped_chars[] = 54836 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 54837 "abcdefghijklmnopqrstuvwxyz" 54838 "0123456789" 54839 "@*_+-./"; 54840 return c < 0x100 && 54841 memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); 54842 } 54843 54844 static int isURIUnescaped(int c, int isComponent) { 54845 return c < 0x100 && 54846 ((c >= 0x61 && c <= 0x7a) || 54847 (c >= 0x41 && c <= 0x5a) || 54848 (c >= 0x30 && c <= 0x39) || 54849 memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || 54850 (!isComponent && isURIReserved(c))); 54851 } 54852 54853 static int encodeURI_hex(StringBuffer *b, int c) { 54854 uint8_t buf[6]; 54855 int n = 0; 54856 const char *hex = "0123456789ABCDEF"; 54857 54858 buf[n++] = '%'; 54859 if (c >= 256) { 54860 buf[n++] = 'u'; 54861 buf[n++] = hex[(c >> 12) & 15]; 54862 buf[n++] = hex[(c >> 8) & 15]; 54863 } 54864 buf[n++] = hex[(c >> 4) & 15]; 54865 buf[n++] = hex[(c >> 0) & 15]; 54866 return string_buffer_write8(b, buf, n); 54867 } 54868 54869 static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, 54870 int argc, JSValueConst *argv, 54871 int isComponent) 54872 { 54873 JSValue str; 54874 StringBuffer b_s, *b = &b_s; 54875 JSString *p; 54876 int k, c, c1; 54877 54878 str = JS_ToString(ctx, argv[0]); 54879 if (JS_IsException(str)) 54880 return str; 54881 54882 p = JS_VALUE_GET_STRING(str); 54883 string_buffer_init(ctx, b, p->len); 54884 for (k = 0; k < p->len;) { 54885 c = string_get(p, k); 54886 k++; 54887 if (isURIUnescaped(c, isComponent)) { 54888 string_buffer_putc16(b, c); 54889 } else { 54890 if (is_lo_surrogate(c)) { 54891 js_throw_URIError(ctx, "invalid character"); 54892 goto fail; 54893 } else if (is_hi_surrogate(c)) { 54894 if (k >= p->len) { 54895 js_throw_URIError(ctx, "expecting surrogate pair"); 54896 goto fail; 54897 } 54898 c1 = string_get(p, k); 54899 k++; 54900 if (!is_lo_surrogate(c1)) { 54901 js_throw_URIError(ctx, "expecting surrogate pair"); 54902 goto fail; 54903 } 54904 c = from_surrogate(c, c1); 54905 } 54906 if (c < 0x80) { 54907 encodeURI_hex(b, c); 54908 } else { 54909 /* XXX: use C UTF-8 conversion ? */ 54910 if (c < 0x800) { 54911 encodeURI_hex(b, (c >> 6) | 0xc0); 54912 } else { 54913 if (c < 0x10000) { 54914 encodeURI_hex(b, (c >> 12) | 0xe0); 54915 } else { 54916 encodeURI_hex(b, (c >> 18) | 0xf0); 54917 encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); 54918 } 54919 encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); 54920 } 54921 encodeURI_hex(b, (c & 0x3f) | 0x80); 54922 } 54923 } 54924 } 54925 JS_FreeValue(ctx, str); 54926 return string_buffer_end(b); 54927 54928 fail: 54929 JS_FreeValue(ctx, str); 54930 string_buffer_free(b); 54931 return JS_EXCEPTION; 54932 } 54933 54934 static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, 54935 int argc, JSValueConst *argv) 54936 { 54937 JSValue str; 54938 StringBuffer b_s, *b = &b_s; 54939 JSString *p; 54940 int i, len, c; 54941 54942 str = JS_ToString(ctx, argv[0]); 54943 if (JS_IsException(str)) 54944 return str; 54945 54946 p = JS_VALUE_GET_STRING(str); 54947 string_buffer_init(ctx, b, p->len); 54948 for (i = 0, len = p->len; i < len; i++) { 54949 c = string_get(p, i); 54950 if (isUnescaped(c)) { 54951 string_buffer_putc16(b, c); 54952 } else { 54953 encodeURI_hex(b, c); 54954 } 54955 } 54956 JS_FreeValue(ctx, str); 54957 return string_buffer_end(b); 54958 } 54959 54960 static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, 54961 int argc, JSValueConst *argv) 54962 { 54963 JSValue str; 54964 StringBuffer b_s, *b = &b_s; 54965 JSString *p; 54966 int i, len, c, n; 54967 54968 str = JS_ToString(ctx, argv[0]); 54969 if (JS_IsException(str)) 54970 return str; 54971 54972 string_buffer_init(ctx, b, 0); 54973 p = JS_VALUE_GET_STRING(str); 54974 for (i = 0, len = p->len; i < len; i++) { 54975 c = string_get(p, i); 54976 if (c == '%') { 54977 if (i + 6 <= len 54978 && string_get(p, i + 1) == 'u' 54979 && (n = string_get_hex(p, i + 2, 4)) >= 0) { 54980 c = n; 54981 i += 6 - 1; 54982 } else 54983 if (i + 3 <= len 54984 && (n = string_get_hex(p, i + 1, 2)) >= 0) { 54985 c = n; 54986 i += 3 - 1; 54987 } 54988 } 54989 string_buffer_putc16(b, c); 54990 } 54991 JS_FreeValue(ctx, str); 54992 return string_buffer_end(b); 54993 } 54994 54995 /* global object */ 54996 54997 static const JSCFunctionListEntry js_global_funcs[] = { 54998 JS_CFUNC_DEF("parseInt", 2, js_parseInt ), 54999 JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), 55000 JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), 55001 JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), 55002 55003 JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), 55004 JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), 55005 JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ), 55006 JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), 55007 JS_CFUNC_DEF("escape", 1, js_global_escape ), 55008 JS_CFUNC_DEF("unescape", 1, js_global_unescape ), 55009 JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), 55010 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), 55011 JS_PROP_UNDEFINED_DEF("undefined", 0 ), 55012 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), 55013 JS_CFUNC_DEF("eval", 1, js_global_eval ), 55014 }; 55015 55016 /* Date */ 55017 55018 static int64_t math_mod(int64_t a, int64_t b) { 55019 /* return positive modulo */ 55020 int64_t m = a % b; 55021 return m + (m < 0) * b; 55022 } 55023 55024 static int64_t floor_div(int64_t a, int64_t b) { 55025 /* integer division rounding toward -Infinity */ 55026 int64_t m = a % b; 55027 return (a - (m + (m < 0) * b)) / b; 55028 } 55029 55030 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, 55031 int argc, JSValueConst *argv); 55032 55033 static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) 55034 { 55035 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 55036 JSObject *p = JS_VALUE_GET_OBJ(this_val); 55037 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) 55038 return JS_ToFloat64(ctx, valp, p->u.object_data); 55039 } 55040 JS_ThrowTypeError(ctx, "not a Date object"); 55041 return -1; 55042 } 55043 55044 static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) 55045 { 55046 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 55047 JSObject *p = JS_VALUE_GET_OBJ(this_val); 55048 if (p->class_id == JS_CLASS_DATE) { 55049 JS_FreeValue(ctx, p->u.object_data); 55050 p->u.object_data = JS_NewFloat64(ctx, v); 55051 return JS_DupValue(ctx, p->u.object_data); 55052 } 55053 } 55054 return JS_ThrowTypeError(ctx, "not a Date object"); 55055 } 55056 55057 static int64_t days_from_year(int64_t y) { 55058 return 365 * (y - 1970) + floor_div(y - 1969, 4) - 55059 floor_div(y - 1901, 100) + floor_div(y - 1601, 400); 55060 } 55061 55062 static int64_t days_in_year(int64_t y) { 55063 return 365 + !(y % 4) - !(y % 100) + !(y % 400); 55064 } 55065 55066 /* return the year, update days */ 55067 static int64_t year_from_days(int64_t *days) { 55068 int64_t y, d1, nd, d = *days; 55069 y = floor_div(d * 10000, 3652425) + 1970; 55070 /* the initial approximation is very good, so only a few 55071 iterations are necessary */ 55072 for(;;) { 55073 d1 = d - days_from_year(y); 55074 if (d1 < 0) { 55075 y--; 55076 d1 += days_in_year(y); 55077 } else { 55078 nd = days_in_year(y); 55079 if (d1 < nd) 55080 break; 55081 d1 -= nd; 55082 y++; 55083 } 55084 } 55085 *days = d1; 55086 return y; 55087 } 55088 55089 static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 55090 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 55091 static char const day_names[] = "SunMonTueWedThuFriSat"; 55092 55093 static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, 55094 double fields[minimum_length(9)], int is_local, int force) 55095 { 55096 double dval; 55097 int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; 55098 55099 if (JS_ThisTimeValue(ctx, &dval, obj)) 55100 return -1; 55101 55102 if (isnan(dval)) { 55103 if (!force) 55104 return FALSE; /* NaN */ 55105 d = 0; /* initialize all fields to 0 */ 55106 } else { 55107 d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ 55108 if (is_local) { 55109 tz = -getTimezoneOffset(d); 55110 d += tz * 60000; 55111 } 55112 } 55113 55114 /* result is >= 0, we can use % */ 55115 h = math_mod(d, 86400000); 55116 days = (d - h) / 86400000; 55117 ms = h % 1000; 55118 h = (h - ms) / 1000; 55119 s = h % 60; 55120 h = (h - s) / 60; 55121 m = h % 60; 55122 h = (h - m) / 60; 55123 wd = math_mod(days + 4, 7); /* week day */ 55124 y = year_from_days(&days); 55125 55126 for(i = 0; i < 11; i++) { 55127 md = month_days[i]; 55128 if (i == 1) 55129 md += days_in_year(y) - 365; 55130 if (days < md) 55131 break; 55132 days -= md; 55133 } 55134 fields[0] = y; 55135 fields[1] = i; 55136 fields[2] = days + 1; 55137 fields[3] = h; 55138 fields[4] = m; 55139 fields[5] = s; 55140 fields[6] = ms; 55141 fields[7] = wd; 55142 fields[8] = tz; 55143 return TRUE; 55144 } 55145 55146 static double time_clip(double t) { 55147 if (t >= -8.64e15 && t <= 8.64e15) 55148 return trunc(t) + 0.0; /* convert -0 to +0 */ 55149 else 55150 return NAN; 55151 } 55152 55153 /* The spec mandates the use of 'double' and it specifies the order 55154 of the operations */ 55155 static double set_date_fields(double fields[minimum_length(7)], int is_local) { 55156 double y, m, dt, ym, mn, day, h, s, milli, time, tv; 55157 int yi, mi, i; 55158 int64_t days; 55159 volatile double temp; /* enforce evaluation order */ 55160 55161 /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ 55162 y = fields[0]; 55163 m = fields[1]; 55164 dt = fields[2]; 55165 ym = y + floor(m / 12); 55166 mn = fmod(m, 12); 55167 if (mn < 0) 55168 mn += 12; 55169 if (ym < -271821 || ym > 275760) 55170 return NAN; 55171 55172 yi = ym; 55173 mi = mn; 55174 days = days_from_year(yi); 55175 for(i = 0; i < mi; i++) { 55176 days += month_days[i]; 55177 if (i == 1) 55178 days += days_in_year(yi) - 365; 55179 } 55180 day = days + dt - 1; 55181 55182 /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ 55183 h = fields[3]; 55184 m = fields[4]; 55185 s = fields[5]; 55186 milli = fields[6]; 55187 /* Use a volatile intermediary variable to ensure order of evaluation 55188 * as specified in ECMA. This fixes a test262 error on 55189 * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. 55190 * Without the volatile qualifier, the compile can generate code 55191 * that performs the computation in a different order or with instructions 55192 * that produce a different result such as FMA (float multiply and add). 55193 */ 55194 time = h * 3600000; 55195 time += (temp = m * 60000); 55196 time += (temp = s * 1000); 55197 time += milli; 55198 55199 /* emulate 21.4.1.16 MakeDate ( day, time ) */ 55200 tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ 55201 if (!isfinite(tv)) 55202 return NAN; 55203 55204 /* adjust for local time and clip */ 55205 if (is_local) { 55206 int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; 55207 tv += getTimezoneOffset(ti) * 60000; 55208 } 55209 return time_clip(tv); 55210 } 55211 55212 static double set_date_fields_checked(double fields[minimum_length(7)], int is_local) 55213 { 55214 int i; 55215 double a; 55216 for(i = 0; i < 7; i++) { 55217 a = fields[i]; 55218 if (!isfinite(a)) 55219 return NAN; 55220 fields[i] = trunc(a); 55221 if (i == 0 && fields[0] >= 0 && fields[0] < 100) 55222 fields[0] += 1900; 55223 } 55224 return set_date_fields(fields, is_local); 55225 } 55226 55227 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, 55228 int argc, JSValueConst *argv, int magic) 55229 { 55230 // get_date_field(obj, n, is_local) 55231 double fields[9]; 55232 int res, n, is_local; 55233 55234 is_local = magic & 0x0F; 55235 n = (magic >> 4) & 0x0F; 55236 res = get_date_fields(ctx, this_val, fields, is_local, 0); 55237 if (res < 0) 55238 return JS_EXCEPTION; 55239 if (!res) 55240 return JS_NAN; 55241 55242 if (magic & 0x100) { // getYear 55243 fields[0] -= 1900; 55244 } 55245 return JS_NewFloat64(ctx, fields[n]); 55246 } 55247 55248 static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, 55249 int argc, JSValueConst *argv, int magic) 55250 { 55251 // _field(obj, first_field, end_field, args, is_local) 55252 double fields[9]; 55253 int res, first_field, end_field, is_local, i, n, res1; 55254 double d, a; 55255 55256 d = NAN; 55257 first_field = (magic >> 8) & 0x0F; 55258 end_field = (magic >> 4) & 0x0F; 55259 is_local = magic & 0x0F; 55260 55261 res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); 55262 if (res < 0) 55263 return JS_EXCEPTION; 55264 res1 = res; 55265 55266 // Argument coercion is observable and must be done unconditionally. 55267 n = min_int(argc, end_field - first_field); 55268 for(i = 0; i < n; i++) { 55269 if (JS_ToFloat64(ctx, &a, argv[i])) 55270 return JS_EXCEPTION; 55271 if (!isfinite(a)) 55272 res = FALSE; 55273 fields[first_field + i] = trunc(a); 55274 } 55275 55276 if (!res1) 55277 return JS_NAN; /* thisTimeValue is NaN */ 55278 55279 if (res && argc > 0) 55280 d = set_date_fields(fields, is_local); 55281 55282 return JS_SetThisTimeValue(ctx, this_val, d); 55283 } 55284 55285 /* fmt: 55286 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" 55287 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" 55288 2: toISOString: "2018-01-02T23:02:56.927Z" 55289 3: toLocaleString: "1/2/2018, 11:40:40 PM" 55290 part: 1=date, 2=time 3=all 55291 XXX: should use a variant of strftime(). 55292 */ 55293 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, 55294 int argc, JSValueConst *argv, int magic) 55295 { 55296 // _string(obj, fmt, part) 55297 char buf[64]; 55298 double fields[9]; 55299 int res, fmt, part, pos; 55300 int y, mon, d, h, m, s, ms, wd, tz; 55301 55302 fmt = (magic >> 4) & 0x0F; 55303 part = magic & 0x0F; 55304 55305 res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); 55306 if (res < 0) 55307 return JS_EXCEPTION; 55308 if (!res) { 55309 if (fmt == 2) 55310 return JS_ThrowRangeError(ctx, "Date value is NaN"); 55311 else 55312 return js_new_string8(ctx, "Invalid Date"); 55313 } 55314 55315 y = fields[0]; 55316 mon = fields[1]; 55317 d = fields[2]; 55318 h = fields[3]; 55319 m = fields[4]; 55320 s = fields[5]; 55321 ms = fields[6]; 55322 wd = fields[7]; 55323 tz = fields[8]; 55324 55325 pos = 0; 55326 55327 if (part & 1) { /* date part */ 55328 switch(fmt) { 55329 case 0: 55330 pos += snprintf(buf + pos, sizeof(buf) - pos, 55331 "%.3s, %02d %.3s %0*d ", 55332 day_names + wd * 3, d, 55333 month_names + mon * 3, 4 + (y < 0), y); 55334 break; 55335 case 1: 55336 pos += snprintf(buf + pos, sizeof(buf) - pos, 55337 "%.3s %.3s %02d %0*d", 55338 day_names + wd * 3, 55339 month_names + mon * 3, d, 4 + (y < 0), y); 55340 if (part == 3) { 55341 buf[pos++] = ' '; 55342 } 55343 break; 55344 case 2: 55345 if (y >= 0 && y <= 9999) { 55346 pos += snprintf(buf + pos, sizeof(buf) - pos, 55347 "%04d", y); 55348 } else { 55349 pos += snprintf(buf + pos, sizeof(buf) - pos, 55350 "%+07d", y); 55351 } 55352 pos += snprintf(buf + pos, sizeof(buf) - pos, 55353 "-%02d-%02dT", mon + 1, d); 55354 break; 55355 case 3: 55356 pos += snprintf(buf + pos, sizeof(buf) - pos, 55357 "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); 55358 if (part == 3) { 55359 buf[pos++] = ','; 55360 buf[pos++] = ' '; 55361 } 55362 break; 55363 } 55364 } 55365 if (part & 2) { /* time part */ 55366 switch(fmt) { 55367 case 0: 55368 pos += snprintf(buf + pos, sizeof(buf) - pos, 55369 "%02d:%02d:%02d GMT", h, m, s); 55370 break; 55371 case 1: 55372 pos += snprintf(buf + pos, sizeof(buf) - pos, 55373 "%02d:%02d:%02d GMT", h, m, s); 55374 if (tz < 0) { 55375 buf[pos++] = '-'; 55376 tz = -tz; 55377 } else { 55378 buf[pos++] = '+'; 55379 } 55380 /* tz is >= 0, can use % */ 55381 pos += snprintf(buf + pos, sizeof(buf) - pos, 55382 "%02d%02d", tz / 60, tz % 60); 55383 /* XXX: tack the time zone code? */ 55384 break; 55385 case 2: 55386 pos += snprintf(buf + pos, sizeof(buf) - pos, 55387 "%02d:%02d:%02d.%03dZ", h, m, s, ms); 55388 break; 55389 case 3: 55390 pos += snprintf(buf + pos, sizeof(buf) - pos, 55391 "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, 55392 (h < 12) ? 'A' : 'P'); 55393 break; 55394 } 55395 } 55396 return JS_NewStringLen(ctx, buf, pos); 55397 } 55398 55399 /* OS dependent: return the UTC time in ms since 1970. */ 55400 static int64_t date_now(void) { 55401 struct timeval tv; 55402 gettimeofday(&tv, NULL); 55403 return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); 55404 } 55405 55406 static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, 55407 int argc, JSValueConst *argv) 55408 { 55409 // Date(y, mon, d, h, m, s, ms) 55410 JSValue rv; 55411 int i, n; 55412 double val; 55413 55414 if (JS_IsUndefined(new_target)) { 55415 /* invoked as function */ 55416 argc = 0; 55417 } 55418 n = argc; 55419 if (n == 0) { 55420 val = date_now(); 55421 } else if (n == 1) { 55422 JSValue v, dv; 55423 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { 55424 JSObject *p = JS_VALUE_GET_OBJ(argv[0]); 55425 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { 55426 if (JS_ToFloat64(ctx, &val, p->u.object_data)) 55427 return JS_EXCEPTION; 55428 val = time_clip(val); 55429 goto has_val; 55430 } 55431 } 55432 v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); 55433 if (JS_IsString(v)) { 55434 dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); 55435 JS_FreeValue(ctx, v); 55436 if (JS_IsException(dv)) 55437 return JS_EXCEPTION; 55438 if (JS_ToFloat64Free(ctx, &val, dv)) 55439 return JS_EXCEPTION; 55440 } else { 55441 if (JS_ToFloat64Free(ctx, &val, v)) 55442 return JS_EXCEPTION; 55443 } 55444 val = time_clip(val); 55445 } else { 55446 double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; 55447 if (n > 7) 55448 n = 7; 55449 for(i = 0; i < n; i++) { 55450 if (JS_ToFloat64(ctx, &fields[i], argv[i])) 55451 return JS_EXCEPTION; 55452 } 55453 val = set_date_fields_checked(fields, 1); 55454 } 55455 has_val: 55456 #if 0 55457 JSValueConst args[3]; 55458 args[0] = new_target; 55459 args[1] = ctx->class_proto[JS_CLASS_DATE]; 55460 args[2] = JS_NewFloat64(ctx, val); 55461 rv = js___date_create(ctx, JS_UNDEFINED, 3, args); 55462 #else 55463 rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); 55464 if (!JS_IsException(rv)) 55465 JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); 55466 #endif 55467 if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { 55468 /* invoked as a function, return (new Date()).toString(); */ 55469 JSValue s; 55470 s = get_date_string(ctx, rv, 0, NULL, 0x13); 55471 JS_FreeValue(ctx, rv); 55472 rv = s; 55473 } 55474 return rv; 55475 } 55476 55477 static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, 55478 int argc, JSValueConst *argv) 55479 { 55480 // UTC(y, mon, d, h, m, s, ms) 55481 double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; 55482 int i, n; 55483 55484 n = argc; 55485 if (n == 0) 55486 return JS_NAN; 55487 if (n > 7) 55488 n = 7; 55489 for(i = 0; i < n; i++) { 55490 if (JS_ToFloat64(ctx, &fields[i], argv[i])) 55491 return JS_EXCEPTION; 55492 } 55493 return JS_NewFloat64(ctx, set_date_fields_checked(fields, 0)); 55494 } 55495 55496 /* Date string parsing */ 55497 55498 static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { 55499 if (sp[*pp] == c) { 55500 *pp += 1; 55501 return TRUE; 55502 } else { 55503 return FALSE; 55504 } 55505 } 55506 55507 /* skip spaces, update offset, return next char */ 55508 static int string_skip_spaces(const uint8_t *sp, int *pp) { 55509 int c; 55510 while ((c = sp[*pp]) == ' ') 55511 *pp += 1; 55512 return c; 55513 } 55514 55515 /* skip dashes dots and commas */ 55516 static int string_skip_separators(const uint8_t *sp, int *pp) { 55517 int c; 55518 while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') 55519 *pp += 1; 55520 return c; 55521 } 55522 55523 /* skip a word, stop on spaces, digits and separators, update offset */ 55524 static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) { 55525 int c; 55526 while (!strchr(stoplist, c = sp[*pp])) 55527 *pp += 1; 55528 return c; 55529 } 55530 55531 /* parse a numeric field (max_digits = 0 -> no maximum) */ 55532 static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, 55533 int min_digits, int max_digits) 55534 { 55535 int v = 0; 55536 int c, p = *pp, p_start; 55537 55538 p_start = p; 55539 while ((c = sp[p]) >= '0' && c <= '9') { 55540 /* arbitrary limit to 9 digits */ 55541 if (v >= 100000000) 55542 return FALSE; 55543 v = v * 10 + c - '0'; 55544 p++; 55545 if (p - p_start == max_digits) 55546 break; 55547 } 55548 if (p - p_start < min_digits) 55549 return FALSE; 55550 *pval = v; 55551 *pp = p; 55552 return TRUE; 55553 } 55554 55555 static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) { 55556 /* parse optional fractional part as milliseconds and truncate. */ 55557 /* spec does not indicate which rounding should be used */ 55558 int mul = 100, ms = 0, c, p_start, p = *pp; 55559 55560 c = sp[p]; 55561 if (c == '.' || c == ',') { 55562 p++; 55563 p_start = p; 55564 while ((c = sp[p]) >= '0' && c <= '9') { 55565 ms += (c - '0') * mul; 55566 mul /= 10; 55567 p++; 55568 if (p - p_start == 9) 55569 break; 55570 } 55571 if (p > p_start) { 55572 /* only consume the separator if digits are present */ 55573 *pval = ms; 55574 *pp = p; 55575 } 55576 } 55577 return TRUE; 55578 } 55579 55580 static uint8_t upper_ascii(uint8_t c) { 55581 return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; 55582 } 55583 55584 static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) { 55585 int tz = 0, sgn, hh, mm, p = *pp; 55586 55587 sgn = sp[p++]; 55588 if (sgn == '+' || sgn == '-') { 55589 int n = p; 55590 if (!string_get_digits(sp, &p, &hh, 1, 0)) 55591 return FALSE; 55592 n = p - n; 55593 if (strict && n != 2 && n != 4) 55594 return FALSE; 55595 while (n > 4) { 55596 n -= 2; 55597 hh /= 100; 55598 } 55599 if (n > 2) { 55600 mm = hh % 100; 55601 hh = hh / 100; 55602 } else { 55603 mm = 0; 55604 if (string_skip_char(sp, &p, ':')) { 55605 /* optional separator */ 55606 if (!string_get_digits(sp, &p, &mm, 2, 2)) 55607 return FALSE; 55608 } else { 55609 if (strict) 55610 return FALSE; /* [+-]HH is not accepted in strict mode */ 55611 } 55612 } 55613 if (hh > 23 || mm > 59) 55614 return FALSE; 55615 tz = hh * 60 + mm; 55616 if (sgn != '+') 55617 tz = -tz; 55618 } else 55619 if (sgn != 'Z') { 55620 return FALSE; 55621 } 55622 *pp = p; 55623 *tzp = tz; 55624 return TRUE; 55625 } 55626 55627 static BOOL string_match(const uint8_t *sp, int *pp, const char *s) { 55628 int p = *pp; 55629 while (*s != '\0') { 55630 if (upper_ascii(sp[p]) != upper_ascii(*s++)) 55631 return FALSE; 55632 p++; 55633 } 55634 *pp = p; 55635 return TRUE; 55636 } 55637 55638 static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { 55639 int n, i; 55640 55641 for (n = 0; n < count; n++) { 55642 for (i = 0;; i++) { 55643 if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i])) 55644 break; 55645 if (i == 2) 55646 return n; 55647 } 55648 } 55649 return -1; 55650 } 55651 55652 static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { 55653 int n; 55654 55655 n = find_abbrev(sp, *pp, month_names, 12); 55656 if (n < 0) 55657 return FALSE; 55658 55659 *pval = n + 1; 55660 *pp += 3; 55661 return TRUE; 55662 } 55663 55664 /* parse toISOString format */ 55665 static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { 55666 int sgn, i, p = 0; 55667 55668 /* initialize fields to the beginning of the Epoch */ 55669 for (i = 0; i < 9; i++) { 55670 fields[i] = (i == 2); 55671 } 55672 *is_local = FALSE; 55673 55674 /* year is either yyyy digits or [+-]yyyyyy */ 55675 sgn = sp[p]; 55676 if (sgn == '-' || sgn == '+') { 55677 p++; 55678 if (!string_get_digits(sp, &p, &fields[0], 6, 6)) 55679 return FALSE; 55680 if (sgn == '-') { 55681 if (fields[0] == 0) 55682 return FALSE; // reject -000000 55683 fields[0] = -fields[0]; 55684 } 55685 } else { 55686 if (!string_get_digits(sp, &p, &fields[0], 4, 4)) 55687 return FALSE; 55688 } 55689 if (string_skip_char(sp, &p, '-')) { 55690 if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */ 55691 return FALSE; 55692 if (fields[1] < 1) 55693 return FALSE; 55694 fields[1] -= 1; 55695 if (string_skip_char(sp, &p, '-')) { 55696 if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */ 55697 return FALSE; 55698 if (fields[2] < 1) 55699 return FALSE; 55700 } 55701 } 55702 if (string_skip_char(sp, &p, 'T')) { 55703 *is_local = TRUE; 55704 if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */ 55705 || !string_skip_char(sp, &p, ':') 55706 || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */ 55707 fields[3] = 100; // reject unconditionally 55708 return TRUE; 55709 } 55710 if (string_skip_char(sp, &p, ':')) { 55711 if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */ 55712 return FALSE; 55713 string_get_milliseconds(sp, &p, &fields[6]); 55714 } 55715 } 55716 /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ 55717 if (sp[p]) { 55718 *is_local = FALSE; 55719 if (!string_get_tzoffset(sp, &p, &fields[8], TRUE)) 55720 return FALSE; 55721 } 55722 /* error if extraneous characters */ 55723 return sp[p] == '\0'; 55724 } 55725 55726 static struct { 55727 char name[6]; 55728 int16_t offset; 55729 } const js_tzabbr[] = { 55730 { "GMT", 0 }, // Greenwich Mean Time 55731 { "UTC", 0 }, // Coordinated Universal Time 55732 { "UT", 0 }, // Universal Time 55733 { "Z", 0 }, // Zulu Time 55734 { "EDT", -4 * 60 }, // Eastern Daylight Time 55735 { "EST", -5 * 60 }, // Eastern Standard Time 55736 { "CDT", -5 * 60 }, // Central Daylight Time 55737 { "CST", -6 * 60 }, // Central Standard Time 55738 { "MDT", -6 * 60 }, // Mountain Daylight Time 55739 { "MST", -7 * 60 }, // Mountain Standard Time 55740 { "PDT", -7 * 60 }, // Pacific Daylight Time 55741 { "PST", -8 * 60 }, // Pacific Standard Time 55742 { "WET", +0 * 60 }, // Western European Time 55743 { "WEST", +1 * 60 }, // Western European Summer Time 55744 { "CET", +1 * 60 }, // Central European Time 55745 { "CEST", +2 * 60 }, // Central European Summer Time 55746 { "EET", +2 * 60 }, // Eastern European Time 55747 { "EEST", +3 * 60 }, // Eastern European Summer Time 55748 }; 55749 55750 static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) { 55751 for (size_t i = 0; i < countof(js_tzabbr); i++) { 55752 if (string_match(sp, pp, js_tzabbr[i].name)) { 55753 *offset = js_tzabbr[i].offset; 55754 return TRUE; 55755 } 55756 } 55757 return FALSE; 55758 } 55759 55760 /* parse toString, toUTCString and other formats */ 55761 static BOOL js_date_parse_otherstring(const uint8_t *sp, 55762 int fields[minimum_length(9)], 55763 BOOL *is_local) { 55764 int c, i, val, p = 0, p_start; 55765 int num[3]; 55766 BOOL has_year = FALSE; 55767 BOOL has_mon = FALSE; 55768 BOOL has_time = FALSE; 55769 int num_index = 0; 55770 55771 /* initialize fields to the beginning of 2001-01-01 */ 55772 fields[0] = 2001; 55773 fields[1] = 1; 55774 fields[2] = 1; 55775 for (i = 3; i < 9; i++) { 55776 fields[i] = 0; 55777 } 55778 *is_local = TRUE; 55779 55780 while (string_skip_spaces(sp, &p)) { 55781 p_start = p; 55782 if ((c = sp[p]) == '+' || c == '-') { 55783 if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) { 55784 *is_local = FALSE; 55785 } else { 55786 p++; 55787 if (string_get_digits(sp, &p, &val, 1, 0)) { 55788 if (c == '-') { 55789 if (val == 0) 55790 return FALSE; 55791 val = -val; 55792 } 55793 fields[0] = val; 55794 has_year = TRUE; 55795 } 55796 } 55797 } else 55798 if (string_get_digits(sp, &p, &val, 1, 0)) { 55799 if (string_skip_char(sp, &p, ':')) { 55800 /* time part */ 55801 fields[3] = val; 55802 if (!string_get_digits(sp, &p, &fields[4], 1, 2)) 55803 return FALSE; 55804 if (string_skip_char(sp, &p, ':')) { 55805 if (!string_get_digits(sp, &p, &fields[5], 1, 2)) 55806 return FALSE; 55807 string_get_milliseconds(sp, &p, &fields[6]); 55808 } 55809 has_time = TRUE; 55810 if ((sp[p] == '+' || sp[p] == '-') && 55811 string_get_tzoffset(sp, &p, &fields[8], FALSE)) { 55812 *is_local = FALSE; 55813 } 55814 } else { 55815 if (p - p_start > 2 && !has_year) { 55816 fields[0] = val; 55817 has_year = TRUE; 55818 } else 55819 if ((val < 1 || val > 31) && !has_year) { 55820 fields[0] = val + (val < 100) * 1900 + (val < 50) * 100; 55821 has_year = TRUE; 55822 } else { 55823 if (num_index == 3) 55824 return FALSE; 55825 num[num_index++] = val; 55826 } 55827 } 55828 } else 55829 if (string_get_month(sp, &p, &fields[1])) { 55830 has_mon = TRUE; 55831 string_skip_until(sp, &p, "0123456789 -/("); 55832 } else 55833 if (has_time && string_match(sp, &p, "PM")) { 55834 if (fields[3] < 12) 55835 fields[3] += 12; 55836 continue; 55837 } else 55838 if (has_time && string_match(sp, &p, "AM")) { 55839 if (fields[3] == 12) 55840 fields[3] -= 12; 55841 continue; 55842 } else 55843 if (string_get_tzabbr(sp, &p, &fields[8])) { 55844 *is_local = FALSE; 55845 continue; 55846 } else 55847 if (c == '(') { /* skip parenthesized phrase */ 55848 int level = 0; 55849 while ((c = sp[p]) != '\0') { 55850 p++; 55851 level += (c == '('); 55852 level -= (c == ')'); 55853 if (!level) 55854 break; 55855 } 55856 if (level > 0) 55857 return FALSE; 55858 } else 55859 if (c == ')') { 55860 return FALSE; 55861 } else { 55862 if (has_year + has_mon + has_time + num_index) 55863 return FALSE; 55864 /* skip a word */ 55865 string_skip_until(sp, &p, " -/("); 55866 } 55867 string_skip_separators(sp, &p); 55868 } 55869 if (num_index + has_year + has_mon > 3) 55870 return FALSE; 55871 55872 switch (num_index) { 55873 case 0: 55874 if (!has_year) 55875 return FALSE; 55876 break; 55877 case 1: 55878 if (has_mon) 55879 fields[2] = num[0]; 55880 else 55881 fields[1] = num[0]; 55882 break; 55883 case 2: 55884 if (has_year) { 55885 fields[1] = num[0]; 55886 fields[2] = num[1]; 55887 } else 55888 if (has_mon) { 55889 fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100; 55890 fields[2] = num[0]; 55891 } else { 55892 fields[1] = num[0]; 55893 fields[2] = num[1]; 55894 } 55895 break; 55896 case 3: 55897 fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; 55898 fields[1] = num[0]; 55899 fields[2] = num[1]; 55900 break; 55901 default: 55902 return FALSE; 55903 } 55904 if (fields[1] < 1 || fields[2] < 1) 55905 return FALSE; 55906 fields[1] -= 1; 55907 return TRUE; 55908 } 55909 55910 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, 55911 int argc, JSValueConst *argv) 55912 { 55913 JSValue s, rv; 55914 int fields[9]; 55915 double fields1[9]; 55916 double d; 55917 int i, c; 55918 JSString *sp; 55919 uint8_t buf[128]; 55920 BOOL is_local; 55921 55922 rv = JS_NAN; 55923 55924 s = JS_ToString(ctx, argv[0]); 55925 if (JS_IsException(s)) 55926 return JS_EXCEPTION; 55927 55928 sp = JS_VALUE_GET_STRING(s); 55929 /* convert the string as a byte array */ 55930 for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) { 55931 c = string_get(sp, i); 55932 if (c > 255) 55933 c = (c == 0x2212) ? '-' : 'x'; 55934 buf[i] = c; 55935 } 55936 buf[i] = '\0'; 55937 if (js_date_parse_isostring(buf, fields, &is_local) 55938 || js_date_parse_otherstring(buf, fields, &is_local)) { 55939 static int const field_max[6] = { 0, 11, 31, 24, 59, 59 }; 55940 BOOL valid = TRUE; 55941 /* check field maximum values */ 55942 for (i = 1; i < 6; i++) { 55943 if (fields[i] > field_max[i]) 55944 valid = FALSE; 55945 } 55946 /* special case 24:00:00.000 */ 55947 if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) 55948 valid = FALSE; 55949 if (valid) { 55950 for(i = 0; i < 7; i++) 55951 fields1[i] = fields[i]; 55952 d = set_date_fields(fields1, is_local) - fields[8] * 60000; 55953 rv = JS_NewFloat64(ctx, d); 55954 } 55955 } 55956 JS_FreeValue(ctx, s); 55957 return rv; 55958 } 55959 55960 static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, 55961 int argc, JSValueConst *argv) 55962 { 55963 // now() 55964 return JS_NewInt64(ctx, date_now()); 55965 } 55966 55967 static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, 55968 int argc, JSValueConst *argv) 55969 { 55970 // Symbol_toPrimitive(hint) 55971 JSValueConst obj = this_val; 55972 JSAtom hint = JS_ATOM_NULL; 55973 int hint_num; 55974 55975 if (!JS_IsObject(obj)) 55976 return JS_ThrowTypeErrorNotAnObject(ctx); 55977 55978 if (JS_IsString(argv[0])) { 55979 hint = JS_ValueToAtom(ctx, argv[0]); 55980 if (hint == JS_ATOM_NULL) 55981 return JS_EXCEPTION; 55982 JS_FreeAtom(ctx, hint); 55983 } 55984 switch (hint) { 55985 case JS_ATOM_number: 55986 case JS_ATOM_integer: 55987 hint_num = HINT_NUMBER; 55988 break; 55989 case JS_ATOM_string: 55990 case JS_ATOM_default: 55991 hint_num = HINT_STRING; 55992 break; 55993 default: 55994 return JS_ThrowTypeError(ctx, "invalid hint"); 55995 } 55996 return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); 55997 } 55998 55999 static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, 56000 int argc, JSValueConst *argv) 56001 { 56002 // getTimezoneOffset() 56003 double v; 56004 56005 if (JS_ThisTimeValue(ctx, &v, this_val)) 56006 return JS_EXCEPTION; 56007 if (isnan(v)) 56008 return JS_NAN; 56009 else 56010 /* assuming -8.64e15 <= v <= -8.64e15 */ 56011 return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); 56012 } 56013 56014 static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, 56015 int argc, JSValueConst *argv) 56016 { 56017 // getTime() 56018 double v; 56019 56020 if (JS_ThisTimeValue(ctx, &v, this_val)) 56021 return JS_EXCEPTION; 56022 return JS_NewFloat64(ctx, v); 56023 } 56024 56025 static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, 56026 int argc, JSValueConst *argv) 56027 { 56028 // setTime(v) 56029 double v; 56030 56031 if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) 56032 return JS_EXCEPTION; 56033 return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); 56034 } 56035 56036 static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, 56037 int argc, JSValueConst *argv) 56038 { 56039 // setYear(y) 56040 double y; 56041 JSValueConst args[1]; 56042 56043 if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) 56044 return JS_EXCEPTION; 56045 y = +y; 56046 if (isfinite(y)) { 56047 y = trunc(y); 56048 if (y >= 0 && y < 100) 56049 y += 1900; 56050 } 56051 args[0] = JS_NewFloat64(ctx, y); 56052 return set_date_field(ctx, this_val, 1, args, 0x011); 56053 } 56054 56055 static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, 56056 int argc, JSValueConst *argv) 56057 { 56058 // toJSON(key) 56059 JSValue obj, tv, method, rv; 56060 double d; 56061 56062 rv = JS_EXCEPTION; 56063 tv = JS_UNDEFINED; 56064 56065 obj = JS_ToObject(ctx, this_val); 56066 tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); 56067 if (JS_IsException(tv)) 56068 goto exception; 56069 if (JS_IsNumber(tv)) { 56070 if (JS_ToFloat64(ctx, &d, tv) < 0) 56071 goto exception; 56072 if (!isfinite(d)) { 56073 rv = JS_NULL; 56074 goto done; 56075 } 56076 } 56077 method = JS_GetProperty(ctx, obj, JS_ATOM_toISOString); 56078 if (JS_IsException(method)) 56079 goto exception; 56080 if (!JS_IsFunction(ctx, method)) { 56081 JS_ThrowTypeError(ctx, "object needs toISOString method"); 56082 JS_FreeValue(ctx, method); 56083 goto exception; 56084 } 56085 rv = JS_CallFree(ctx, method, obj, 0, NULL); 56086 exception: 56087 done: 56088 JS_FreeValue(ctx, obj); 56089 JS_FreeValue(ctx, tv); 56090 return rv; 56091 } 56092 56093 static const JSCFunctionListEntry js_date_funcs[] = { 56094 JS_CFUNC_DEF("now", 0, js_Date_now ), 56095 JS_CFUNC_DEF("parse", 1, js_Date_parse ), 56096 JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), 56097 }; 56098 56099 static const JSCFunctionListEntry js_date_proto_funcs[] = { 56100 JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), 56101 JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), 56102 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), 56103 JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), 56104 JS_ALIAS_DEF("toGMTString", "toUTCString" ), 56105 JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), 56106 JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), 56107 JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), 56108 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), 56109 JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), 56110 JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), 56111 JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), 56112 JS_CFUNC_DEF("getTime", 0, js_date_getTime ), 56113 JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), 56114 JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), 56115 JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), 56116 JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), 56117 JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), 56118 JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), 56119 JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), 56120 JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), 56121 JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), 56122 JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), 56123 JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), 56124 JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), 56125 JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), 56126 JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), 56127 JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), 56128 JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), 56129 JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), 56130 JS_CFUNC_DEF("setTime", 1, js_date_setTime ), 56131 JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), 56132 JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), 56133 JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), 56134 JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), 56135 JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), 56136 JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), 56137 JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), 56138 JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), 56139 JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), 56140 JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), 56141 JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), 56142 JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), 56143 JS_CFUNC_DEF("setYear", 1, js_date_setYear ), 56144 JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), 56145 JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), 56146 JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), 56147 }; 56148 56149 JSValue JS_NewDate(JSContext *ctx, double epoch_ms) 56150 { 56151 JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); 56152 if (JS_IsException(obj)) 56153 return JS_EXCEPTION; 56154 JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms))); 56155 return obj; 56156 } 56157 56158 int JS_AddIntrinsicDate(JSContext *ctx) 56159 { 56160 JSValue obj; 56161 56162 /* Date */ 56163 obj = JS_NewCConstructor(ctx, JS_CLASS_DATE, "Date", 56164 js_date_constructor, 7, JS_CFUNC_constructor_or_func, 0, 56165 JS_UNDEFINED, 56166 js_date_funcs, countof(js_date_funcs), 56167 js_date_proto_funcs, countof(js_date_proto_funcs), 56168 0); 56169 if (JS_IsException(obj)) 56170 return -1; 56171 JS_FreeValue(ctx, obj); 56172 return 0; 56173 } 56174 56175 /* eval */ 56176 56177 int JS_AddIntrinsicEval(JSContext *ctx) 56178 { 56179 ctx->eval_internal = __JS_EvalInternal; 56180 return 0; 56181 } 56182 56183 /* BigInt */ 56184 56185 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) 56186 { 56187 uint32_t tag; 56188 56189 redo: 56190 tag = JS_VALUE_GET_NORM_TAG(val); 56191 switch(tag) { 56192 case JS_TAG_INT: 56193 case JS_TAG_BOOL: 56194 val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); 56195 break; 56196 case JS_TAG_SHORT_BIG_INT: 56197 case JS_TAG_BIG_INT: 56198 break; 56199 case JS_TAG_FLOAT64: 56200 { 56201 double d = JS_VALUE_GET_FLOAT64(val); 56202 JSBigInt *r; 56203 int res; 56204 r = js_bigint_from_float64(ctx, &res, d); 56205 if (!r) { 56206 if (res == 0) { 56207 val = JS_EXCEPTION; 56208 } else if (res == 1) { 56209 val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); 56210 } else { 56211 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } 56212 } else { 56213 val = JS_CompactBigInt(ctx, r); 56214 } 56215 } 56216 break; 56217 case JS_TAG_STRING: 56218 case JS_TAG_STRING_ROPE: 56219 val = JS_StringToBigIntErr(ctx, val); 56220 break; 56221 case JS_TAG_OBJECT: 56222 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 56223 if (JS_IsException(val)) 56224 break; 56225 goto redo; 56226 case JS_TAG_NULL: 56227 case JS_TAG_UNDEFINED: 56228 default: 56229 JS_FreeValue(ctx, val); 56230 return JS_ThrowTypeError(ctx, "cannot convert to BigInt"); 56231 } 56232 return val; 56233 } 56234 56235 static JSValue js_bigint_constructor(JSContext *ctx, 56236 JSValueConst new_target, 56237 int argc, JSValueConst *argv) 56238 { 56239 if (!JS_IsUndefined(new_target)) 56240 return JS_ThrowTypeErrorNotAConstructor(ctx, new_target); 56241 return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); 56242 } 56243 56244 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) 56245 { 56246 if (JS_IsBigInt(ctx, this_val)) 56247 return JS_DupValue(ctx, this_val); 56248 56249 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 56250 JSObject *p = JS_VALUE_GET_OBJ(this_val); 56251 if (p->class_id == JS_CLASS_BIG_INT) { 56252 if (JS_IsBigInt(ctx, p->u.object_data)) 56253 return JS_DupValue(ctx, p->u.object_data); 56254 } 56255 } 56256 return JS_ThrowTypeError(ctx, "not a BigInt"); 56257 } 56258 56259 static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, 56260 int argc, JSValueConst *argv) 56261 { 56262 JSValue val; 56263 int base; 56264 JSValue ret; 56265 56266 val = js_thisBigIntValue(ctx, this_val); 56267 if (JS_IsException(val)) 56268 return val; 56269 if (argc == 0 || JS_IsUndefined(argv[0])) { 56270 base = 10; 56271 } else { 56272 base = js_get_radix(ctx, argv[0]); 56273 if (base < 0) 56274 goto fail; 56275 } 56276 ret = js_bigint_to_string1(ctx, val, base); 56277 JS_FreeValue(ctx, val); 56278 return ret; 56279 fail: 56280 JS_FreeValue(ctx, val); 56281 return JS_EXCEPTION; 56282 } 56283 56284 static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, 56285 int argc, JSValueConst *argv) 56286 { 56287 return js_thisBigIntValue(ctx, this_val); 56288 } 56289 56290 static JSValue js_bigint_asUintN(JSContext *ctx, 56291 JSValueConst this_val, 56292 int argc, JSValueConst *argv, int asIntN) 56293 { 56294 uint64_t bits; 56295 JSValue res, a; 56296 56297 if (JS_ToIndex(ctx, &bits, argv[0])) 56298 return JS_EXCEPTION; 56299 a = JS_ToBigInt(ctx, argv[1]); 56300 if (JS_IsException(a)) 56301 return JS_EXCEPTION; 56302 if (bits == 0) { 56303 JS_FreeValue(ctx, a); 56304 res = __JS_NewShortBigInt(ctx, 0); 56305 } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) { 56306 /* fast case */ 56307 if (bits >= JS_SHORT_BIG_INT_BITS) { 56308 res = a; 56309 } else { 56310 uint64_t v; 56311 int shift; 56312 shift = 64 - bits; 56313 v = JS_VALUE_GET_SHORT_BIG_INT(a); 56314 v = v << shift; 56315 if (asIntN) 56316 v = (int64_t)v >> shift; 56317 else 56318 v = v >> shift; 56319 res = __JS_NewShortBigInt(ctx, v); 56320 } 56321 } else { 56322 JSBigInt *r, *p = JS_VALUE_GET_PTR(a); 56323 if (bits >= p->len * JS_LIMB_BITS) { 56324 res = a; 56325 } else { 56326 int len, shift, i; 56327 js_limb_t v; 56328 len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS; 56329 r = js_bigint_new(ctx, len); 56330 if (!r) { 56331 JS_FreeValue(ctx, a); 56332 return JS_EXCEPTION; 56333 } 56334 r->len = len; 56335 for(i = 0; i < len - 1; i++) 56336 r->tab[i] = p->tab[i]; 56337 shift = (-bits) & (JS_LIMB_BITS - 1); 56338 /* 0 <= shift <= JS_LIMB_BITS - 1 */ 56339 v = p->tab[len - 1] << shift; 56340 if (asIntN) 56341 v = (js_slimb_t)v >> shift; 56342 else 56343 v = v >> shift; 56344 r->tab[len - 1] = v; 56345 r = js_bigint_normalize(ctx, r); 56346 JS_FreeValue(ctx, a); 56347 res = JS_CompactBigInt(ctx, r); 56348 } 56349 } 56350 return res; 56351 } 56352 56353 static const JSCFunctionListEntry js_bigint_funcs[] = { 56354 JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), 56355 JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), 56356 }; 56357 56358 static const JSCFunctionListEntry js_bigint_proto_funcs[] = { 56359 JS_CFUNC_DEF("toString", 0, js_bigint_toString ), 56360 JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), 56361 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), 56362 }; 56363 56364 static int JS_AddIntrinsicBigInt(JSContext *ctx) 56365 { 56366 JSValue obj1; 56367 56368 obj1 = JS_NewCConstructor(ctx, JS_CLASS_BIG_INT, "BigInt", 56369 js_bigint_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56370 JS_UNDEFINED, 56371 js_bigint_funcs, countof(js_bigint_funcs), 56372 js_bigint_proto_funcs, countof(js_bigint_proto_funcs), 56373 0); 56374 if (JS_IsException(obj1)) 56375 return -1; 56376 JS_FreeValue(ctx, obj1); 56377 return 0; 56378 } 56379 56380 /* Minimum amount of objects to be able to compile code and display 56381 error messages. */ 56382 static int JS_AddIntrinsicBasicObjects(JSContext *ctx) 56383 { 56384 JSValue obj; 56385 JSCFunctionType ft; 56386 int i; 56387 56388 /* warning: ordering is tricky */ 56389 ctx->class_proto[JS_CLASS_OBJECT] = 56390 JS_NewObjectProtoClassAlloc(ctx, JS_NULL, JS_CLASS_OBJECT, 56391 countof(js_object_proto_funcs) + 1); 56392 if (JS_IsException(ctx->class_proto[JS_CLASS_OBJECT])) 56393 return -1; 56394 JS_SetImmutablePrototype(ctx, ctx->class_proto[JS_CLASS_OBJECT]); 56395 56396 /* 2 more properties: caller and arguments */ 56397 ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, 56398 JS_CFUNC_generic, 0, 56399 ctx->class_proto[JS_CLASS_OBJECT], 56400 countof(js_function_proto_funcs) + 3 + 2); 56401 if (JS_IsException(ctx->function_proto)) 56402 return -1; 56403 ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto); 56404 56405 ctx->global_obj = JS_NewObjectProtoClassAlloc(ctx, ctx->class_proto[JS_CLASS_OBJECT], 56406 JS_CLASS_GLOBAL_OBJECT, 64); 56407 if (JS_IsException(ctx->global_obj)) 56408 return -1; 56409 { 56410 JSObject *p; 56411 obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL, JS_CLASS_OBJECT, 4); 56412 p = JS_VALUE_GET_OBJ(ctx->global_obj); 56413 p->u.global_object.uninitialized_vars = obj; 56414 } 56415 ctx->global_var_obj = JS_NewObjectProtoClassAlloc(ctx, JS_NULL, 56416 JS_CLASS_OBJECT, 16); 56417 if (JS_IsException(ctx->global_var_obj)) 56418 return -1; 56419 56420 /* Error */ 56421 ft.generic_magic = js_error_constructor; 56422 obj = JS_NewCConstructor(ctx, JS_CLASS_ERROR, "Error", 56423 ft.generic, 1, JS_CFUNC_constructor_or_func_magic, -1, 56424 JS_UNDEFINED, 56425 js_error_funcs, countof(js_error_funcs), 56426 js_error_proto_funcs, countof(js_error_proto_funcs), 56427 0); 56428 if (JS_IsException(obj)) 56429 return -1; 56430 56431 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 56432 JSValue func_obj; 56433 const JSCFunctionListEntry *funcs; 56434 int n_args; 56435 char buf[ATOM_GET_STR_BUF_SIZE]; 56436 const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), 56437 JS_ATOM_EvalError + i); 56438 n_args = 1 + (i == JS_AGGREGATE_ERROR); 56439 funcs = js_native_error_proto_funcs + 2 * i; 56440 func_obj = JS_NewCConstructor(ctx, -1, name, 56441 ft.generic, n_args, JS_CFUNC_constructor_or_func_magic, i, 56442 obj, 56443 NULL, 0, 56444 funcs, 2, 56445 0); 56446 if (JS_IsException(func_obj)) { 56447 JS_FreeValue(ctx, obj); 56448 return -1; 56449 } 56450 ctx->native_error_proto[i] = JS_GetProperty(ctx, func_obj, JS_ATOM_prototype); 56451 JS_FreeValue(ctx, func_obj); 56452 if (JS_IsException(ctx->native_error_proto[i])) { 56453 JS_FreeValue(ctx, obj); 56454 return -1; 56455 } 56456 } 56457 JS_FreeValue(ctx, obj); 56458 56459 /* Array */ 56460 obj = JS_NewCConstructor(ctx, JS_CLASS_ARRAY, "Array", 56461 js_array_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56462 JS_UNDEFINED, 56463 js_array_funcs, countof(js_array_funcs), 56464 js_array_proto_funcs, countof(js_array_proto_funcs), 56465 JS_NEW_CTOR_PROTO_CLASS); 56466 if (JS_IsException(obj)) 56467 return -1; 56468 ctx->array_ctor = obj; 56469 56470 { 56471 JSObject *p = JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_ARRAY]); 56472 p->is_std_array_prototype = TRUE; 56473 } 56474 56475 ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), 56476 JS_PROP_INITIAL_HASH_SIZE, 1); 56477 if (!ctx->array_shape) 56478 return -1; 56479 if (add_shape_property(ctx, &ctx->array_shape, NULL, 56480 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH)) 56481 return -1; 56482 56483 ctx->arguments_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_OBJECT]), 56484 JS_PROP_INITIAL_HASH_SIZE, 3); 56485 if (!ctx->arguments_shape) 56486 return -1; 56487 if (add_shape_property(ctx, &ctx->arguments_shape, NULL, 56488 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) 56489 return -1; 56490 if (add_shape_property(ctx, &ctx->arguments_shape, NULL, 56491 JS_ATOM_Symbol_iterator, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) 56492 return -1; 56493 if (add_shape_property(ctx, &ctx->arguments_shape, NULL, 56494 JS_ATOM_callee, JS_PROP_GETSET)) 56495 return -1; 56496 56497 ctx->mapped_arguments_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_OBJECT]), 56498 JS_PROP_INITIAL_HASH_SIZE, 3); 56499 if (!ctx->mapped_arguments_shape) 56500 return -1; 56501 if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL, 56502 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) 56503 return -1; 56504 if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL, 56505 JS_ATOM_Symbol_iterator, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) 56506 return -1; 56507 if (add_shape_property(ctx, &ctx->mapped_arguments_shape, NULL, 56508 JS_ATOM_callee, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) 56509 return -1; 56510 56511 return 0; 56512 } 56513 56514 int JS_AddIntrinsicBaseObjects(JSContext *ctx) 56515 { 56516 JSValue obj1, obj2; 56517 JSCFunctionType ft; 56518 56519 ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); 56520 if (JS_IsException(ctx->throw_type_error)) 56521 return -1; 56522 /* add caller and arguments properties to throw a TypeError */ 56523 if (JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, 56524 ctx->throw_type_error, ctx->throw_type_error, 56525 JS_PROP_HAS_GET | JS_PROP_HAS_SET | 56526 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE) < 0) 56527 return -1; 56528 if (JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, 56529 ctx->throw_type_error, ctx->throw_type_error, 56530 JS_PROP_HAS_GET | JS_PROP_HAS_SET | 56531 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE) < 0) 56532 return -1; 56533 JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); 56534 56535 /* Object */ 56536 obj1 = JS_NewCConstructor(ctx, JS_CLASS_OBJECT, "Object", 56537 js_object_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56538 JS_UNDEFINED, 56539 js_object_funcs, countof(js_object_funcs), 56540 js_object_proto_funcs, countof(js_object_proto_funcs), 56541 JS_NEW_CTOR_PROTO_EXIST); 56542 if (JS_IsException(obj1)) 56543 return -1; 56544 JS_FreeValue(ctx, obj1); 56545 56546 /* Function */ 56547 ft.generic_magic = js_function_constructor; 56548 obj1 = JS_NewCConstructor(ctx, JS_CLASS_BYTECODE_FUNCTION, "Function", 56549 ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_NORMAL, 56550 JS_UNDEFINED, 56551 NULL, 0, 56552 js_function_proto_funcs, countof(js_function_proto_funcs), 56553 JS_NEW_CTOR_PROTO_EXIST); 56554 if (JS_IsException(obj1)) 56555 return -1; 56556 ctx->function_ctor = obj1; 56557 56558 /* Iterator */ 56559 obj2 = JS_NewCConstructor(ctx, JS_CLASS_ITERATOR, "Iterator", 56560 js_iterator_constructor, 0, JS_CFUNC_constructor_or_func, 0, 56561 JS_UNDEFINED, 56562 js_iterator_funcs, countof(js_iterator_funcs), 56563 js_iterator_proto_funcs, countof(js_iterator_proto_funcs), 56564 0); 56565 if (JS_IsException(obj2)) 56566 return -1; 56567 // quirk: Iterator.prototype.constructor is an accessor property 56568 // TODO(bnoordhuis) mildly inefficient because JS_NewGlobalCConstructor 56569 // first creates a .constructor value property that we then replace with 56570 // an accessor 56571 obj1 = JS_NewCFunctionData(ctx, js_iterator_constructor_getset, 56572 0, 0, 1, (JSValueConst *)&obj2); 56573 if (JS_IsException(obj1)) { 56574 JS_FreeValue(ctx, obj2); 56575 return -1; 56576 } 56577 if (JS_DefineProperty(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56578 JS_ATOM_constructor, JS_UNDEFINED, 56579 obj1, obj1, 56580 JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_CONFIGURABLE) < 0) { 56581 JS_FreeValue(ctx, obj2); 56582 JS_FreeValue(ctx, obj1); 56583 return -1; 56584 } 56585 JS_FreeValue(ctx, obj1); 56586 ctx->iterator_ctor = obj2; 56587 56588 ctx->class_proto[JS_CLASS_ITERATOR_CONCAT] = 56589 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56590 js_iterator_concat_proto_funcs, 56591 countof(js_iterator_concat_proto_funcs)); 56592 if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_CONCAT])) 56593 return -1; 56594 ctx->class_proto[JS_CLASS_ITERATOR_HELPER] = 56595 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56596 js_iterator_helper_proto_funcs, 56597 countof(js_iterator_helper_proto_funcs)); 56598 if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_HELPER])) 56599 return -1; 56600 56601 ctx->class_proto[JS_CLASS_ITERATOR_WRAP] = 56602 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56603 js_iterator_wrap_proto_funcs, 56604 countof(js_iterator_wrap_proto_funcs)); 56605 if (JS_IsException(ctx->class_proto[JS_CLASS_ITERATOR_WRAP])) 56606 return -1; 56607 56608 /* needed to initialize arguments[Symbol.iterator] */ 56609 ctx->array_proto_values = 56610 JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); 56611 if (JS_IsException(ctx->array_proto_values)) 56612 return -1; 56613 56614 ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = 56615 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56616 js_array_iterator_proto_funcs, 56617 countof(js_array_iterator_proto_funcs)); 56618 if (JS_IsException(ctx->class_proto[JS_CLASS_ARRAY_ITERATOR])) 56619 return -1; 56620 56621 /* parseFloat and parseInteger must be defined before Number 56622 because of the Number.parseFloat and Number.parseInteger 56623 aliases */ 56624 if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, 56625 countof(js_global_funcs))) 56626 return -1; 56627 56628 /* Number */ 56629 obj1 = JS_NewCConstructor(ctx, JS_CLASS_NUMBER, "Number", 56630 js_number_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56631 JS_UNDEFINED, 56632 js_number_funcs, countof(js_number_funcs), 56633 js_number_proto_funcs, countof(js_number_proto_funcs), 56634 JS_NEW_CTOR_PROTO_CLASS); 56635 if (JS_IsException(obj1)) 56636 return -1; 56637 JS_FreeValue(ctx, obj1); 56638 if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0))) 56639 return -1; 56640 56641 /* Boolean */ 56642 obj1 = JS_NewCConstructor(ctx, JS_CLASS_BOOLEAN, "Boolean", 56643 js_boolean_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56644 JS_UNDEFINED, 56645 NULL, 0, 56646 js_boolean_proto_funcs, countof(js_boolean_proto_funcs), 56647 JS_NEW_CTOR_PROTO_CLASS); 56648 if (JS_IsException(obj1)) 56649 return -1; 56650 JS_FreeValue(ctx, obj1); 56651 if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE))) 56652 return -1; 56653 56654 /* String */ 56655 obj1 = JS_NewCConstructor(ctx, JS_CLASS_STRING, "String", 56656 js_string_constructor, 1, JS_CFUNC_constructor_or_func, 0, 56657 JS_UNDEFINED, 56658 js_string_funcs, countof(js_string_funcs), 56659 js_string_proto_funcs, countof(js_string_proto_funcs), 56660 JS_NEW_CTOR_PROTO_CLASS); 56661 if (JS_IsException(obj1)) 56662 return -1; 56663 JS_FreeValue(ctx, obj1); 56664 if (JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string))) 56665 return -1; 56666 56667 ctx->class_proto[JS_CLASS_STRING_ITERATOR] = 56668 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56669 js_string_iterator_proto_funcs, 56670 countof(js_string_iterator_proto_funcs)); 56671 if (JS_IsException(ctx->class_proto[JS_CLASS_STRING_ITERATOR])) 56672 return -1; 56673 56674 /* Math: create as autoinit object */ 56675 js_random_init(ctx); 56676 if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj))) 56677 return -1; 56678 56679 /* ES6 Reflect: create as autoinit object */ 56680 if (JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj))) 56681 return -1; 56682 56683 /* ES6 Symbol */ 56684 obj1 = JS_NewCConstructor(ctx, JS_CLASS_SYMBOL, "Symbol", 56685 js_symbol_constructor, 0, JS_CFUNC_constructor_or_func, 0, 56686 JS_UNDEFINED, 56687 js_symbol_funcs, countof(js_symbol_funcs), 56688 js_symbol_proto_funcs, countof(js_symbol_proto_funcs), 56689 0); 56690 if (JS_IsException(obj1)) 56691 return -1; 56692 JS_FreeValue(ctx, obj1); 56693 56694 /* ES6 Generator */ 56695 ctx->class_proto[JS_CLASS_GENERATOR] = 56696 JS_NewObjectProtoList(ctx, ctx->class_proto[JS_CLASS_ITERATOR], 56697 js_generator_proto_funcs, 56698 countof(js_generator_proto_funcs)); 56699 if (JS_IsException(ctx->class_proto[JS_CLASS_GENERATOR])) 56700 return -1; 56701 56702 ft.generic_magic = js_function_constructor; 56703 obj1 = JS_NewCConstructor(ctx, JS_CLASS_GENERATOR_FUNCTION, "GeneratorFunction", 56704 ft.generic, 1, JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR, 56705 ctx->function_ctor, 56706 NULL, 0, 56707 js_generator_function_proto_funcs, 56708 countof(js_generator_function_proto_funcs), 56709 JS_NEW_CTOR_NO_GLOBAL | JS_NEW_CTOR_READONLY); 56710 if (JS_IsException(obj1)) 56711 return -1; 56712 JS_FreeValue(ctx, obj1); 56713 if (JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], 56714 ctx->class_proto[JS_CLASS_GENERATOR], 56715 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE)) 56716 return -1; 56717 56718 /* global properties */ 56719 ctx->eval_obj = JS_GetProperty(ctx, ctx->global_obj, JS_ATOM_eval); 56720 if (JS_IsException(ctx->eval_obj)) 56721 return -1; 56722 56723 if (JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, 56724 JS_DupValue(ctx, ctx->global_obj), 56725 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE) < 0) 56726 return -1; 56727 56728 /* BigInt */ 56729 if (JS_AddIntrinsicBigInt(ctx)) 56730 return -1; 56731 return 0; 56732 } 56733 56734 /* Typed Arrays */ 56735 56736 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { 56737 0, 0, 0, 1, 1, 2, 2, 56738 3, 3, // BigInt64Array, BigUint64Array 56739 1, 2, 3 // Float16Array, Float32Array, Float64Array 56740 }; 56741 56742 static JSValue js_array_buffer_constructor3(JSContext *ctx, 56743 JSValueConst new_target, 56744 uint64_t len, uint64_t *max_len, 56745 JSClassID class_id, 56746 uint8_t *buf, 56747 JSFreeArrayBufferDataFunc *free_func, 56748 void *opaque, BOOL alloc_flag) 56749 { 56750 JSRuntime *rt = ctx->rt; 56751 JSValue obj; 56752 JSArrayBuffer *abuf = NULL; 56753 uint64_t sab_alloc_len; 56754 56755 if (!alloc_flag && buf && max_len && free_func != js_array_buffer_free) { 56756 // not observable from JS land, only through C API misuse; 56757 // JS code cannot create externally managed buffers directly 56758 return JS_ThrowInternalError(ctx, 56759 "resizable ArrayBuffers not supported " 56760 "for externally managed buffers"); 56761 } 56762 obj = js_create_from_ctor(ctx, new_target, class_id); 56763 if (JS_IsException(obj)) 56764 return obj; 56765 /* XXX: we are currently limited to 2 GB */ 56766 if (len > INT32_MAX) { 56767 JS_ThrowRangeError(ctx, "invalid array buffer length"); 56768 goto fail; 56769 } 56770 if (max_len && *max_len > INT32_MAX) { 56771 JS_ThrowRangeError(ctx, "invalid max array buffer length"); 56772 goto fail; 56773 } 56774 abuf = js_malloc(ctx, sizeof(*abuf)); 56775 if (!abuf) 56776 goto fail; 56777 abuf->byte_length = len; 56778 abuf->max_byte_length = max_len ? *max_len : -1; 56779 if (alloc_flag) { 56780 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && 56781 rt->sab_funcs.sab_alloc) { 56782 // TOOD(bnoordhuis) resizing backing memory for SABs atomically 56783 // is hard so we cheat and allocate |maxByteLength| bytes upfront 56784 sab_alloc_len = max_len ? *max_len : len; 56785 abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, 56786 max_int(sab_alloc_len, 1)); 56787 if (!abuf->data) 56788 goto fail; 56789 memset(abuf->data, 0, sab_alloc_len); 56790 } else { 56791 /* the allocation must be done after the object creation */ 56792 abuf->data = js_mallocz(ctx, max_int(len, 1)); 56793 if (!abuf->data) 56794 goto fail; 56795 } 56796 } else { 56797 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && 56798 rt->sab_funcs.sab_dup) { 56799 rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); 56800 } 56801 abuf->data = buf; 56802 } 56803 init_list_head(&abuf->array_list); 56804 abuf->detached = FALSE; 56805 abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); 56806 abuf->opaque = opaque; 56807 abuf->free_func = free_func; 56808 if (alloc_flag && buf) 56809 memcpy(abuf->data, buf, len); 56810 JS_SetOpaque(obj, abuf); 56811 return obj; 56812 fail: 56813 JS_FreeValue(ctx, obj); 56814 js_free(ctx, abuf); 56815 return JS_EXCEPTION; 56816 } 56817 56818 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) 56819 { 56820 js_free_rt(rt, ptr); 56821 } 56822 56823 static JSValue js_array_buffer_constructor2(JSContext *ctx, 56824 JSValueConst new_target, 56825 uint64_t len, uint64_t *max_len, 56826 JSClassID class_id) 56827 { 56828 return js_array_buffer_constructor3(ctx, new_target, len, max_len, class_id, 56829 NULL, js_array_buffer_free, NULL, 56830 TRUE); 56831 } 56832 56833 static JSValue js_array_buffer_constructor1(JSContext *ctx, 56834 JSValueConst new_target, 56835 uint64_t len, uint64_t *max_len) 56836 { 56837 return js_array_buffer_constructor2(ctx, new_target, len, max_len, 56838 JS_CLASS_ARRAY_BUFFER); 56839 } 56840 56841 JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, 56842 JSFreeArrayBufferDataFunc *free_func, void *opaque, 56843 BOOL is_shared) 56844 { 56845 JSClassID class_id = 56846 is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER; 56847 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, class_id, 56848 buf, free_func, opaque, FALSE); 56849 } 56850 56851 /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ 56852 JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) 56853 { 56854 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, 56855 JS_CLASS_ARRAY_BUFFER, 56856 (uint8_t *)buf, 56857 js_array_buffer_free, NULL, 56858 TRUE); 56859 } 56860 56861 static JSValue js_array_buffer_constructor0(JSContext *ctx, JSValueConst new_target, 56862 int argc, JSValueConst *argv, 56863 JSClassID class_id) 56864 { 56865 uint64_t len, max_len, *pmax_len = NULL; 56866 JSValue obj, val; 56867 int64_t i; 56868 56869 if (JS_ToIndex(ctx, &len, argv[0])) 56870 return JS_EXCEPTION; 56871 if (argc < 2) 56872 goto next; 56873 if (!JS_IsObject(argv[1])) 56874 goto next; 56875 obj = JS_ToObject(ctx, argv[1]); 56876 if (JS_IsException(obj)) 56877 return JS_EXCEPTION; 56878 val = JS_GetProperty(ctx, obj, JS_ATOM_maxByteLength); 56879 JS_FreeValue(ctx, obj); 56880 if (JS_IsException(val)) 56881 return JS_EXCEPTION; 56882 if (JS_IsUndefined(val)) 56883 goto next; 56884 if (JS_ToInt64Free(ctx, &i, val)) 56885 return JS_EXCEPTION; 56886 // don't have to check i < 0 because len >= 0 56887 if (len > i || i > MAX_SAFE_INTEGER) 56888 return JS_ThrowRangeError(ctx, "invalid array buffer max length"); 56889 max_len = i; 56890 pmax_len = &max_len; 56891 next: 56892 return js_array_buffer_constructor2(ctx, new_target, len, pmax_len, 56893 class_id); 56894 } 56895 56896 static JSValue js_array_buffer_constructor(JSContext *ctx, 56897 JSValueConst new_target, 56898 int argc, JSValueConst *argv) 56899 { 56900 return js_array_buffer_constructor0(ctx, new_target, argc, argv, 56901 JS_CLASS_ARRAY_BUFFER); 56902 } 56903 56904 static JSValue js_shared_array_buffer_constructor(JSContext *ctx, 56905 JSValueConst new_target, 56906 int argc, JSValueConst *argv) 56907 { 56908 return js_array_buffer_constructor0(ctx, new_target, argc, argv, 56909 JS_CLASS_SHARED_ARRAY_BUFFER); 56910 } 56911 56912 /* also used for SharedArrayBuffer */ 56913 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) 56914 { 56915 JSObject *p = JS_VALUE_GET_OBJ(val); 56916 JSArrayBuffer *abuf = p->u.array_buffer; 56917 struct list_head *el, *el1; 56918 56919 if (abuf) { 56920 /* The ArrayBuffer finalizer may be called before the typed 56921 array finalizers using it, so abuf->array_list is not 56922 necessarily empty. */ 56923 list_for_each_safe(el, el1, &abuf->array_list) { 56924 JSTypedArray *ta; 56925 JSObject *p1; 56926 56927 ta = list_entry(el, JSTypedArray, link); 56928 ta->link.prev = NULL; 56929 ta->link.next = NULL; 56930 p1 = ta->obj; 56931 /* Note: the typed array length and offset fields are not modified */ 56932 if (p1->class_id != JS_CLASS_DATAVIEW) { 56933 p1->u.array.count = 0; 56934 p1->u.array.u.ptr = NULL; 56935 } 56936 } 56937 if (abuf->shared && rt->sab_funcs.sab_free) { 56938 rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); 56939 } else { 56940 if (abuf->free_func) 56941 abuf->free_func(rt, abuf->opaque, abuf->data); 56942 } 56943 js_free_rt(rt, abuf); 56944 } 56945 } 56946 56947 static JSValue js_array_buffer_isView(JSContext *ctx, 56948 JSValueConst this_val, 56949 int argc, JSValueConst *argv) 56950 { 56951 JSObject *p; 56952 BOOL res; 56953 res = FALSE; 56954 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { 56955 p = JS_VALUE_GET_OBJ(argv[0]); 56956 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 56957 p->class_id <= JS_CLASS_DATAVIEW) { 56958 res = TRUE; 56959 } 56960 } 56961 return JS_NewBool(ctx, res); 56962 } 56963 56964 static const JSCFunctionListEntry js_array_buffer_funcs[] = { 56965 JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ), 56966 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 56967 }; 56968 56969 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) 56970 { 56971 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); 56972 } 56973 56974 static JSValue JS_ThrowTypeErrorArrayBufferOOB(JSContext *ctx) 56975 { 56976 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached or resized"); 56977 } 56978 56979 // #sec-get-arraybuffer.prototype.detached 56980 static JSValue js_array_buffer_get_detached(JSContext *ctx, 56981 JSValueConst this_val) 56982 { 56983 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); 56984 if (!abuf) 56985 return JS_EXCEPTION; 56986 if (abuf->shared) 56987 return JS_ThrowTypeError(ctx, "detached called on SharedArrayBuffer"); 56988 return JS_NewBool(ctx, abuf->detached); 56989 } 56990 56991 static JSValue js_array_buffer_get_byteLength(JSContext *ctx, 56992 JSValueConst this_val, 56993 int class_id) 56994 { 56995 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); 56996 if (!abuf) 56997 return JS_EXCEPTION; 56998 /* return 0 if detached */ 56999 return JS_NewUint32(ctx, abuf->byte_length); 57000 } 57001 57002 static JSValue js_array_buffer_get_maxByteLength(JSContext *ctx, 57003 JSValueConst this_val, 57004 int class_id) 57005 { 57006 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); 57007 if (!abuf) 57008 return JS_EXCEPTION; 57009 if (array_buffer_is_resizable(abuf)) 57010 return JS_NewUint32(ctx, abuf->max_byte_length); 57011 return JS_NewUint32(ctx, abuf->byte_length); 57012 } 57013 57014 static JSValue js_array_buffer_get_resizable(JSContext *ctx, 57015 JSValueConst this_val, 57016 int class_id) 57017 { 57018 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); 57019 if (!abuf) 57020 return JS_EXCEPTION; 57021 return JS_NewBool(ctx, array_buffer_is_resizable(abuf)); 57022 } 57023 57024 static void js_array_buffer_update_typed_arrays(JSArrayBuffer *abuf) 57025 { 57026 uint32_t size_log2, size_elem; 57027 struct list_head *el; 57028 JSTypedArray *ta; 57029 JSObject *p; 57030 uint8_t *data; 57031 int64_t len; 57032 57033 len = abuf->byte_length; 57034 data = abuf->data; 57035 // update lengths of all typed arrays backed by this array buffer 57036 list_for_each(el, &abuf->array_list) { 57037 ta = list_entry(el, JSTypedArray, link); 57038 p = ta->obj; 57039 if (p->class_id == JS_CLASS_DATAVIEW) { 57040 if (ta->track_rab) { 57041 if (ta->offset < len) 57042 ta->length = len - ta->offset; 57043 else 57044 ta->length = 0; 57045 } 57046 } else { 57047 p->u.array.count = 0; 57048 p->u.array.u.ptr = NULL; 57049 size_log2 = typed_array_size_log2(p->class_id); 57050 size_elem = 1 << size_log2; 57051 if (ta->track_rab) { 57052 if (len >= (int64_t)ta->offset + size_elem) { 57053 p->u.array.count = (len - ta->offset) >> size_log2; 57054 p->u.array.u.ptr = &data[ta->offset]; 57055 } 57056 } else { 57057 if (len >= (int64_t)ta->offset + ta->length) { 57058 p->u.array.count = ta->length >> size_log2; 57059 p->u.array.u.ptr = &data[ta->offset]; 57060 } 57061 } 57062 } 57063 } 57064 57065 } 57066 57067 void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) 57068 { 57069 JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); 57070 57071 if (!abuf || abuf->detached) 57072 return; 57073 if (abuf->free_func) 57074 abuf->free_func(ctx->rt, abuf->opaque, abuf->data); 57075 abuf->data = NULL; 57076 abuf->byte_length = 0; 57077 abuf->detached = TRUE; 57078 js_array_buffer_update_typed_arrays(abuf); 57079 } 57080 57081 /* check if obj is ArrayBuffer or SharedArrayBuffer */ 57082 static BOOL js_is_array_buffer(JSContext *ctx, JSValueConst obj) 57083 { 57084 JSObject *p; 57085 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 57086 return FALSE; 57087 p = JS_VALUE_GET_OBJ(obj); 57088 if (p->class_id != JS_CLASS_ARRAY_BUFFER && 57089 p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { 57090 return FALSE; 57091 } 57092 return TRUE; 57093 } 57094 57095 /* get an ArrayBuffer or SharedArrayBuffer */ 57096 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj) 57097 { 57098 JSObject *p; 57099 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 57100 goto fail; 57101 p = JS_VALUE_GET_OBJ(obj); 57102 if (p->class_id != JS_CLASS_ARRAY_BUFFER && 57103 p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { 57104 fail: 57105 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); 57106 return NULL; 57107 } 57108 return p->u.array_buffer; 57109 } 57110 57111 static BOOL array_buffer_is_resizable(const JSArrayBuffer *abuf) 57112 { 57113 return abuf->max_byte_length >= 0; 57114 } 57115 57116 // ES #sec-arraybuffer.prototype.transfer 57117 static JSValue js_array_buffer_transfer(JSContext *ctx, 57118 JSValueConst this_val, 57119 int argc, JSValueConst *argv, 57120 int transfer_to_fixed_length) 57121 { 57122 JSArrayBuffer *abuf; 57123 uint64_t new_len, *pmax_len, max_len; 57124 JSValue res; 57125 57126 abuf = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_BUFFER); 57127 if (!abuf) 57128 return JS_EXCEPTION; 57129 if (abuf->shared) 57130 return JS_ThrowTypeError(ctx, "cannot transfer a SharedArrayBuffer"); 57131 if (argc < 1 || JS_IsUndefined(argv[0])) 57132 new_len = abuf->byte_length; 57133 else if (JS_ToIndex(ctx, &new_len, argv[0])) 57134 return JS_EXCEPTION; 57135 if (abuf->detached) 57136 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57137 pmax_len = NULL; 57138 if (!transfer_to_fixed_length) { 57139 if (array_buffer_is_resizable(abuf)) { // carry over maxByteLength 57140 max_len = abuf->max_byte_length; 57141 if (new_len > max_len) 57142 return JS_ThrowTypeError(ctx, "invalid array buffer length"); 57143 // TODO(bnoordhuis) support externally managed RABs 57144 if (abuf->free_func == js_array_buffer_free) 57145 pmax_len = &max_len; 57146 } 57147 } 57148 57149 /* create an empty AB */ 57150 if (new_len == 0) { 57151 res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, JS_CLASS_ARRAY_BUFFER); 57152 if (JS_IsException(res)) 57153 return res; 57154 JS_DetachArrayBuffer(ctx, this_val); 57155 } else { 57156 uint64_t old_len; 57157 57158 old_len = abuf->byte_length; 57159 57160 /* if length mismatch, realloc. Otherwise, use the same backing buffer. */ 57161 if (new_len != old_len) { 57162 /* XXX: we are currently limited to 2 GB */ 57163 if (new_len > INT32_MAX) 57164 return JS_ThrowRangeError(ctx, "invalid array buffer length"); 57165 57166 if (abuf->free_func != js_array_buffer_free) { 57167 JSArrayBuffer *new_abuf; 57168 /* cannot use js_realloc() because the buffer was 57169 allocated with a custom allocator */ 57170 res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, pmax_len, JS_CLASS_ARRAY_BUFFER); 57171 if (JS_IsException(res)) 57172 return res; 57173 new_abuf = JS_GetOpaque2(ctx, res, JS_CLASS_ARRAY_BUFFER); 57174 memcpy(new_abuf->data, abuf->data, min_int(old_len, new_len)); 57175 abuf->free_func(ctx->rt, abuf->opaque, abuf->data); 57176 } else { 57177 JSArrayBuffer *new_abuf; 57178 uint8_t *new_bs; 57179 /* reallocate the buffer after the new array buffer is 57180 created in case the new array buffer creation 57181 fails. */ 57182 res = js_array_buffer_constructor2(ctx, JS_UNDEFINED, 0, pmax_len, JS_CLASS_ARRAY_BUFFER); 57183 if (JS_IsException(res)) 57184 return res; 57185 new_bs = js_realloc(ctx, abuf->data, new_len); 57186 if (!new_bs) { 57187 JS_FreeValue(ctx, res); 57188 return JS_EXCEPTION; 57189 } 57190 if (new_len > old_len) 57191 memset(new_bs + old_len, 0, new_len - old_len); 57192 new_abuf = JS_GetOpaque2(ctx, res, JS_CLASS_ARRAY_BUFFER); 57193 js_free(ctx, new_abuf->data); 57194 new_abuf->data = new_bs; 57195 new_abuf->byte_length = new_len; 57196 } 57197 } else { 57198 /* can keep the custom free function */ 57199 res = js_array_buffer_constructor3(ctx, JS_UNDEFINED, new_len, pmax_len, 57200 JS_CLASS_ARRAY_BUFFER, 57201 abuf->data, abuf->free_func, 57202 abuf->opaque, FALSE); 57203 if (JS_IsException(res)) 57204 return res; 57205 } 57206 /* neuter the backing buffer */ 57207 abuf->data = NULL; 57208 abuf->byte_length = 0; 57209 abuf->detached = TRUE; 57210 js_array_buffer_update_typed_arrays(abuf); 57211 } 57212 return res; 57213 } 57214 57215 static JSValue js_array_buffer_resize(JSContext *ctx, JSValueConst this_val, 57216 int argc, JSValueConst *argv, int class_id) 57217 { 57218 JSArrayBuffer *abuf; 57219 uint8_t *data; 57220 int64_t len; 57221 57222 abuf = JS_GetOpaque2(ctx, this_val, class_id); 57223 if (!abuf) 57224 return JS_EXCEPTION; 57225 if (JS_ToInt64(ctx, &len, argv[0])) 57226 return JS_EXCEPTION; 57227 if (abuf->detached) 57228 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57229 if (!array_buffer_is_resizable(abuf)) 57230 return JS_ThrowTypeError(ctx, "array buffer is not resizable"); 57231 // TODO(bnoordhuis) support externally managed RABs 57232 if (abuf->free_func != js_array_buffer_free) 57233 return JS_ThrowTypeError(ctx, "external array buffer is not resizable"); 57234 if (len < 0 || len > abuf->max_byte_length) { 57235 bad_length: 57236 return JS_ThrowRangeError(ctx, "invalid array buffer length"); 57237 } 57238 // SABs can only grow and we don't need to realloc because 57239 // js_array_buffer_constructor3 commits all memory upfront; 57240 // regular RABs are resizable both ways and realloc 57241 if (abuf->shared) { 57242 if (len < abuf->byte_length) 57243 goto bad_length; 57244 // Note this is off-spec; there's supposed to be a single atomic 57245 // |byteLength| property that's shared across SABs but we store 57246 // it per SAB instead. That means when thread A calls sab.grow(2) 57247 // at time t0, and thread B calls sab.grow(1) at time t1, we don't 57248 // throw a TypeError in thread B as the spec says we should, 57249 // instead both threads get their own view of the backing memory, 57250 // 2 bytes big in A, and 1 byte big in B 57251 abuf->byte_length = len; 57252 } else { 57253 data = js_realloc(ctx, abuf->data, max_int(len, 1)); 57254 if (!data) 57255 return JS_EXCEPTION; 57256 if (len > abuf->byte_length) 57257 memset(&data[abuf->byte_length], 0, len - abuf->byte_length); 57258 abuf->byte_length = len; 57259 abuf->data = data; 57260 } 57261 js_array_buffer_update_typed_arrays(abuf); 57262 return JS_UNDEFINED; 57263 } 57264 57265 static JSValue js_array_buffer_slice(JSContext *ctx, 57266 JSValueConst this_val, 57267 int argc, JSValueConst *argv, int class_id) 57268 { 57269 JSArrayBuffer *abuf, *new_abuf; 57270 int64_t len, start, end, new_len; 57271 JSValue ctor, new_obj; 57272 57273 abuf = JS_GetOpaque2(ctx, this_val, class_id); 57274 if (!abuf) 57275 return JS_EXCEPTION; 57276 if (abuf->detached) 57277 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57278 len = abuf->byte_length; 57279 57280 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 57281 return JS_EXCEPTION; 57282 57283 end = len; 57284 if (!JS_IsUndefined(argv[1])) { 57285 if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) 57286 return JS_EXCEPTION; 57287 } 57288 new_len = max_int64(end - start, 0); 57289 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 57290 if (JS_IsException(ctor)) 57291 return ctor; 57292 if (JS_IsUndefined(ctor)) { 57293 new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, 57294 NULL, class_id); 57295 } else { 57296 JSValue args[1]; 57297 args[0] = JS_NewInt64(ctx, new_len); 57298 new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args); 57299 JS_FreeValue(ctx, ctor); 57300 JS_FreeValue(ctx, args[0]); 57301 } 57302 if (JS_IsException(new_obj)) 57303 return new_obj; 57304 new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); 57305 if (!new_abuf) 57306 goto fail; 57307 if (js_same_value(ctx, new_obj, this_val)) { 57308 JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); 57309 goto fail; 57310 } 57311 if (new_abuf->detached) { 57312 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57313 goto fail; 57314 } 57315 if (new_abuf->byte_length < new_len) { 57316 JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); 57317 goto fail; 57318 } 57319 /* must test again because of side effects */ 57320 if (abuf->detached || abuf->byte_length < start + new_len) { 57321 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57322 goto fail; 57323 } 57324 memcpy(new_abuf->data, abuf->data + start, new_len); 57325 return new_obj; 57326 fail: 57327 JS_FreeValue(ctx, new_obj); 57328 return JS_EXCEPTION; 57329 } 57330 57331 static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { 57332 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), 57333 JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_ARRAY_BUFFER ), 57334 JS_CGETSET_MAGIC_DEF("resizable", js_array_buffer_get_resizable, NULL, JS_CLASS_ARRAY_BUFFER ), 57335 JS_CGETSET_DEF("detached", js_array_buffer_get_detached, NULL ), 57336 JS_CFUNC_MAGIC_DEF("resize", 1, js_array_buffer_resize, JS_CLASS_ARRAY_BUFFER ), 57337 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), 57338 JS_CFUNC_MAGIC_DEF("transfer", 0, js_array_buffer_transfer, 0 ), 57339 JS_CFUNC_MAGIC_DEF("transferToFixedLength", 0, js_array_buffer_transfer, 1 ), 57340 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), 57341 }; 57342 57343 /* SharedArrayBuffer */ 57344 57345 static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { 57346 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 57347 }; 57348 57349 static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { 57350 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), 57351 JS_CGETSET_MAGIC_DEF("maxByteLength", js_array_buffer_get_maxByteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), 57352 JS_CGETSET_MAGIC_DEF("growable", js_array_buffer_get_resizable, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), 57353 JS_CFUNC_MAGIC_DEF("grow", 1, js_array_buffer_resize, JS_CLASS_SHARED_ARRAY_BUFFER ), 57354 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), 57355 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), 57356 }; 57357 57358 static JSObject *get_typed_array(JSContext *ctx, JSValueConst this_val) 57359 { 57360 JSObject *p; 57361 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 57362 goto fail; 57363 p = JS_VALUE_GET_OBJ(this_val); 57364 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && 57365 p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { 57366 fail: 57367 JS_ThrowTypeError(ctx, "not a TypedArray"); 57368 return NULL; 57369 } 57370 return p; 57371 } 57372 57373 // is the typed array detached or out of bounds relative to its RAB? 57374 // |p| must be a typed array, *not* a DataView 57375 static BOOL typed_array_is_oob(JSObject *p) 57376 { 57377 JSArrayBuffer *abuf; 57378 JSTypedArray *ta; 57379 int len, size_elem; 57380 int64_t end; 57381 57382 assert(p->class_id >= JS_CLASS_UINT8C_ARRAY); 57383 assert(p->class_id <= JS_CLASS_FLOAT64_ARRAY); 57384 57385 ta = p->u.typed_array; 57386 abuf = ta->buffer->u.array_buffer; 57387 if (abuf->detached) 57388 return TRUE; 57389 len = abuf->byte_length; 57390 if (ta->offset > len) 57391 return TRUE; 57392 if (ta->track_rab) 57393 return FALSE; 57394 if (len < (int64_t)ta->offset + ta->length) 57395 return TRUE; 57396 size_elem = 1 << typed_array_size_log2(p->class_id); 57397 end = (int64_t)ta->offset + (int64_t)p->u.array.count * size_elem; 57398 return end > len; 57399 } 57400 57401 // Be *very* careful if you touch the typed array's memory directly: 57402 // the length is only valid until the next call into JS land because 57403 // JS code can detach or resize the backing array buffer. Functions 57404 // like JS_GetProperty and JS_ToIndex call JS code. 57405 // 57406 // Exclusively reading or writing elements with JS_GetProperty, 57407 // JS_GetPropertyInt64, JS_SetProperty, etc. is safe because they 57408 // perform bounds checks, as does js_get_fast_array_element. 57409 static int js_typed_array_get_length_unsafe(JSContext *ctx, JSValueConst obj) 57410 { 57411 JSObject *p; 57412 p = get_typed_array(ctx, obj); 57413 if (!p) 57414 return -1; 57415 if (typed_array_is_oob(p)) { 57416 JS_ThrowTypeErrorArrayBufferOOB(ctx); 57417 return -1; 57418 } 57419 return p->u.array.count; 57420 } 57421 57422 static int validate_typed_array(JSContext *ctx, JSValueConst this_val) 57423 { 57424 JSObject *p; 57425 p = get_typed_array(ctx, this_val); 57426 if (!p) 57427 return -1; 57428 if (typed_array_is_oob(p)) { 57429 JS_ThrowTypeErrorArrayBufferOOB(ctx); 57430 return -1; 57431 } 57432 return 0; 57433 } 57434 57435 static JSValue js_typed_array_get_length(JSContext *ctx, 57436 JSValueConst this_val) 57437 { 57438 JSObject *p; 57439 p = get_typed_array(ctx, this_val); 57440 if (!p) 57441 return JS_EXCEPTION; 57442 return JS_NewInt32(ctx, p->u.array.count); 57443 } 57444 57445 static JSValue js_typed_array_get_buffer(JSContext *ctx, 57446 JSValueConst this_val) 57447 { 57448 JSObject *p; 57449 JSTypedArray *ta; 57450 p = get_typed_array(ctx, this_val); 57451 if (!p) 57452 return JS_EXCEPTION; 57453 ta = p->u.typed_array; 57454 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 57455 } 57456 57457 static JSValue js_typed_array_get_byteLength(JSContext *ctx, 57458 JSValueConst this_val) 57459 { 57460 JSObject *p; 57461 JSTypedArray *ta; 57462 int size_log2; 57463 57464 p = get_typed_array(ctx, this_val); 57465 if (!p) 57466 return JS_EXCEPTION; 57467 if (typed_array_is_oob(p)) 57468 return JS_NewInt32(ctx, 0); 57469 ta = p->u.typed_array; 57470 if (!ta->track_rab) 57471 return JS_NewUint32(ctx, ta->length); 57472 size_log2 = typed_array_size_log2(p->class_id); 57473 return JS_NewInt64(ctx, (int64_t)p->u.array.count << size_log2); 57474 } 57475 57476 static JSValue js_typed_array_get_byteOffset(JSContext *ctx, 57477 JSValueConst this_val) 57478 { 57479 JSObject *p; 57480 JSTypedArray *ta; 57481 p = get_typed_array(ctx, this_val); 57482 if (!p) 57483 return JS_EXCEPTION; 57484 if (typed_array_is_oob(p)) 57485 return JS_NewInt32(ctx, 0); 57486 ta = p->u.typed_array; 57487 return JS_NewUint32(ctx, ta->offset); 57488 } 57489 57490 JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, 57491 JSTypedArrayEnum type) 57492 { 57493 if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64) 57494 return JS_ThrowRangeError(ctx, "invalid typed array type"); 57495 57496 return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv, 57497 JS_CLASS_UINT8C_ARRAY + type); 57498 } 57499 57500 /* Return the buffer associated to the typed array or an exception if 57501 it is not a typed array or if the buffer is detached. pbyte_offset, 57502 pbyte_length or pbytes_per_element can be NULL. */ 57503 JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, 57504 size_t *pbyte_offset, 57505 size_t *pbyte_length, 57506 size_t *pbytes_per_element) 57507 { 57508 JSObject *p; 57509 JSTypedArray *ta; 57510 p = get_typed_array(ctx, obj); 57511 if (!p) 57512 return JS_EXCEPTION; 57513 if (typed_array_is_oob(p)) 57514 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 57515 ta = p->u.typed_array; 57516 if (pbyte_offset) 57517 *pbyte_offset = ta->offset; 57518 if (pbyte_length) 57519 *pbyte_length = ta->length; 57520 if (pbytes_per_element) { 57521 *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); 57522 } 57523 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 57524 } 57525 57526 BOOL JS_IsArrayBuffer(JSValueConst obj) 57527 { 57528 JSObject *p; 57529 57530 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 57531 return FALSE; 57532 p = JS_VALUE_GET_OBJ(obj); 57533 if (p->class_id == JS_CLASS_ARRAY_BUFFER || 57534 p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { 57535 return TRUE; 57536 } 57537 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 57538 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 57539 return TRUE; 57540 } 57541 return FALSE; 57542 } 57543 57544 /* return NULL if exception. WARNING: any JS call can detach the 57545 buffer and render the returned pointer invalid */ 57546 uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj) 57547 { 57548 if (js_is_array_buffer(ctx, obj)) { 57549 JSArrayBuffer* abuf = js_get_array_buffer(ctx, obj); 57550 if (!abuf) 57551 goto fail; 57552 if (abuf->detached) { 57553 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57554 goto fail; 57555 } 57556 *psize = abuf->byte_length; 57557 return abuf->data; 57558 } else { 57559 JSObject* p; 57560 JSTypedArray* ta; 57561 JSArrayBuffer* abuf; 57562 p = get_typed_array(ctx, obj); 57563 if (!p) { 57564 goto fail; 57565 } 57566 if (typed_array_is_oob(p)) { 57567 JS_ThrowTypeErrorArrayBufferOOB(ctx); 57568 goto fail; 57569 } 57570 ta = p->u.typed_array; 57571 abuf = ta->buffer->u.array_buffer; 57572 if (!abuf) 57573 goto fail; 57574 if (abuf->detached) { 57575 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57576 goto fail; 57577 } 57578 *psize = ta->length; 57579 return abuf->data + ta->offset; 57580 } 57581 JS_ThrowTypeError(ctx, "expected ArrayBuffer or ArrayBufferView"); 57582 fail: 57583 *psize = 0; 57584 return NULL; 57585 } 57586 57587 57588 static JSValue js_typed_array_get_toStringTag(JSContext *ctx, 57589 JSValueConst this_val) 57590 { 57591 JSObject *p; 57592 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 57593 return JS_UNDEFINED; 57594 p = JS_VALUE_GET_OBJ(this_val); 57595 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && 57596 p->class_id <= JS_CLASS_FLOAT64_ARRAY)) 57597 return JS_UNDEFINED; 57598 return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); 57599 } 57600 57601 static JSValue js_typed_array_set_internal(JSContext *ctx, 57602 JSValueConst dst, 57603 JSValueConst src, 57604 JSValueConst off) 57605 { 57606 JSObject *p; 57607 JSObject *src_p; 57608 uint32_t i; 57609 int64_t dst_len, src_len, offset; 57610 JSValue val, src_obj = JS_UNDEFINED; 57611 57612 p = get_typed_array(ctx, dst); 57613 if (!p) 57614 goto fail; 57615 if (JS_ToInt64Sat(ctx, &offset, off)) 57616 goto fail; 57617 if (offset < 0) 57618 goto range_error; 57619 if (typed_array_is_oob(p)) { 57620 detached: 57621 JS_ThrowTypeErrorArrayBufferOOB(ctx); 57622 goto fail; 57623 } 57624 dst_len = p->u.array.count; 57625 src_obj = JS_ToObject(ctx, src); 57626 if (JS_IsException(src_obj)) 57627 goto fail; 57628 src_p = JS_VALUE_GET_OBJ(src_obj); 57629 if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && 57630 src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 57631 JSTypedArray *dest_ta = p->u.typed_array; 57632 JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer; 57633 JSTypedArray *src_ta = src_p->u.typed_array; 57634 JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; 57635 int shift = typed_array_size_log2(p->class_id); 57636 57637 if (typed_array_is_oob(src_p)) 57638 goto detached; 57639 57640 src_len = src_p->u.array.count; 57641 if (offset > dst_len - src_len) 57642 goto range_error; 57643 57644 /* copying between typed objects */ 57645 if (src_p->class_id == p->class_id) { 57646 /* same type, use memmove */ 57647 memmove(dest_abuf->data + dest_ta->offset + (offset << shift), 57648 src_abuf->data + src_ta->offset, src_len << shift); 57649 goto done; 57650 } 57651 if (dest_abuf->data == src_abuf->data) { 57652 /* copying between the same buffer using different types of mappings 57653 would require a temporary buffer */ 57654 } 57655 /* otherwise, default behavior is slow but correct */ 57656 } else { 57657 // can change |dst| as a side effect; per spec, 57658 // perform the range check against its old length 57659 if (js_get_length64(ctx, &src_len, src_obj)) 57660 goto fail; 57661 if (offset > dst_len - src_len) { 57662 range_error: 57663 JS_ThrowRangeError(ctx, "invalid array length"); 57664 goto fail; 57665 } 57666 } 57667 for(i = 0; i < src_len; i++) { 57668 val = JS_GetPropertyUint32(ctx, src_obj, i); 57669 if (JS_IsException(val)) 57670 goto fail; 57671 if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) 57672 goto fail; 57673 } 57674 done: 57675 JS_FreeValue(ctx, src_obj); 57676 return JS_UNDEFINED; 57677 fail: 57678 JS_FreeValue(ctx, src_obj); 57679 return JS_EXCEPTION; 57680 } 57681 57682 static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, 57683 int argc, JSValueConst *argv) 57684 { 57685 JSObject *p; 57686 int64_t idx, len; 57687 57688 p = get_typed_array(ctx, this_val); 57689 if (!p) 57690 return JS_EXCEPTION; 57691 57692 if (typed_array_is_oob(p)) 57693 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57694 len = p->u.array.count; 57695 57696 // note: can change p->u.array.count 57697 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 57698 return JS_EXCEPTION; 57699 57700 if (idx < 0) 57701 idx = len + idx; 57702 57703 len = p->u.array.count; 57704 if (idx < 0 || idx >= len) 57705 return JS_UNDEFINED; 57706 return JS_GetPropertyInt64(ctx, this_val, idx); 57707 } 57708 57709 static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val, 57710 int argc, JSValueConst *argv) 57711 { 57712 JSValue arr, val; 57713 JSObject *p; 57714 int64_t idx, len; 57715 57716 p = get_typed_array(ctx, this_val); 57717 if (!p) 57718 return JS_EXCEPTION; 57719 if (typed_array_is_oob(p)) 57720 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 57721 57722 len = p->u.array.count; 57723 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 57724 return JS_EXCEPTION; 57725 57726 if (idx < 0) 57727 idx = len + idx; 57728 57729 val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); 57730 if (JS_IsException(val)) 57731 return JS_EXCEPTION; 57732 57733 if (typed_array_is_oob(p) || idx < 0 || idx >= p->u.array.count) 57734 return JS_ThrowRangeError(ctx, "invalid array index"); 57735 57736 /* warning: 'this_val' may have been resized, so 'len' may be 57737 larger than its length */ 57738 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 57739 p->class_id, len); 57740 if (JS_IsException(arr)) { 57741 JS_FreeValue(ctx, val); 57742 return JS_EXCEPTION; 57743 } 57744 if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { 57745 JS_FreeValue(ctx, arr); 57746 return JS_EXCEPTION; 57747 } 57748 return arr; 57749 } 57750 57751 static JSValue js_typed_array_set(JSContext *ctx, 57752 JSValueConst this_val, 57753 int argc, JSValueConst *argv) 57754 { 57755 JSValueConst offset = JS_UNDEFINED; 57756 if (argc > 1) { 57757 offset = argv[1]; 57758 } 57759 return js_typed_array_set_internal(ctx, this_val, argv[0], offset); 57760 } 57761 57762 static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val, 57763 int argc, JSValueConst *argv, int magic) 57764 { 57765 if (validate_typed_array(ctx, this_val)) 57766 return JS_EXCEPTION; 57767 return js_create_array_iterator(ctx, this_val, argc, argv, magic); 57768 } 57769 57770 static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor, 57771 int argc, JSValueConst *argv) 57772 { 57773 JSValue ret; 57774 int new_len; 57775 int64_t len; 57776 57777 ret = JS_CallConstructor(ctx, ctor, argc, argv); 57778 if (JS_IsException(ret)) 57779 return ret; 57780 /* validate the typed array */ 57781 new_len = js_typed_array_get_length_unsafe(ctx, ret); 57782 if (new_len < 0) 57783 goto fail; 57784 if (argc == 1) { 57785 /* ensure that it is large enough */ 57786 if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) 57787 goto fail; 57788 if (new_len < len) { 57789 JS_ThrowTypeError(ctx, "TypedArray length is too small"); 57790 fail: 57791 JS_FreeValue(ctx, ret); 57792 return JS_EXCEPTION; 57793 } 57794 } 57795 return ret; 57796 } 57797 57798 #if 0 57799 static JSValue js_typed_array___create(JSContext *ctx, 57800 JSValueConst this_val, 57801 int argc, JSValueConst *argv) 57802 { 57803 return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1); 57804 } 57805 #endif 57806 57807 static JSValue js_typed_array___speciesCreate(JSContext *ctx, 57808 JSValueConst this_val, 57809 int argc, JSValueConst *argv) 57810 { 57811 JSValueConst obj; 57812 JSObject *p; 57813 JSValue ctor, ret; 57814 int argc1; 57815 57816 obj = argv[0]; 57817 p = get_typed_array(ctx, obj); 57818 if (!p) 57819 return JS_EXCEPTION; 57820 ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); 57821 if (JS_IsException(ctor)) 57822 return ctor; 57823 argc1 = max_int(argc - 1, 0); 57824 if (JS_IsUndefined(ctor)) { 57825 ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, 57826 p->class_id); 57827 } else { 57828 ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); 57829 JS_FreeValue(ctx, ctor); 57830 } 57831 return ret; 57832 } 57833 57834 static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, 57835 int argc, JSValueConst *argv) 57836 { 57837 // from(items, mapfn = void 0, this_arg = void 0) 57838 JSValueConst items = argv[0], mapfn, this_arg; 57839 JSValueConst args[2]; 57840 JSValue iter, arr, r, v, v2; 57841 int64_t k, len; 57842 int mapping; 57843 57844 mapping = FALSE; 57845 mapfn = JS_UNDEFINED; 57846 this_arg = JS_UNDEFINED; 57847 r = JS_UNDEFINED; 57848 arr = JS_UNDEFINED; 57849 iter = JS_UNDEFINED; 57850 57851 if (argc > 1) { 57852 mapfn = argv[1]; 57853 if (!JS_IsUndefined(mapfn)) { 57854 if (check_function(ctx, mapfn)) 57855 goto exception; 57856 mapping = 1; 57857 if (argc > 2) 57858 this_arg = argv[2]; 57859 } 57860 } 57861 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); 57862 if (JS_IsException(iter)) 57863 goto exception; 57864 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { 57865 uint32_t len1; 57866 if (!JS_IsFunction(ctx, iter)) { 57867 JS_ThrowTypeError(ctx, "value is not iterable"); 57868 goto exception; 57869 } 57870 arr = js_array_from_iterator(ctx, &len1, items, iter); 57871 if (JS_IsException(arr)) 57872 goto exception; 57873 len = len1; 57874 } else { 57875 arr = JS_ToObject(ctx, items); 57876 if (JS_IsException(arr)) 57877 goto exception; 57878 if (js_get_length64(ctx, &len, arr) < 0) 57879 goto exception; 57880 } 57881 v = JS_NewInt64(ctx, len); 57882 args[0] = v; 57883 r = js_typed_array_create(ctx, this_val, 1, args); 57884 JS_FreeValue(ctx, v); 57885 if (JS_IsException(r)) 57886 goto exception; 57887 for(k = 0; k < len; k++) { 57888 v = JS_GetPropertyInt64(ctx, arr, k); 57889 if (JS_IsException(v)) 57890 goto exception; 57891 if (mapping) { 57892 args[0] = v; 57893 args[1] = JS_NewInt32(ctx, k); 57894 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 57895 JS_FreeValue(ctx, v); 57896 v = v2; 57897 if (JS_IsException(v)) 57898 goto exception; 57899 } 57900 if (JS_SetPropertyInt64(ctx, r, k, v) < 0) 57901 goto exception; 57902 } 57903 goto done; 57904 exception: 57905 JS_FreeValue(ctx, r); 57906 r = JS_EXCEPTION; 57907 done: 57908 JS_FreeValue(ctx, arr); 57909 JS_FreeValue(ctx, iter); 57910 return r; 57911 } 57912 57913 static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val, 57914 int argc, JSValueConst *argv) 57915 { 57916 JSValue obj; 57917 JSValueConst args[1]; 57918 int i; 57919 57920 args[0] = JS_NewInt32(ctx, argc); 57921 obj = js_typed_array_create(ctx, this_val, 1, args); 57922 if (JS_IsException(obj)) 57923 return obj; 57924 57925 for(i = 0; i < argc; i++) { 57926 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) { 57927 JS_FreeValue(ctx, obj); 57928 return JS_EXCEPTION; 57929 } 57930 } 57931 return obj; 57932 } 57933 57934 static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, 57935 int argc, JSValueConst *argv) 57936 { 57937 JSObject *p; 57938 int len, to, from, final, count, shift, space; 57939 57940 p = get_typed_array(ctx, this_val); 57941 if (!p) 57942 return JS_EXCEPTION; 57943 if (typed_array_is_oob(p)) 57944 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 57945 len = p->u.array.count; 57946 57947 if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) 57948 return JS_EXCEPTION; 57949 57950 if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) 57951 return JS_EXCEPTION; 57952 57953 final = len; 57954 if (argc > 2 && !JS_IsUndefined(argv[2])) { 57955 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) 57956 return JS_EXCEPTION; 57957 } 57958 57959 if (typed_array_is_oob(p)) 57960 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 57961 57962 // RAB may have been resized by evil .valueOf method 57963 space = p->u.array.count - max_int(to, from); 57964 count = min_int(final - from, len - to); 57965 count = min_int(count, space); 57966 if (count > 0) { 57967 shift = typed_array_size_log2(p->class_id); 57968 memmove(p->u.array.u.uint8_ptr + (to << shift), 57969 p->u.array.u.uint8_ptr + (from << shift), 57970 count << shift); 57971 } 57972 return JS_DupValue(ctx, this_val); 57973 } 57974 57975 static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, 57976 int argc, JSValueConst *argv) 57977 { 57978 JSObject *p; 57979 int len, k, final, shift; 57980 uint64_t v64; 57981 57982 p = get_typed_array(ctx, this_val); 57983 if (!p) 57984 return JS_EXCEPTION; 57985 if (typed_array_is_oob(p)) 57986 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 57987 len = p->u.array.count; 57988 57989 if (p->class_id == JS_CLASS_UINT8C_ARRAY) { 57990 int32_t v; 57991 if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) 57992 return JS_EXCEPTION; 57993 v64 = v; 57994 } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { 57995 uint32_t v; 57996 if (JS_ToUint32(ctx, &v, argv[0])) 57997 return JS_EXCEPTION; 57998 v64 = v; 57999 } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { 58000 if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) 58001 return JS_EXCEPTION; 58002 } else { 58003 double d; 58004 if (JS_ToFloat64(ctx, &d, argv[0])) 58005 return JS_EXCEPTION; 58006 if (p->class_id == JS_CLASS_FLOAT16_ARRAY) { 58007 v64 = tofp16(d); 58008 } else if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { 58009 union { 58010 float f; 58011 uint32_t u32; 58012 } u; 58013 u.f = d; 58014 v64 = u.u32; 58015 } else { 58016 JSFloat64Union u; 58017 u.d = d; 58018 v64 = u.u64; 58019 } 58020 } 58021 58022 k = 0; 58023 if (argc > 1) { 58024 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) 58025 return JS_EXCEPTION; 58026 } 58027 58028 final = len; 58029 if (argc > 2 && !JS_IsUndefined(argv[2])) { 58030 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) 58031 return JS_EXCEPTION; 58032 } 58033 58034 if (typed_array_is_oob(p)) 58035 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 58036 58037 // RAB may have been resized by evil .valueOf method 58038 final = min_int(final, p->u.array.count); 58039 shift = typed_array_size_log2(p->class_id); 58040 switch(shift) { 58041 case 0: 58042 if (k < final) { 58043 memset(p->u.array.u.uint8_ptr + k, v64, final - k); 58044 } 58045 break; 58046 case 1: 58047 for(; k < final; k++) { 58048 p->u.array.u.uint16_ptr[k] = v64; 58049 } 58050 break; 58051 case 2: 58052 for(; k < final; k++) { 58053 p->u.array.u.uint32_ptr[k] = v64; 58054 } 58055 break; 58056 case 3: 58057 for(; k < final; k++) { 58058 p->u.array.u.uint64_ptr[k] = v64; 58059 } 58060 break; 58061 default: 58062 abort(); 58063 } 58064 return JS_DupValue(ctx, this_val); 58065 } 58066 58067 static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, 58068 int argc, JSValueConst *argv, int mode) 58069 { 58070 JSValueConst func, this_arg; 58071 JSValueConst args[3]; 58072 JSValue val, index_val, res; 58073 int len, k, end; 58074 int dir; 58075 58076 val = JS_UNDEFINED; 58077 len = js_typed_array_get_length_unsafe(ctx, this_val); 58078 if (len < 0) 58079 goto exception; 58080 58081 func = argv[0]; 58082 if (check_function(ctx, func)) 58083 goto exception; 58084 58085 this_arg = JS_UNDEFINED; 58086 if (argc > 1) 58087 this_arg = argv[1]; 58088 58089 k = 0; 58090 dir = 1; 58091 end = len; 58092 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { 58093 k = len - 1; 58094 dir = -1; 58095 end = -1; 58096 } 58097 58098 for(; k != end; k += dir) { 58099 index_val = JS_NewInt32(ctx, k); 58100 val = JS_GetPropertyValue(ctx, this_val, index_val); 58101 if (JS_IsException(val)) 58102 goto exception; 58103 args[0] = val; 58104 args[1] = index_val; 58105 args[2] = this_val; 58106 res = JS_Call(ctx, func, this_arg, 3, args); 58107 if (JS_IsException(res)) 58108 goto exception; 58109 if (JS_ToBoolFree(ctx, res)) { 58110 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { 58111 JS_FreeValue(ctx, val); 58112 return index_val; 58113 } else { 58114 return val; 58115 } 58116 } 58117 JS_FreeValue(ctx, val); 58118 } 58119 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) 58120 return JS_NewInt32(ctx, -1); 58121 else 58122 return JS_UNDEFINED; 58123 58124 exception: 58125 JS_FreeValue(ctx, val); 58126 return JS_EXCEPTION; 58127 } 58128 58129 #define special_indexOf 0 58130 #define special_lastIndexOf 1 58131 #define special_includes -1 58132 58133 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, 58134 int argc, JSValueConst *argv, int special) 58135 { 58136 JSObject *p; 58137 int len, tag, is_int, is_bigint, k, stop, inc, res = -1; 58138 int64_t v64; 58139 double d; 58140 float f; 58141 uint16_t hf; 58142 58143 p = get_typed_array(ctx, this_val); 58144 if (!p) 58145 return JS_EXCEPTION; 58146 if (typed_array_is_oob(p)) 58147 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 58148 len = p->u.array.count; 58149 58150 if (len == 0) 58151 goto done; 58152 58153 if (special == special_lastIndexOf) { 58154 k = len - 1; 58155 if (argc > 1) { 58156 int64_t k1; 58157 if (JS_ToInt64Clamp(ctx, &k1, argv[1], -1, len - 1, len)) 58158 goto exception; 58159 k = k1; 58160 if (k < 0) 58161 goto done; 58162 } 58163 stop = -1; 58164 inc = -1; 58165 } else { 58166 k = 0; 58167 if (argc > 1) { 58168 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) 58169 goto exception; 58170 } 58171 stop = len; 58172 inc = 1; 58173 } 58174 58175 /* includes function: 'undefined' can be found if searching out of bounds */ 58176 if (len > p->u.array.count && special == special_includes && 58177 JS_IsUndefined(argv[0]) && k < len) { 58178 res = 0; 58179 goto done; 58180 } 58181 58182 // RAB may have been resized by evil .valueOf method 58183 len = min_int(len, p->u.array.count); 58184 if (len == 0) 58185 goto done; 58186 if (special == special_lastIndexOf) 58187 k = min_int(k, len - 1); 58188 else 58189 k = min_int(k, len); 58190 stop = min_int(stop, len); 58191 58192 is_bigint = 0; 58193 is_int = 0; /* avoid warning */ 58194 v64 = 0; /* avoid warning */ 58195 tag = JS_VALUE_GET_NORM_TAG(argv[0]); 58196 if (tag == JS_TAG_INT) { 58197 is_int = 1; 58198 v64 = JS_VALUE_GET_INT(argv[0]); 58199 d = v64; 58200 } else 58201 if (tag == JS_TAG_FLOAT64) { 58202 d = JS_VALUE_GET_FLOAT64(argv[0]); 58203 if (d >= INT64_MIN && d < 0x1p63) { 58204 v64 = d; 58205 is_int = (v64 == d); 58206 } 58207 } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) { 58208 JSBigIntBuf buf1; 58209 JSBigInt *p1; 58210 int sz = (64 / JS_LIMB_BITS); 58211 if (tag == JS_TAG_SHORT_BIG_INT) 58212 p1 = js_bigint_set_short(&buf1, argv[0]); 58213 else 58214 p1 = JS_VALUE_GET_PTR(argv[0]); 58215 58216 if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { 58217 if (p1->len > sz) 58218 goto done; /* does not fit an int64 : cannot be found */ 58219 } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { 58220 if (js_bigint_sign(p1)) 58221 goto done; /* v < 0 */ 58222 if (p1->len <= sz) { 58223 /* OK */ 58224 } else if (p1->len == sz + 1 && p1->tab[sz] == 0) { 58225 /* 2^63 <= v <= 2^64-1 */ 58226 } else { 58227 goto done; 58228 } 58229 } else { 58230 goto done; 58231 } 58232 if (JS_ToBigInt64(ctx, &v64, argv[0])) 58233 goto exception; 58234 d = 0; 58235 is_bigint = 1; 58236 } else { 58237 goto done; 58238 } 58239 58240 switch (p->class_id) { 58241 case JS_CLASS_INT8_ARRAY: 58242 if (is_int && (int8_t)v64 == v64) 58243 goto scan8; 58244 break; 58245 case JS_CLASS_UINT8C_ARRAY: 58246 case JS_CLASS_UINT8_ARRAY: 58247 if (is_int && (uint8_t)v64 == v64) { 58248 const uint8_t *pv, *pp; 58249 uint16_t v; 58250 scan8: 58251 pv = p->u.array.u.uint8_ptr; 58252 v = v64; 58253 if (inc > 0) { 58254 pp = NULL; 58255 if (pv) 58256 pp = memchr(pv + k, v, len - k); 58257 if (pp) 58258 res = pp - pv; 58259 } else { 58260 for (; k != stop; k += inc) { 58261 if (pv[k] == v) { 58262 res = k; 58263 break; 58264 } 58265 } 58266 } 58267 } 58268 break; 58269 case JS_CLASS_INT16_ARRAY: 58270 if (is_int && (int16_t)v64 == v64) 58271 goto scan16; 58272 break; 58273 case JS_CLASS_UINT16_ARRAY: 58274 if (is_int && (uint16_t)v64 == v64) { 58275 const uint16_t *pv; 58276 uint16_t v; 58277 scan16: 58278 pv = p->u.array.u.uint16_ptr; 58279 v = v64; 58280 for (; k != stop; k += inc) { 58281 if (pv[k] == v) { 58282 res = k; 58283 break; 58284 } 58285 } 58286 } 58287 break; 58288 case JS_CLASS_INT32_ARRAY: 58289 if (is_int && (int32_t)v64 == v64) 58290 goto scan32; 58291 break; 58292 case JS_CLASS_UINT32_ARRAY: 58293 if (is_int && (uint32_t)v64 == v64) { 58294 const uint32_t *pv; 58295 uint32_t v; 58296 scan32: 58297 pv = p->u.array.u.uint32_ptr; 58298 v = v64; 58299 for (; k != stop; k += inc) { 58300 if (pv[k] == v) { 58301 res = k; 58302 break; 58303 } 58304 } 58305 } 58306 break; 58307 case JS_CLASS_FLOAT16_ARRAY: 58308 if (is_bigint) 58309 break; 58310 if (isnan(d)) { 58311 const uint16_t *pv = p->u.array.u.fp16_ptr; 58312 /* special case: indexOf returns -1, includes finds NaN */ 58313 if (special != special_includes) 58314 goto done; 58315 for (; k != stop; k += inc) { 58316 if (isfp16nan(pv[k])) { 58317 res = k; 58318 break; 58319 } 58320 } 58321 } else if (d == 0) { 58322 // special case: includes also finds negative zero 58323 const uint16_t *pv = p->u.array.u.fp16_ptr; 58324 for (; k != stop; k += inc) { 58325 if (isfp16zero(pv[k])) { 58326 res = k; 58327 break; 58328 } 58329 } 58330 } else if (hf = tofp16(d), d == fromfp16(hf)) { 58331 const uint16_t *pv = p->u.array.u.fp16_ptr; 58332 for (; k != stop; k += inc) { 58333 if (pv[k] == hf) { 58334 res = k; 58335 break; 58336 } 58337 } 58338 } 58339 break; 58340 case JS_CLASS_FLOAT32_ARRAY: 58341 if (is_bigint) 58342 break; 58343 if (isnan(d)) { 58344 const float *pv = p->u.array.u.float_ptr; 58345 /* special case: indexOf returns -1, includes finds NaN */ 58346 if (special != special_includes) 58347 goto done; 58348 for (; k != stop; k += inc) { 58349 if (isnan(pv[k])) { 58350 res = k; 58351 break; 58352 } 58353 } 58354 } else if ((f = (float)d) == d) { 58355 const float *pv = p->u.array.u.float_ptr; 58356 for (; k != stop; k += inc) { 58357 if (pv[k] == f) { 58358 res = k; 58359 break; 58360 } 58361 } 58362 } 58363 break; 58364 case JS_CLASS_FLOAT64_ARRAY: 58365 if (is_bigint) 58366 break; 58367 if (isnan(d)) { 58368 const double *pv = p->u.array.u.double_ptr; 58369 /* special case: indexOf returns -1, includes finds NaN */ 58370 if (special != special_includes) 58371 goto done; 58372 for (; k != stop; k += inc) { 58373 if (isnan(pv[k])) { 58374 res = k; 58375 break; 58376 } 58377 } 58378 } else { 58379 const double *pv = p->u.array.u.double_ptr; 58380 for (; k != stop; k += inc) { 58381 if (pv[k] == d) { 58382 res = k; 58383 break; 58384 } 58385 } 58386 } 58387 break; 58388 case JS_CLASS_BIG_INT64_ARRAY: 58389 if (is_bigint) { 58390 goto scan64; 58391 } 58392 break; 58393 case JS_CLASS_BIG_UINT64_ARRAY: 58394 if (is_bigint) { 58395 const uint64_t *pv; 58396 uint64_t v; 58397 scan64: 58398 pv = p->u.array.u.uint64_ptr; 58399 v = v64; 58400 for (; k != stop; k += inc) { 58401 if (pv[k] == v) { 58402 res = k; 58403 break; 58404 } 58405 } 58406 } 58407 break; 58408 } 58409 58410 done: 58411 if (special == special_includes) 58412 return JS_NewBool(ctx, res >= 0); 58413 else 58414 return JS_NewInt32(ctx, res); 58415 58416 exception: 58417 return JS_EXCEPTION; 58418 } 58419 58420 static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, 58421 int argc, JSValueConst *argv, int toLocaleString) 58422 { 58423 JSValue sep = JS_UNDEFINED, el; 58424 StringBuffer b_s, *b = &b_s; 58425 JSString *s = NULL; 58426 JSObject *p; 58427 int i, len, oldlen, newlen; 58428 int c; 58429 58430 p = get_typed_array(ctx, this_val); 58431 if (!p) 58432 return JS_EXCEPTION; 58433 if (typed_array_is_oob(p)) 58434 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 58435 len = oldlen = newlen = p->u.array.count; 58436 58437 c = ','; /* default separator */ 58438 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { 58439 sep = JS_ToString(ctx, argv[0]); 58440 if (JS_IsException(sep)) 58441 goto exception; 58442 s = JS_VALUE_GET_STRING(sep); 58443 if (s->len == 1 && !s->is_wide_char) 58444 c = s->u.str8[0]; 58445 else 58446 c = -1; 58447 // ToString(sep) can detach or resize the arraybuffer as a side effect 58448 newlen = p->u.array.count; 58449 len = min_int(len, newlen); 58450 } 58451 string_buffer_init(ctx, b, 0); 58452 58453 /* XXX: optimize with direct access */ 58454 for(i = 0; i < len; i++) { 58455 if (i > 0) { 58456 if (c >= 0) { 58457 if (string_buffer_putc8(b, c)) 58458 goto fail; 58459 } else { 58460 if (string_buffer_concat(b, s, 0, s->len)) 58461 goto fail; 58462 } 58463 } 58464 el = JS_GetPropertyUint32(ctx, this_val, i); 58465 /* Can return undefined for example if the typed array is detached */ 58466 if (!JS_IsNull(el) && !JS_IsUndefined(el)) { 58467 if (JS_IsException(el)) 58468 goto fail; 58469 if (toLocaleString) { 58470 el = JS_ToLocaleStringFree(ctx, el); 58471 } 58472 if (string_buffer_concat_value_free(b, el)) 58473 goto fail; 58474 } 58475 } 58476 58477 // add extra separators in case RAB was resized by evil .valueOf method 58478 i = max_int(1, newlen); 58479 for(/*empty*/; i < oldlen; i++) { 58480 if (c >= 0) { 58481 if (string_buffer_putc8(b, c)) 58482 goto fail; 58483 } else { 58484 if (string_buffer_concat(b, s, 0, s->len)) 58485 goto fail; 58486 } 58487 } 58488 58489 JS_FreeValue(ctx, sep); 58490 return string_buffer_end(b); 58491 58492 fail: 58493 string_buffer_free(b); 58494 JS_FreeValue(ctx, sep); 58495 exception: 58496 return JS_EXCEPTION; 58497 } 58498 58499 static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, 58500 int argc, JSValueConst *argv) 58501 { 58502 JSObject *p; 58503 int len; 58504 58505 len = js_typed_array_get_length_unsafe(ctx, this_val); 58506 if (len < 0) 58507 return JS_EXCEPTION; 58508 if (len > 0) { 58509 p = JS_VALUE_GET_OBJ(this_val); 58510 switch (typed_array_size_log2(p->class_id)) { 58511 case 0: 58512 { 58513 uint8_t *p1 = p->u.array.u.uint8_ptr; 58514 uint8_t *p2 = p1 + len - 1; 58515 while (p1 < p2) { 58516 uint8_t v = *p1; 58517 *p1++ = *p2; 58518 *p2-- = v; 58519 } 58520 } 58521 break; 58522 case 1: 58523 { 58524 uint16_t *p1 = p->u.array.u.uint16_ptr; 58525 uint16_t *p2 = p1 + len - 1; 58526 while (p1 < p2) { 58527 uint16_t v = *p1; 58528 *p1++ = *p2; 58529 *p2-- = v; 58530 } 58531 } 58532 break; 58533 case 2: 58534 { 58535 uint32_t *p1 = p->u.array.u.uint32_ptr; 58536 uint32_t *p2 = p1 + len - 1; 58537 while (p1 < p2) { 58538 uint32_t v = *p1; 58539 *p1++ = *p2; 58540 *p2-- = v; 58541 } 58542 } 58543 break; 58544 case 3: 58545 { 58546 uint64_t *p1 = p->u.array.u.uint64_ptr; 58547 uint64_t *p2 = p1 + len - 1; 58548 while (p1 < p2) { 58549 uint64_t v = *p1; 58550 *p1++ = *p2; 58551 *p2-- = v; 58552 } 58553 } 58554 break; 58555 default: 58556 abort(); 58557 } 58558 } 58559 return JS_DupValue(ctx, this_val); 58560 } 58561 58562 static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val, 58563 int argc, JSValueConst *argv) 58564 { 58565 JSValue arr, ret; 58566 JSObject *p; 58567 58568 p = get_typed_array(ctx, this_val); 58569 if (!p) 58570 return JS_EXCEPTION; 58571 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 58572 p->class_id, p->u.array.count); 58573 if (JS_IsException(arr)) 58574 return JS_EXCEPTION; 58575 ret = js_typed_array_reverse(ctx, arr, argc, argv); 58576 JS_FreeValue(ctx, arr); 58577 return ret; 58578 } 58579 58580 static void slice_memcpy(uint8_t *dst, const uint8_t *src, size_t len) 58581 { 58582 if (dst + len <= src || dst >= src + len) { 58583 /* no overlap: can use memcpy */ 58584 memcpy(dst, src, len); 58585 } else { 58586 /* otherwise the spec mandates byte copy */ 58587 while (len-- != 0) 58588 *dst++ = *src++; 58589 } 58590 } 58591 58592 static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, 58593 int argc, JSValueConst *argv) 58594 { 58595 JSValueConst args[2]; 58596 JSValue arr, val; 58597 JSObject *p, *p1; 58598 int n, len, start, final, count, shift, space; 58599 58600 arr = JS_UNDEFINED; 58601 p = get_typed_array(ctx, this_val); 58602 if (!p) 58603 goto exception; 58604 if (typed_array_is_oob(p)) 58605 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 58606 len = p->u.array.count; 58607 58608 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) 58609 goto exception; 58610 final = len; 58611 if (!JS_IsUndefined(argv[1])) { 58612 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) 58613 goto exception; 58614 } 58615 count = max_int(final - start, 0); 58616 58617 shift = typed_array_size_log2(p->class_id); 58618 58619 args[0] = this_val; 58620 args[1] = JS_NewInt32(ctx, count); 58621 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 58622 if (JS_IsException(arr)) 58623 goto exception; 58624 58625 if (count > 0) { 58626 if (validate_typed_array(ctx, this_val) 58627 || validate_typed_array(ctx, arr)) 58628 goto exception; 58629 58630 p1 = get_typed_array(ctx, arr); 58631 space = max_int(0, p->u.array.count - start); 58632 count = min_int(count, space); 58633 if (p1 != NULL && p->class_id == p1->class_id) { 58634 slice_memcpy(p1->u.array.u.uint8_ptr, 58635 p->u.array.u.uint8_ptr + (start << shift), 58636 count << shift); 58637 } else { 58638 for (n = 0; n < count; n++) { 58639 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); 58640 if (JS_IsException(val)) 58641 goto exception; 58642 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, 58643 JS_PROP_THROW) < 0) 58644 goto exception; 58645 } 58646 } 58647 } 58648 return arr; 58649 58650 exception: 58651 JS_FreeValue(ctx, arr); 58652 return JS_EXCEPTION; 58653 } 58654 58655 static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val, 58656 int argc, JSValueConst *argv) 58657 { 58658 JSValueConst args[4]; 58659 JSValue arr, ta_buffer; 58660 JSTypedArray *ta; 58661 JSObject *p; 58662 int len, start, final, count, shift, offset; 58663 BOOL is_auto; 58664 58665 p = get_typed_array(ctx, this_val); 58666 if (!p) 58667 goto exception; 58668 len = p->u.array.count; 58669 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) 58670 goto exception; 58671 58672 shift = typed_array_size_log2(p->class_id); 58673 ta = p->u.typed_array; 58674 /* Read byteOffset (ta->offset) even if detached */ 58675 offset = ta->offset + (start << shift); 58676 58677 final = len; 58678 if (JS_IsUndefined(argv[1])) { 58679 is_auto = ta->track_rab; 58680 } else { 58681 is_auto = FALSE; 58682 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) 58683 goto exception; 58684 } 58685 count = max_int(final - start, 0); 58686 ta_buffer = js_typed_array_get_buffer(ctx, this_val); 58687 if (JS_IsException(ta_buffer)) 58688 goto exception; 58689 args[0] = this_val; 58690 args[1] = ta_buffer; 58691 args[2] = JS_NewInt32(ctx, offset); 58692 args[3] = JS_NewInt32(ctx, count); 58693 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, is_auto ? 3 : 4, args); 58694 JS_FreeValue(ctx, ta_buffer); 58695 return arr; 58696 58697 exception: 58698 return JS_EXCEPTION; 58699 } 58700 58701 /* TypedArray.prototype.sort */ 58702 58703 static int js_cmp_doubles(double x, double y) 58704 { 58705 if (isnan(x)) return isnan(y) ? 0 : +1; 58706 if (isnan(y)) return -1; 58707 if (x < y) return -1; 58708 if (x > y) return 1; 58709 if (x != 0) return 0; 58710 if (signbit(x)) return signbit(y) ? 0 : -1; 58711 else return signbit(y) ? 1 : 0; 58712 } 58713 58714 static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) { 58715 return *(const int8_t *)a - *(const int8_t *)b; 58716 } 58717 58718 static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) { 58719 return *(const uint8_t *)a - *(const uint8_t *)b; 58720 } 58721 58722 static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) { 58723 return *(const int16_t *)a - *(const int16_t *)b; 58724 } 58725 58726 static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) { 58727 return *(const uint16_t *)a - *(const uint16_t *)b; 58728 } 58729 58730 static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) { 58731 int32_t x = *(const int32_t *)a; 58732 int32_t y = *(const int32_t *)b; 58733 return (y < x) - (y > x); 58734 } 58735 58736 static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { 58737 uint32_t x = *(const uint32_t *)a; 58738 uint32_t y = *(const uint32_t *)b; 58739 return (y < x) - (y > x); 58740 } 58741 58742 static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { 58743 int64_t x = *(const int64_t *)a; 58744 int64_t y = *(const int64_t *)b; 58745 return (y < x) - (y > x); 58746 } 58747 58748 static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { 58749 uint64_t x = *(const uint64_t *)a; 58750 uint64_t y = *(const uint64_t *)b; 58751 return (y < x) - (y > x); 58752 } 58753 58754 static int js_TA_cmp_float16(const void *a, const void *b, void *opaque) { 58755 return js_cmp_doubles(fromfp16(*(const uint16_t *)a), 58756 fromfp16(*(const uint16_t *)b)); 58757 } 58758 58759 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { 58760 return js_cmp_doubles(*(const float *)a, *(const float *)b); 58761 } 58762 58763 static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) { 58764 return js_cmp_doubles(*(const double *)a, *(const double *)b); 58765 } 58766 58767 static JSValue js_TA_get_int8(JSContext *ctx, const void *a) { 58768 return JS_NewInt32(ctx, *(const int8_t *)a); 58769 } 58770 58771 static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) { 58772 return JS_NewInt32(ctx, *(const uint8_t *)a); 58773 } 58774 58775 static JSValue js_TA_get_int16(JSContext *ctx, const void *a) { 58776 return JS_NewInt32(ctx, *(const int16_t *)a); 58777 } 58778 58779 static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) { 58780 return JS_NewInt32(ctx, *(const uint16_t *)a); 58781 } 58782 58783 static JSValue js_TA_get_int32(JSContext *ctx, const void *a) { 58784 return JS_NewInt32(ctx, *(const int32_t *)a); 58785 } 58786 58787 static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { 58788 return JS_NewUint32(ctx, *(const uint32_t *)a); 58789 } 58790 58791 static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { 58792 return JS_NewBigInt64(ctx, *(int64_t *)a); 58793 } 58794 58795 static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { 58796 return JS_NewBigUint64(ctx, *(uint64_t *)a); 58797 } 58798 58799 static JSValue js_TA_get_float16(JSContext *ctx, const void *a) { 58800 return __JS_NewFloat64(ctx, fromfp16(*(const uint16_t *)a)); 58801 } 58802 58803 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { 58804 return __JS_NewFloat64(ctx, *(const float *)a); 58805 } 58806 58807 static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { 58808 return __JS_NewFloat64(ctx, *(const double *)a); 58809 } 58810 58811 struct TA_sort_context { 58812 JSContext *ctx; 58813 int exception; /* 1 = exception, 2 = detached typed array */ 58814 uint8_t *array; 58815 JSValueConst cmp; 58816 JSValue (*getfun)(JSContext *ctx, const void *a); 58817 int elt_size; 58818 }; 58819 58820 static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { 58821 struct TA_sort_context *psc = opaque; 58822 JSContext *ctx = psc->ctx; 58823 uint32_t a_idx, b_idx; 58824 JSValueConst argv[2]; 58825 JSValue res; 58826 int cmp; 58827 58828 cmp = 0; 58829 if (!psc->exception) { 58830 /* Note: the typed array can be detached without causing an 58831 error */ 58832 a_idx = *(uint32_t *)a; 58833 b_idx = *(uint32_t *)b; 58834 argv[0] = psc->getfun(ctx, psc->array + 58835 a_idx * (size_t)psc->elt_size); 58836 argv[1] = psc->getfun(ctx, psc->array + 58837 b_idx * (size_t)(psc->elt_size)); 58838 res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); 58839 if (JS_IsException(res)) { 58840 psc->exception = 1; 58841 goto done; 58842 } 58843 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { 58844 int val = JS_VALUE_GET_INT(res); 58845 cmp = (val > 0) - (val < 0); 58846 } else { 58847 double val; 58848 if (JS_ToFloat64Free(ctx, &val, res) < 0) { 58849 psc->exception = 1; 58850 goto done; 58851 } else { 58852 cmp = (val > 0) - (val < 0); 58853 } 58854 } 58855 if (cmp == 0) { 58856 /* make sort stable: compare array offsets */ 58857 cmp = (a_idx > b_idx) - (a_idx < b_idx); 58858 } 58859 done: 58860 JS_FreeValue(ctx, (JSValue)argv[0]); 58861 JS_FreeValue(ctx, (JSValue)argv[1]); 58862 } 58863 return cmp; 58864 } 58865 58866 static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, 58867 int argc, JSValueConst *argv) 58868 { 58869 JSObject *p; 58870 int len; 58871 size_t elt_size; 58872 struct TA_sort_context tsc; 58873 int (*cmpfun)(const void *a, const void *b, void *opaque); 58874 58875 tsc.ctx = ctx; 58876 tsc.exception = 0; 58877 tsc.cmp = argv[0]; 58878 58879 if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) 58880 return JS_EXCEPTION; 58881 len = js_typed_array_get_length_unsafe(ctx, this_val); 58882 if (len < 0) 58883 return JS_EXCEPTION; 58884 58885 if (len > 1) { 58886 p = JS_VALUE_GET_OBJ(this_val); 58887 switch (p->class_id) { 58888 case JS_CLASS_INT8_ARRAY: 58889 tsc.getfun = js_TA_get_int8; 58890 cmpfun = js_TA_cmp_int8; 58891 break; 58892 case JS_CLASS_UINT8C_ARRAY: 58893 case JS_CLASS_UINT8_ARRAY: 58894 tsc.getfun = js_TA_get_uint8; 58895 cmpfun = js_TA_cmp_uint8; 58896 break; 58897 case JS_CLASS_INT16_ARRAY: 58898 tsc.getfun = js_TA_get_int16; 58899 cmpfun = js_TA_cmp_int16; 58900 break; 58901 case JS_CLASS_UINT16_ARRAY: 58902 tsc.getfun = js_TA_get_uint16; 58903 cmpfun = js_TA_cmp_uint16; 58904 break; 58905 case JS_CLASS_INT32_ARRAY: 58906 tsc.getfun = js_TA_get_int32; 58907 cmpfun = js_TA_cmp_int32; 58908 break; 58909 case JS_CLASS_UINT32_ARRAY: 58910 tsc.getfun = js_TA_get_uint32; 58911 cmpfun = js_TA_cmp_uint32; 58912 break; 58913 case JS_CLASS_BIG_INT64_ARRAY: 58914 tsc.getfun = js_TA_get_int64; 58915 cmpfun = js_TA_cmp_int64; 58916 break; 58917 case JS_CLASS_BIG_UINT64_ARRAY: 58918 tsc.getfun = js_TA_get_uint64; 58919 cmpfun = js_TA_cmp_uint64; 58920 break; 58921 case JS_CLASS_FLOAT16_ARRAY: 58922 tsc.getfun = js_TA_get_float16; 58923 cmpfun = js_TA_cmp_float16; 58924 break; 58925 case JS_CLASS_FLOAT32_ARRAY: 58926 tsc.getfun = js_TA_get_float32; 58927 cmpfun = js_TA_cmp_float32; 58928 break; 58929 case JS_CLASS_FLOAT64_ARRAY: 58930 tsc.getfun = js_TA_get_float64; 58931 cmpfun = js_TA_cmp_float64; 58932 break; 58933 default: 58934 abort(); 58935 } 58936 elt_size = 1 << typed_array_size_log2(p->class_id); 58937 if (!JS_IsUndefined(tsc.cmp)) { 58938 uint32_t *array_idx; 58939 void *array; 58940 size_t i, j; 58941 58942 /* the array must be copied because the comparison 58943 function may modify it */ 58944 array = js_malloc(ctx, len * elt_size); 58945 if (!array) 58946 return JS_EXCEPTION; 58947 memcpy(array, p->u.array.u.ptr, len * elt_size); 58948 58949 /* array_idx is needed to have a stable sort */ 58950 array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); 58951 if (!array_idx) { 58952 js_free(ctx, array); 58953 return JS_EXCEPTION; 58954 } 58955 for(i = 0; i < len; i++) 58956 array_idx[i] = i; 58957 tsc.elt_size = elt_size; 58958 tsc.array = array; 58959 rqsort(array_idx, len, sizeof(array_idx[0]), 58960 js_TA_cmp_generic, &tsc); 58961 if (tsc.exception) { 58962 if (tsc.exception == 1) { 58963 js_free(ctx, array_idx); 58964 js_free(ctx, array); 58965 return JS_EXCEPTION; 58966 } 58967 /* detached typed array during the sort: no error */ 58968 } else { 58969 void *array_ptr = p->u.array.u.ptr; 58970 len = min_int(len, p->u.array.count); 58971 switch(elt_size) { 58972 case 1: 58973 for(i = 0; i < len; i++) { 58974 j = array_idx[i]; 58975 ((uint8_t *)array_ptr)[i] = ((uint8_t *)array)[j]; 58976 } 58977 break; 58978 case 2: 58979 for(i = 0; i < len; i++) { 58980 j = array_idx[i]; 58981 ((uint16_t *)array_ptr)[i] = ((uint16_t *)array)[j]; 58982 } 58983 break; 58984 case 4: 58985 for(i = 0; i < len; i++) { 58986 j = array_idx[i]; 58987 ((uint32_t *)array_ptr)[i] = ((uint32_t *)array)[j]; 58988 } 58989 break; 58990 case 8: 58991 for(i = 0; i < len; i++) { 58992 j = array_idx[i]; 58993 ((uint64_t *)array_ptr)[i] = ((uint64_t *)array)[j]; 58994 } 58995 break; 58996 default: 58997 abort(); 58998 } 58999 } 59000 js_free(ctx, array_idx); 59001 js_free(ctx, array); 59002 } else { 59003 rqsort(p->u.array.u.ptr, len, elt_size, cmpfun, &tsc); 59004 if (tsc.exception) 59005 return JS_EXCEPTION; 59006 } 59007 } 59008 return JS_DupValue(ctx, this_val); 59009 } 59010 59011 static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val, 59012 int argc, JSValueConst *argv) 59013 { 59014 JSValue arr, ret; 59015 JSObject *p; 59016 59017 p = get_typed_array(ctx, this_val); 59018 if (!p) 59019 return JS_EXCEPTION; 59020 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 59021 p->class_id, p->u.array.count); 59022 if (JS_IsException(arr)) 59023 return JS_EXCEPTION; 59024 ret = js_typed_array_sort(ctx, arr, argc, argv); 59025 JS_FreeValue(ctx, arr); 59026 return ret; 59027 } 59028 59029 /* Uint8Array base64/hex (tc39 proposal-arraybuffer-base64) */ 59030 59031 enum { 59032 B64_ALPHABET_BASE64 = 0, 59033 B64_ALPHABET_BASE64URL = 1, 59034 }; 59035 59036 enum { 59037 B64_LAST_LOOSE = 0, 59038 B64_LAST_STRICT = 1, 59039 B64_LAST_STOP_BEFORE_PARTIAL = 2, 59040 }; 59041 59042 static const unsigned char b64_enc[64] = { 59043 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 59044 'Q','R','S','T','U','V','W','X','Y','Z', 59045 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', 59046 'q','r','s','t','u','v','w','x','y','z', 59047 '0','1','2','3','4','5','6','7','8','9', 59048 '+','/' 59049 }; 59050 59051 static const unsigned char b64url_enc[64] = { 59052 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 59053 'Q','R','S','T','U','V','W','X','Y','Z', 59054 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p', 59055 'q','r','s','t','u','v','w','x','y','z', 59056 '0','1','2','3','4','5','6','7','8','9', 59057 '-','_' 59058 }; 59059 59060 #define K_WS 64 59061 #define K_ER 65 59062 59063 static const uint8_t b64_dec[256] = { 59064 [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER, 59065 [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER, 59066 [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER, 59067 [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER, 59068 [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER, 59069 ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']= 62, [',']=K_ER, ['-']=K_ER, ['.']=K_ER, ['/']= 63, 59070 ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59, 59071 ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER, 59072 ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6, 59073 ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14, 59074 ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22, 59075 ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']=K_ER, 59076 ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32, 59077 ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40, 59078 ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48, 59079 ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER, 59080 [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER, 59081 [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER, 59082 [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER, 59083 [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER, 59084 [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER, 59085 [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER, 59086 [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER, 59087 [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER, 59088 [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER, 59089 [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER, 59090 [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER, 59091 [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER, 59092 [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER, 59093 [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER, 59094 [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER, 59095 [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER, 59096 }; 59097 59098 static const uint8_t b64url_dec[256] = { 59099 [ 0]=K_ER, [ 1]=K_ER, [ 2]=K_ER, [ 3]=K_ER, [ 4]=K_ER, [ 5]=K_ER, [ 6]=K_ER, [ 7]=K_ER, 59100 [ 8]=K_ER, [ 9]=K_WS, [ 10]=K_WS, [ 11]=K_ER, [ 12]=K_WS, [ 13]=K_WS, [ 14]=K_ER, [ 15]=K_ER, 59101 [ 16]=K_ER, [ 17]=K_ER, [ 18]=K_ER, [ 19]=K_ER, [ 20]=K_ER, [ 21]=K_ER, [ 22]=K_ER, [ 23]=K_ER, 59102 [ 24]=K_ER, [ 25]=K_ER, [ 26]=K_ER, [ 27]=K_ER, [ 28]=K_ER, [ 29]=K_ER, [ 30]=K_ER, [ 31]=K_ER, 59103 [' ']=K_WS, ['!']=K_ER, ['"']=K_ER, ['#']=K_ER, ['$']=K_ER, ['%']=K_ER, ['&']=K_ER, [ 39]=K_ER, 59104 ['(']=K_ER, [')']=K_ER, ['*']=K_ER, ['+']=K_ER, [',']=K_ER, ['-']= 62, ['.']=K_ER, ['/']=K_ER, 59105 ['0']= 52, ['1']= 53, ['2']= 54, ['3']= 55, ['4']= 56, ['5']= 57, ['6']= 58, ['7']= 59, 59106 ['8']= 60, ['9']= 61, [':']=K_ER, [';']=K_ER, ['<']=K_ER, ['=']=K_ER, ['>']=K_ER, ['?']=K_ER, 59107 ['@']=K_ER, ['A']= 0, ['B']= 1, ['C']= 2, ['D']= 3, ['E']= 4, ['F']= 5, ['G']= 6, 59108 ['H']= 7, ['I']= 8, ['J']= 9, ['K']= 10, ['L']= 11, ['M']= 12, ['N']= 13, ['O']= 14, 59109 ['P']= 15, ['Q']= 16, ['R']= 17, ['S']= 18, ['T']= 19, ['U']= 20, ['V']= 21, ['W']= 22, 59110 ['X']= 23, ['Y']= 24, ['Z']= 25, ['[']=K_ER, [ 92]=K_ER, [']']=K_ER, ['^']=K_ER, ['_']= 63, 59111 ['`']=K_ER, ['a']= 26, ['b']= 27, ['c']= 28, ['d']= 29, ['e']= 30, ['f']= 31, ['g']= 32, 59112 ['h']= 33, ['i']= 34, ['j']= 35, ['k']= 36, ['l']= 37, ['m']= 38, ['n']= 39, ['o']= 40, 59113 ['p']= 41, ['q']= 42, ['r']= 43, ['s']= 44, ['t']= 45, ['u']= 46, ['v']= 47, ['w']= 48, 59114 ['x']= 49, ['y']= 50, ['z']= 51, ['{']=K_ER, ['|']=K_ER, ['}']=K_ER, ['~']=K_ER, [127]=K_ER, 59115 [128]=K_ER, [129]=K_ER, [130]=K_ER, [131]=K_ER, [132]=K_ER, [133]=K_ER, [134]=K_ER, [135]=K_ER, 59116 [136]=K_ER, [137]=K_ER, [138]=K_ER, [139]=K_ER, [140]=K_ER, [141]=K_ER, [142]=K_ER, [143]=K_ER, 59117 [144]=K_ER, [145]=K_ER, [146]=K_ER, [147]=K_ER, [148]=K_ER, [149]=K_ER, [150]=K_ER, [151]=K_ER, 59118 [152]=K_ER, [153]=K_ER, [154]=K_ER, [155]=K_ER, [156]=K_ER, [157]=K_ER, [158]=K_ER, [159]=K_ER, 59119 [160]=K_ER, [161]=K_ER, [162]=K_ER, [163]=K_ER, [164]=K_ER, [165]=K_ER, [166]=K_ER, [167]=K_ER, 59120 [168]=K_ER, [169]=K_ER, [170]=K_ER, [171]=K_ER, [172]=K_ER, [173]=K_ER, [174]=K_ER, [175]=K_ER, 59121 [176]=K_ER, [177]=K_ER, [178]=K_ER, [179]=K_ER, [180]=K_ER, [181]=K_ER, [182]=K_ER, [183]=K_ER, 59122 [184]=K_ER, [185]=K_ER, [186]=K_ER, [187]=K_ER, [188]=K_ER, [189]=K_ER, [190]=K_ER, [191]=K_ER, 59123 [192]=K_ER, [193]=K_ER, [194]=K_ER, [195]=K_ER, [196]=K_ER, [197]=K_ER, [198]=K_ER, [199]=K_ER, 59124 [200]=K_ER, [201]=K_ER, [202]=K_ER, [203]=K_ER, [204]=K_ER, [205]=K_ER, [206]=K_ER, [207]=K_ER, 59125 [208]=K_ER, [209]=K_ER, [210]=K_ER, [211]=K_ER, [212]=K_ER, [213]=K_ER, [214]=K_ER, [215]=K_ER, 59126 [216]=K_ER, [217]=K_ER, [218]=K_ER, [219]=K_ER, [220]=K_ER, [221]=K_ER, [222]=K_ER, [223]=K_ER, 59127 [224]=K_ER, [225]=K_ER, [226]=K_ER, [227]=K_ER, [228]=K_ER, [229]=K_ER, [230]=K_ER, [231]=K_ER, 59128 [232]=K_ER, [233]=K_ER, [234]=K_ER, [235]=K_ER, [236]=K_ER, [237]=K_ER, [238]=K_ER, [239]=K_ER, 59129 [240]=K_ER, [241]=K_ER, [242]=K_ER, [243]=K_ER, [244]=K_ER, [245]=K_ER, [246]=K_ER, [247]=K_ER, 59130 [248]=K_ER, [249]=K_ER, [250]=K_ER, [251]=K_ER, [252]=K_ER, [253]=K_ER, [254]=K_ER, [255]=K_ER, 59131 }; 59132 59133 static size_t b64_encode(const uint8_t *src, size_t len, char *dst, 59134 const unsigned char *alpha) 59135 { 59136 size_t i, j; 59137 59138 for (i = 0, j = 0; i + 3 <= len; i += 3, j += 4) { 59139 uint32_t v = 65536*src[i] + 256*src[i + 1] + src[i + 2]; 59140 dst[j + 0] = alpha[(v >> 18) & 63]; 59141 dst[j + 1] = alpha[(v >> 12) & 63]; 59142 dst[j + 2] = alpha[(v >> 6) & 63]; 59143 dst[j + 3] = alpha[v & 63]; 59144 } 59145 59146 size_t rem = len - i; 59147 if (rem == 1) { 59148 uint32_t v = 65536*src[i]; 59149 dst[j++] = alpha[(v >> 18) & 63]; 59150 dst[j++] = alpha[(v >> 12) & 63]; 59151 dst[j++] = '='; 59152 dst[j++] = '='; 59153 } else if (rem == 2) { 59154 uint32_t v = 65536*src[i] + 256*src[i + 1]; 59155 dst[j++] = alpha[(v >> 18) & 63]; 59156 dst[j++] = alpha[(v >> 12) & 63]; 59157 dst[j++] = alpha[(v >> 6) & 63]; 59158 dst[j++] = '='; 59159 } 59160 return j; 59161 } 59162 59163 static size_t b64_skip_ws(const char *src, size_t len, size_t index, 59164 const uint8_t *dec_table) 59165 { 59166 while (index < len && dec_table[(unsigned char)src[index]] == K_WS) 59167 index++; 59168 return index; 59169 } 59170 59171 /* Implements the FromBase64 abstract operation. 59172 src/src_len: the input string (must be ASCII/latin1) 59173 dst/max_len: output buffer 59174 flags: b64_flags or b64_flags_url (selects valid characters) 59175 last_chunk: B64_LAST_LOOSE, B64_LAST_STRICT, or B64_LAST_STOP_BEFORE_PARTIAL 59176 *p_read: set to number of input characters consumed 59177 *p_err: set to 1 on error, 0 on success 59178 Returns: number of bytes written to dst */ 59179 static size_t from_base64(const char *src, size_t src_len, 59180 uint8_t *dst, size_t max_len, 59181 const uint8_t *dec_table, int last_chunk, 59182 size_t *p_read, int *p_err) 59183 { 59184 size_t read = 0, written = 0; 59185 uint32_t v, acc = 0; 59186 int seen = 0; 59187 size_t index = 0; 59188 uint8_t ch; 59189 59190 *p_err = 0; 59191 59192 if (max_len == 0) { 59193 *p_read = 0; 59194 return 0; 59195 } 59196 59197 for (;;) { 59198 if (seen == 0) { 59199 /* Fast path: decode complete groups of 4 valid characters. 59200 Breaks out on whitespace, padding, invalid chars, or capacity. */ 59201 while (index + 4 <= src_len && written + 3 <= max_len) { 59202 uint32_t v0, v1, v2, v3; 59203 v0 = dec_table[(unsigned char)src[index]]; 59204 v1 = dec_table[(unsigned char)src[index + 1]]; 59205 v2 = dec_table[(unsigned char)src[index + 2]]; 59206 v3 = dec_table[(unsigned char)src[index + 3]]; 59207 if ((v0 | v1 | v2 | v3) >= 64) 59208 break; 59209 v = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3; 59210 dst[written] = (uint8_t)(v >> 16); 59211 dst[written + 1] = (uint8_t)(v >> 8); 59212 dst[written + 2] = (uint8_t)(v); 59213 written += 3; 59214 index += 4; 59215 } 59216 read = index; 59217 59218 if (written >= max_len) { 59219 *p_read = read; 59220 return written; 59221 } 59222 } 59223 59224 /* Slow path: handle whitespace, padding, partial groups, capacity. */ 59225 index = b64_skip_ws(src, src_len, index, dec_table); 59226 59227 if (index == src_len) { 59228 if (seen > 0) { 59229 if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) { 59230 *p_read = read; 59231 return written; 59232 } 59233 if (last_chunk == B64_LAST_STRICT) { 59234 *p_err = 1; 59235 return 0; 59236 } 59237 /* loose */ 59238 if (seen == 1) { 59239 *p_err = 1; 59240 return 0; 59241 } 59242 break; 59243 } 59244 *p_read = src_len; 59245 return written; 59246 } 59247 59248 ch = src[index++]; 59249 59250 if (ch == '=') { 59251 if (seen < 2) { 59252 *p_err = 1; 59253 return 0; 59254 } 59255 index = b64_skip_ws(src, src_len, index, dec_table); 59256 if (seen == 2) { 59257 if (index == src_len) { 59258 if (last_chunk == B64_LAST_STOP_BEFORE_PARTIAL) { 59259 *p_read = read; 59260 return written; 59261 } 59262 *p_err = 1; 59263 return 0; 59264 } 59265 if (src[index] == '=') { 59266 index++; 59267 index = b64_skip_ws(src, src_len, index, dec_table); 59268 } else { 59269 *p_err = 1; 59270 return 0; 59271 } 59272 } 59273 /* After padding, only whitespace is allowed */ 59274 if (index != src_len) { 59275 *p_err = 1; 59276 return 0; 59277 } 59278 if (last_chunk == B64_LAST_STRICT) { 59279 uint32_t mask = (seen == 2) ? 0xF : 0x3; 59280 if (acc & mask) { 59281 *p_err = 1; 59282 return 0; 59283 } 59284 } 59285 break; 59286 } 59287 59288 v = dec_table[ch]; 59289 if (v >= 64) { 59290 *p_err = 1; 59291 return 0; 59292 } 59293 59294 /* Check remaining capacity before committing to this group */ 59295 { 59296 size_t remaining = max_len - written; 59297 if ((remaining == 1 && seen == 2) || 59298 (remaining == 2 && seen == 3)) { 59299 *p_read = read; 59300 return written; 59301 } 59302 } 59303 59304 acc = (acc << 6) | v; 59305 seen++; 59306 59307 if (seen == 4) { 59308 dst[written] = (uint8_t)(acc >> 16); 59309 dst[written + 1] = (uint8_t)(acc >> 8); 59310 dst[written + 2] = (uint8_t)(acc); 59311 written += 3; 59312 acc = 0; 59313 seen = 0; 59314 read = index; 59315 if (written >= max_len) { 59316 *p_read = read; 59317 return written; 59318 } 59319 } 59320 } 59321 59322 if (seen == 2) { 59323 dst[written++] = (uint8_t)(acc >> 4); 59324 } else if (seen == 3) { 59325 dst[written] = (uint8_t)(acc >> 10); 59326 dst[written + 1] = (uint8_t)(acc >> 2); 59327 written += 2; 59328 } 59329 *p_read = src_len; 59330 return written; 59331 } 59332 59333 /* Hex helpers */ 59334 static const char u8a_hex_digits[] = "0123456789abcdef"; 59335 59336 static size_t u8a_hex_encode(const uint8_t *src, size_t len, char *dst) 59337 { 59338 for (size_t i = 0; i < len; i++) { 59339 dst[i * 2] = u8a_hex_digits[src[i] >> 4]; 59340 dst[i * 2 + 1] = u8a_hex_digits[src[i] & 0xF]; 59341 } 59342 return len * 2; 59343 } 59344 59345 /* Decode hex string to bytes. 59346 Returns bytes written. Sets *p_read to chars consumed, *p_err on error. */ 59347 static size_t u8a_hex_decode(const char *src, size_t src_len, 59348 uint8_t *dst, size_t max_len, 59349 size_t *p_read, int *p_err) 59350 { 59351 size_t written = 0, i = 0; 59352 *p_err = 0; 59353 59354 if (src_len & 1) { 59355 *p_err = 1; 59356 return 0; 59357 } 59358 59359 while (i < src_len && written < max_len) { 59360 int hi = from_hex(src[i]); 59361 int lo = from_hex(src[i + 1]); 59362 if (hi < 0 || lo < 0) { 59363 *p_err = 1; 59364 return 0; 59365 } 59366 dst[written++] = (uint8_t)((hi << 4) | lo); 59367 i += 2; 59368 } 59369 59370 *p_read = i; 59371 return written; 59372 } 59373 59374 static JSValue JS_NewUint8ArrayCopy(JSContext *ctx, const uint8_t *buf, size_t len) 59375 { 59376 JSValue buffer, obj; 59377 JSArrayBuffer *abuf; 59378 59379 buffer = js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, NULL, 59380 JS_CLASS_ARRAY_BUFFER, 59381 (uint8_t *)buf, 59382 js_array_buffer_free, NULL, 59383 TRUE); 59384 if (JS_IsException(buffer)) 59385 return JS_EXCEPTION; 59386 obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_UINT8_ARRAY); 59387 if (JS_IsException(obj)) { 59388 JS_FreeValue(ctx, buffer); 59389 return JS_EXCEPTION; 59390 } 59391 abuf = js_get_array_buffer(ctx, buffer); 59392 assert(abuf != NULL); 59393 if (typed_array_init(ctx, obj, buffer, 0, abuf->byte_length, /*track_rab*/FALSE)) { 59394 // 'buffer' is freed on error above. 59395 JS_FreeValue(ctx, obj); 59396 return JS_EXCEPTION; 59397 } 59398 return obj; 59399 } 59400 59401 /* Validate that this_val is a Uint8Array (type check only, no detach check). 59402 Returns the JSObject pointer or NULL on error (throws). */ 59403 static JSObject *check_uint8array(JSContext *ctx, JSValueConst this_val) 59404 { 59405 JSObject *p; 59406 59407 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 59408 goto fail; 59409 p = JS_VALUE_GET_OBJ(this_val); 59410 if (p->class_id != JS_CLASS_UINT8_ARRAY) 59411 goto fail; 59412 return p; 59413 fail: 59414 JS_ThrowTypeError(ctx, "not a Uint8Array"); 59415 return NULL; 59416 } 59417 59418 /* Get the data pointer and length of a Uint8Array, checking for detached 59419 buffers. Must be called after options are read (per spec ordering). 59420 Returns 0 on success, -1 on error (throws). */ 59421 static int get_uint8array_bytes(JSContext *ctx, JSObject *p, 59422 uint8_t **pdata, size_t *plen) 59423 { 59424 if (typed_array_is_oob(p)) { 59425 JS_ThrowTypeErrorArrayBufferOOB(ctx); 59426 *pdata = NULL; /* fail safe */ 59427 *plen = 0; 59428 return -1; 59429 } 59430 *pdata = p->u.array.u.uint8_ptr; 59431 *plen = p->u.array.count; 59432 return 0; 59433 } 59434 59435 /* Validate options is undefined or an object (GetOptionsObject). 59436 Returns 0 on success, -1 on error (throws). */ 59437 static int check_options_object(JSContext *ctx, JSValueConst options) 59438 { 59439 if (JS_IsUndefined(options)) 59440 return 0; 59441 if (!JS_IsObject(options)) { 59442 JS_ThrowTypeError(ctx, "options must be an object"); 59443 return -1; 59444 } 59445 return 0; 59446 } 59447 59448 /* Parse the 'alphabet' option from an options object. 59449 Returns B64_ALPHABET_BASE64 or B64_ALPHABET_BASE64URL, or -1 on error. */ 59450 static int parse_alphabet_option(JSContext *ctx, JSValueConst options) 59451 { 59452 JSValue val; 59453 const char *str; 59454 int ret; 59455 59456 if (JS_IsUndefined(options)) 59457 return B64_ALPHABET_BASE64; 59458 59459 val = JS_GetProperty(ctx, options, JS_ATOM_alphabet); 59460 if (JS_IsException(val)) 59461 return -1; 59462 if (JS_IsUndefined(val)) 59463 return B64_ALPHABET_BASE64; 59464 if (!JS_IsString(val)) { 59465 JS_FreeValue(ctx, val); 59466 JS_ThrowTypeError(ctx, "expected string for alphabet"); 59467 return -1; 59468 } 59469 59470 str = JS_ToCString(ctx, val); 59471 JS_FreeValue(ctx, val); 59472 if (!str) 59473 return -1; 59474 59475 if (!strcmp(str, "base64")) 59476 ret = B64_ALPHABET_BASE64; 59477 else if (!strcmp(str, "base64url")) 59478 ret = B64_ALPHABET_BASE64URL; 59479 else { 59480 JS_ThrowTypeError(ctx, "invalid alphabet"); 59481 ret = -1; 59482 } 59483 JS_FreeCString(ctx, str); 59484 return ret; 59485 } 59486 59487 /* Parse the 'lastChunkHandling' option. Returns mode or -1 on error. */ 59488 static int parse_last_chunk_option(JSContext *ctx, JSValueConst options) 59489 { 59490 JSValue val; 59491 const char *str; 59492 int ret; 59493 59494 if (JS_IsUndefined(options)) 59495 return B64_LAST_LOOSE; 59496 59497 val = JS_GetProperty(ctx, options, JS_ATOM_lastChunkHandling); 59498 if (JS_IsException(val)) 59499 return -1; 59500 if (JS_IsUndefined(val)) 59501 return B64_LAST_LOOSE; 59502 if (!JS_IsString(val)) { 59503 JS_FreeValue(ctx, val); 59504 JS_ThrowTypeError(ctx, "expected string for lastChunkHandling"); 59505 return -1; 59506 } 59507 59508 str = JS_ToCString(ctx, val); 59509 JS_FreeValue(ctx, val); 59510 if (!str) 59511 return -1; 59512 59513 if (!strcmp(str, "loose")) 59514 ret = B64_LAST_LOOSE; 59515 else if (!strcmp(str, "strict")) 59516 ret = B64_LAST_STRICT; 59517 else if (!strcmp(str, "stop-before-partial")) 59518 ret = B64_LAST_STOP_BEFORE_PARTIAL; 59519 else { 59520 JS_ThrowTypeError(ctx, "invalid lastChunkHandling option"); 59521 ret = -1; 59522 } 59523 JS_FreeCString(ctx, str); 59524 return ret; 59525 } 59526 59527 /* Uint8Array.prototype.toBase64([options]) */ 59528 static JSValue js_uint8array_to_base64(JSContext *ctx, JSValueConst this_val, 59529 int argc, JSValueConst *argv) 59530 { 59531 uint8_t *data; 59532 size_t len; 59533 JSValueConst options; 59534 JSObject *p; 59535 int alphabet, omit_padding; 59536 size_t out_len, written; 59537 JSString *ostr; 59538 char *dst; 59539 59540 p = check_uint8array(ctx, this_val); 59541 if (!p) 59542 return JS_EXCEPTION; 59543 59544 options = argc > 0 ? argv[0] : JS_UNDEFINED; 59545 if (check_options_object(ctx, options)) 59546 return JS_EXCEPTION; 59547 alphabet = parse_alphabet_option(ctx, options); 59548 if (alphabet < 0) 59549 return JS_EXCEPTION; 59550 59551 omit_padding = 0; 59552 if (!JS_IsUndefined(options)) { 59553 JSValue op_val = JS_GetProperty(ctx, options, JS_ATOM_omitPadding); 59554 if (JS_IsException(op_val)) 59555 return JS_EXCEPTION; 59556 omit_padding = JS_ToBool(ctx, op_val); 59557 JS_FreeValue(ctx, op_val); 59558 } 59559 59560 if (get_uint8array_bytes(ctx, p, &data, &len)) 59561 return JS_EXCEPTION; 59562 59563 out_len = 4 * ((len + 2) / 3); 59564 59565 if (unlikely(out_len > JS_STRING_LEN_MAX)) 59566 return JS_ThrowRangeError(ctx, "output too large"); 59567 59568 ostr = js_alloc_string(ctx, out_len, 0); 59569 if (!ostr) 59570 return JS_EXCEPTION; 59571 59572 dst = (char *)ostr->u.str8; 59573 written = b64_encode(data, len, dst, 59574 alphabet == B64_ALPHABET_BASE64URL ? b64url_enc : b64_enc); 59575 if (omit_padding) { 59576 while (written > 0 && dst[written - 1] == '=') 59577 written--; 59578 } 59579 dst[written] = '\0'; 59580 59581 ostr->len = written; 59582 return JS_MKPTR(JS_TAG_STRING, ostr); 59583 } 59584 59585 /* Uint8Array.prototype.toHex() */ 59586 static JSValue js_uint8array_to_hex(JSContext *ctx, JSValueConst this_val, 59587 int argc, JSValueConst *argv) 59588 { 59589 uint8_t *data; 59590 size_t len, out_len; 59591 JSObject *p; 59592 JSString *ostr; 59593 59594 p = check_uint8array(ctx, this_val); 59595 if (!p) 59596 return JS_EXCEPTION; 59597 if (get_uint8array_bytes(ctx, p, &data, &len)) 59598 return JS_EXCEPTION; 59599 59600 out_len = len * 2; 59601 if (unlikely(out_len > JS_STRING_LEN_MAX)) 59602 return JS_ThrowRangeError(ctx, "output too large"); 59603 59604 ostr = js_alloc_string(ctx, out_len, 0); 59605 if (!ostr) 59606 return JS_EXCEPTION; 59607 59608 u8a_hex_encode(data, len, (char *)ostr->u.str8); 59609 ostr->u.str8[out_len] = '\0'; 59610 return JS_MKPTR(JS_TAG_STRING, ostr); 59611 } 59612 59613 /* Uint8Array.fromBase64(string[, options]) */ 59614 static JSValue js_uint8array_from_base64(JSContext *ctx, JSValueConst this_val, 59615 int argc, JSValueConst *argv) 59616 { 59617 const char *str; 59618 size_t str_len, read_pos, decoded_len, out_cap; 59619 int alphabet, last_chunk, err; 59620 uint8_t *buf; 59621 JSValue result; 59622 JSValueConst options; 59623 59624 if (!JS_IsString(argv[0])) 59625 return JS_ThrowTypeError(ctx, "expected string"); 59626 59627 str = JS_ToCStringLen(ctx, &str_len, argv[0]); 59628 if (!str) 59629 return JS_EXCEPTION; 59630 59631 options = argc > 1 ? argv[1] : JS_UNDEFINED; 59632 if (check_options_object(ctx, options)) { 59633 JS_FreeCString(ctx, str); 59634 return JS_EXCEPTION; 59635 } 59636 alphabet = parse_alphabet_option(ctx, options); 59637 if (alphabet < 0) { 59638 JS_FreeCString(ctx, str); 59639 return JS_EXCEPTION; 59640 } 59641 last_chunk = parse_last_chunk_option(ctx, options); 59642 if (last_chunk < 0) { 59643 JS_FreeCString(ctx, str); 59644 return JS_EXCEPTION; 59645 } 59646 59647 out_cap = (str_len / 4) * 3 + 3; 59648 buf = js_malloc(ctx, out_cap); 59649 if (!buf) { 59650 JS_FreeCString(ctx, str); 59651 return JS_EXCEPTION; 59652 } 59653 59654 decoded_len = from_base64(str, str_len, buf, out_cap, 59655 alphabet == B64_ALPHABET_BASE64URL 59656 ? b64url_dec : b64_dec, 59657 last_chunk, &read_pos, &err); 59658 JS_FreeCString(ctx, str); 59659 59660 if (err) { 59661 js_free(ctx, buf); 59662 return JS_ThrowSyntaxError(ctx, "invalid base64 string"); 59663 } 59664 59665 result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len); 59666 js_free(ctx, buf); 59667 return result; 59668 } 59669 59670 /* Uint8Array.fromHex(string) */ 59671 static JSValue js_uint8array_from_hex(JSContext *ctx, JSValueConst this_val, 59672 int argc, JSValueConst *argv) 59673 { 59674 const char *str; 59675 size_t str_len, read_pos, decoded_len, out_cap; 59676 int err; 59677 uint8_t *buf; 59678 JSValue result; 59679 59680 if (!JS_IsString(argv[0])) 59681 return JS_ThrowTypeError(ctx, "expected string"); 59682 59683 str = JS_ToCStringLen(ctx, &str_len, argv[0]); 59684 if (!str) 59685 return JS_EXCEPTION; 59686 59687 out_cap = str_len / 2 + 1; 59688 buf = js_malloc(ctx, out_cap); 59689 if (!buf) { 59690 JS_FreeCString(ctx, str); 59691 return JS_EXCEPTION; 59692 } 59693 59694 decoded_len = u8a_hex_decode(str, str_len, buf, out_cap, &read_pos, &err); 59695 JS_FreeCString(ctx, str); 59696 59697 if (err) { 59698 js_free(ctx, buf); 59699 return JS_ThrowSyntaxError(ctx, "invalid hex string"); 59700 } 59701 59702 /* XXX: could avoid the copy */ 59703 result = JS_NewUint8ArrayCopy(ctx, buf, decoded_len); 59704 js_free(ctx, buf); 59705 return result; 59706 } 59707 59708 /* Return a { read, written } result object */ 59709 static JSValue js_make_read_written(JSContext *ctx, size_t read, size_t written) 59710 { 59711 JSValue obj = JS_NewObject(ctx); 59712 if (JS_IsException(obj)) 59713 return JS_EXCEPTION; 59714 if (JS_DefinePropertyValueStr(ctx, obj, "read", 59715 JS_NewUint32(ctx, read), JS_PROP_C_W_E) < 0) 59716 goto fail; 59717 if (JS_DefinePropertyValueStr(ctx, obj, "written", 59718 JS_NewUint32(ctx, written), JS_PROP_C_W_E) < 0) 59719 goto fail; 59720 return obj; 59721 fail: 59722 JS_FreeValue(ctx, obj); 59723 return JS_EXCEPTION; 59724 } 59725 59726 /* Uint8Array.prototype.setFromBase64(string[, options]) */ 59727 static JSValue js_uint8array_set_from_base64(JSContext *ctx, 59728 JSValueConst this_val, 59729 int argc, JSValueConst *argv) 59730 { 59731 uint8_t *data; 59732 size_t len; 59733 const char *str; 59734 size_t str_len, read_pos, decoded_len; 59735 JSObject *p; 59736 int alphabet, last_chunk, err; 59737 JSValueConst options; 59738 59739 p = check_uint8array(ctx, this_val); 59740 if (!p) 59741 return JS_EXCEPTION; 59742 59743 if (!JS_IsString(argv[0])) 59744 return JS_ThrowTypeError(ctx, "expected string"); 59745 59746 str = JS_ToCStringLen(ctx, &str_len, argv[0]); 59747 if (!str) 59748 return JS_EXCEPTION; 59749 59750 options = argc > 1 ? argv[1] : JS_UNDEFINED; 59751 if (check_options_object(ctx, options)) { 59752 JS_FreeCString(ctx, str); 59753 return JS_EXCEPTION; 59754 } 59755 alphabet = parse_alphabet_option(ctx, options); 59756 if (alphabet < 0) { 59757 JS_FreeCString(ctx, str); 59758 return JS_EXCEPTION; 59759 } 59760 last_chunk = parse_last_chunk_option(ctx, options); 59761 if (last_chunk < 0) { 59762 JS_FreeCString(ctx, str); 59763 return JS_EXCEPTION; 59764 } 59765 59766 if (get_uint8array_bytes(ctx, p, &data, &len)) { 59767 JS_FreeCString(ctx, str); 59768 return JS_EXCEPTION; 59769 } 59770 59771 decoded_len = from_base64(str, str_len, data, len, 59772 alphabet == B64_ALPHABET_BASE64URL 59773 ? b64url_dec : b64_dec, 59774 last_chunk, &read_pos, &err); 59775 JS_FreeCString(ctx, str); 59776 59777 if (err) 59778 return JS_ThrowSyntaxError(ctx, "invalid base64 string"); 59779 59780 return js_make_read_written(ctx, read_pos, decoded_len); 59781 } 59782 59783 /* Uint8Array.prototype.setFromHex(string) */ 59784 static JSValue js_uint8array_set_from_hex(JSContext *ctx, 59785 JSValueConst this_val, 59786 int argc, JSValueConst *argv) 59787 { 59788 uint8_t *data; 59789 size_t len; 59790 const char *str; 59791 size_t str_len, read_pos, decoded_len; 59792 JSObject *p; 59793 int err; 59794 59795 p = check_uint8array(ctx, this_val); 59796 if (!p) 59797 return JS_EXCEPTION; 59798 59799 if (!JS_IsString(argv[0])) 59800 return JS_ThrowTypeError(ctx, "expected string"); 59801 59802 str = JS_ToCStringLen(ctx, &str_len, argv[0]); 59803 if (!str) 59804 return JS_EXCEPTION; 59805 59806 if (get_uint8array_bytes(ctx, p, &data, &len)) { 59807 JS_FreeCString(ctx, str); 59808 return JS_EXCEPTION; 59809 } 59810 59811 decoded_len = u8a_hex_decode(str, str_len, data, len, &read_pos, &err); 59812 JS_FreeCString(ctx, str); 59813 59814 if (err) 59815 return JS_ThrowSyntaxError(ctx, "invalid hex string"); 59816 59817 return js_make_read_written(ctx, read_pos, decoded_len); 59818 } 59819 59820 static const JSCFunctionListEntry js_typed_array_base_funcs[] = { 59821 JS_CFUNC_DEF("from", 1, js_typed_array_from ), 59822 JS_CFUNC_DEF("of", 0, js_typed_array_of ), 59823 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 59824 }; 59825 59826 static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { 59827 JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), 59828 JS_CFUNC_DEF("at", 1, js_typed_array_at ), 59829 JS_CFUNC_DEF("with", 2, js_typed_array_with ), 59830 JS_CGETSET_DEF("buffer", js_typed_array_get_buffer, NULL ), 59831 JS_CGETSET_DEF("byteLength", js_typed_array_get_byteLength, NULL ), 59832 JS_CGETSET_DEF("byteOffset", js_typed_array_get_byteOffset, NULL ), 59833 JS_CFUNC_DEF("set", 1, js_typed_array_set ), 59834 JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), 59835 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 59836 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ), 59837 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), 59838 JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ), 59839 JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ), 59840 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ), 59841 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ), 59842 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ), 59843 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ), 59844 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ), 59845 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), 59846 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), 59847 JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), 59848 JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ), 59849 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ), 59850 JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ), 59851 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ), 59852 JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), 59853 JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ), 59854 JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), 59855 JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), 59856 JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), 59857 JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ), 59858 JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), 59859 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), 59860 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), 59861 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ), 59862 JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ), 59863 //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ 59864 }; 59865 59866 static const JSCFunctionListEntry js_typed_array_funcs[] = { 59867 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0), 59868 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 2, 0), 59869 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 4, 0), 59870 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 8, 0), 59871 }; 59872 59873 static const JSCFunctionListEntry js_uint8array_proto_funcs[] = { 59874 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0), 59875 JS_CFUNC_DEF("toBase64", 0, js_uint8array_to_base64), 59876 JS_CFUNC_DEF("toHex", 0, js_uint8array_to_hex), 59877 JS_CFUNC_DEF("setFromBase64", 1, js_uint8array_set_from_base64), 59878 JS_CFUNC_DEF("setFromHex", 1, js_uint8array_set_from_hex), 59879 }; 59880 59881 static const JSCFunctionListEntry js_uint8array_funcs[] = { 59882 JS_PROP_INT32_DEF("BYTES_PER_ELEMENT", 1, 0), 59883 JS_CFUNC_DEF("fromBase64", 1, js_uint8array_from_base64), 59884 JS_CFUNC_DEF("fromHex", 1, js_uint8array_from_hex), 59885 }; 59886 59887 static JSValue js_typed_array_base_constructor(JSContext *ctx, 59888 JSValueConst this_val, 59889 int argc, JSValueConst *argv) 59890 { 59891 return JS_ThrowTypeError(ctx, "cannot be called"); 59892 } 59893 59894 /* 'obj' must be an allocated typed array object */ 59895 static int typed_array_init(JSContext *ctx, JSValueConst obj, 59896 JSValue buffer, uint64_t offset, uint64_t len, 59897 BOOL track_rab) 59898 { 59899 JSTypedArray *ta; 59900 JSObject *p, *pbuffer; 59901 JSArrayBuffer *abuf; 59902 int size_log2; 59903 59904 p = JS_VALUE_GET_OBJ(obj); 59905 size_log2 = typed_array_size_log2(p->class_id); 59906 ta = js_malloc(ctx, sizeof(*ta)); 59907 if (!ta) { 59908 JS_FreeValue(ctx, buffer); 59909 return -1; 59910 } 59911 pbuffer = JS_VALUE_GET_OBJ(buffer); 59912 abuf = pbuffer->u.array_buffer; 59913 ta->obj = p; 59914 ta->buffer = pbuffer; 59915 ta->offset = offset; 59916 ta->length = len << size_log2; 59917 ta->track_rab = track_rab; 59918 list_add_tail(&ta->link, &abuf->array_list); 59919 p->u.typed_array = ta; 59920 p->u.array.count = len; 59921 p->u.array.u.ptr = abuf->data + offset; 59922 return 0; 59923 } 59924 59925 JSValue JS_NewTypedArraySimple(JSContext *ctx, JSValue array_buf, size_t bytes_per_element) 59926 { 59927 JSValue obj; 59928 JSObject *p; 59929 JSArrayBuffer *abuf = NULL; 59930 59931 if (bytes_per_element != 1) { 59932 JS_FreeValue(ctx, array_buf); 59933 return JS_ThrowRangeError(ctx, "only byte arrays are supported"); 59934 } 59935 if (JS_VALUE_GET_TAG(array_buf) != JS_TAG_OBJECT) { 59936 JS_FreeValue(ctx, array_buf); 59937 return JS_ThrowTypeError(ctx, "expected array buffer"); 59938 } 59939 p = JS_VALUE_GET_OBJ(array_buf); 59940 if (p->class_id != JS_CLASS_ARRAY_BUFFER) { 59941 JS_FreeValue(ctx, array_buf); 59942 return JS_ThrowTypeError(ctx, "expected array buffer"); 59943 } 59944 abuf = p->u.array_buffer; 59945 if (abuf->detached) { 59946 JS_FreeValue(ctx, array_buf); 59947 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 59948 } 59949 obj = JS_NewObjectClass(ctx, JS_CLASS_UINT8_ARRAY); 59950 if (JS_IsException(obj)) { 59951 JS_FreeValue(ctx, array_buf); 59952 return JS_EXCEPTION; 59953 } 59954 if (typed_array_init(ctx, obj, array_buf, 0, abuf->byte_length, FALSE)) { 59955 JS_FreeValue(ctx, obj); 59956 return JS_EXCEPTION; 59957 } 59958 return obj; 59959 } 59960 59961 59962 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, 59963 JSValueConst obj, JSValueConst method) 59964 { 59965 JSValue arr, iter, next_method = JS_UNDEFINED, val; 59966 BOOL done; 59967 uint32_t k; 59968 59969 *plen = 0; 59970 arr = JS_NewArray(ctx); 59971 if (JS_IsException(arr)) 59972 return arr; 59973 iter = JS_GetIterator2(ctx, obj, method); 59974 if (JS_IsException(iter)) 59975 goto fail; 59976 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 59977 if (JS_IsException(next_method)) 59978 goto fail; 59979 k = 0; 59980 for(;;) { 59981 val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 59982 if (JS_IsException(val)) 59983 goto fail; 59984 if (done) 59985 break; 59986 if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) 59987 goto fail; 59988 k++; 59989 } 59990 JS_FreeValue(ctx, next_method); 59991 JS_FreeValue(ctx, iter); 59992 *plen = k; 59993 return arr; 59994 fail: 59995 JS_FreeValue(ctx, next_method); 59996 JS_FreeValue(ctx, iter); 59997 JS_FreeValue(ctx, arr); 59998 return JS_EXCEPTION; 59999 } 60000 60001 static JSValue js_typed_array_constructor_obj(JSContext *ctx, 60002 JSValueConst new_target, 60003 JSValueConst obj, 60004 int classid) 60005 { 60006 JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; 60007 uint32_t i; 60008 int size_log2; 60009 int64_t len; 60010 60011 size_log2 = typed_array_size_log2(classid); 60012 ret = js_create_from_ctor(ctx, new_target, classid); 60013 if (JS_IsException(ret)) 60014 return JS_EXCEPTION; 60015 60016 iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 60017 if (JS_IsException(iter)) 60018 goto fail; 60019 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { 60020 uint32_t len1; 60021 arr = js_array_from_iterator(ctx, &len1, obj, iter); 60022 JS_FreeValue(ctx, iter); 60023 if (JS_IsException(arr)) 60024 goto fail; 60025 len = len1; 60026 } else { 60027 if (js_get_length64(ctx, &len, obj)) 60028 goto fail; 60029 arr = JS_DupValue(ctx, obj); 60030 } 60031 60032 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 60033 len << size_log2, 60034 NULL); 60035 if (JS_IsException(buffer)) 60036 goto fail; 60037 if (typed_array_init(ctx, ret, buffer, 0, len, /*track_rab*/FALSE)) 60038 goto fail; 60039 60040 for(i = 0; i < len; i++) { 60041 val = JS_GetPropertyUint32(ctx, arr, i); 60042 if (JS_IsException(val)) 60043 goto fail; 60044 if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) 60045 goto fail; 60046 } 60047 JS_FreeValue(ctx, arr); 60048 return ret; 60049 fail: 60050 JS_FreeValue(ctx, arr); 60051 JS_FreeValue(ctx, ret); 60052 return JS_EXCEPTION; 60053 } 60054 60055 static JSValue js_typed_array_constructor_ta(JSContext *ctx, 60056 JSValueConst new_target, 60057 JSValueConst src_obj, 60058 int classid, uint32_t len) 60059 { 60060 JSObject *p, *src_buffer; 60061 JSTypedArray *ta; 60062 JSValue obj, buffer; 60063 uint32_t i; 60064 int size_log2; 60065 JSArrayBuffer *src_abuf, *abuf; 60066 60067 obj = js_create_from_ctor(ctx, new_target, classid); 60068 if (JS_IsException(obj)) 60069 return obj; 60070 p = JS_VALUE_GET_OBJ(src_obj); 60071 if (typed_array_is_oob(p)) { 60072 JS_ThrowTypeErrorArrayBufferOOB(ctx); 60073 goto fail; 60074 } 60075 size_log2 = typed_array_size_log2(classid); 60076 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 60077 (uint64_t)len << size_log2, 60078 NULL); 60079 if (JS_IsException(buffer)) 60080 goto fail; 60081 /* necessary because it could have been detached */ 60082 if (typed_array_is_oob(p)) { 60083 JS_FreeValue(ctx, buffer); 60084 JS_ThrowTypeErrorArrayBufferOOB(ctx); 60085 goto fail; 60086 } 60087 abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); 60088 if (typed_array_init(ctx, obj, buffer, 0, len, /*track_rab*/FALSE)) 60089 goto fail; 60090 ta = p->u.typed_array; 60091 src_buffer = ta->buffer; 60092 src_abuf = src_buffer->u.array_buffer; 60093 if (p->class_id == classid && 60094 (int64_t)ta->offset + (int64_t)abuf->byte_length <= src_abuf->byte_length) { 60095 /* same type and no overflow: copy the content */ 60096 memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); 60097 } else { 60098 for(i = 0; i < len; i++) { 60099 JSValue val; 60100 val = JS_GetPropertyUint32(ctx, src_obj, i); 60101 if (JS_IsException(val)) 60102 goto fail; 60103 if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) 60104 goto fail; 60105 } 60106 } 60107 return obj; 60108 fail: 60109 JS_FreeValue(ctx, obj); 60110 return JS_EXCEPTION; 60111 } 60112 60113 static JSValue js_typed_array_constructor(JSContext *ctx, 60114 JSValueConst new_target, 60115 int argc, JSValueConst *argv, 60116 int classid) 60117 { 60118 BOOL track_rab = FALSE; 60119 JSValue buffer, obj; 60120 JSArrayBuffer *abuf; 60121 int size_log2; 60122 uint64_t len, offset; 60123 60124 size_log2 = typed_array_size_log2(classid); 60125 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { 60126 if (JS_ToIndex(ctx, &len, argv[0])) 60127 return JS_EXCEPTION; 60128 obj = js_create_from_ctor(ctx, new_target, classid); 60129 if (JS_IsException(obj)) 60130 return JS_EXCEPTION; 60131 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 60132 len << size_log2, 60133 NULL); 60134 if (JS_IsException(buffer)) 60135 goto fail; 60136 offset = 0; 60137 } else { 60138 JSObject *p = JS_VALUE_GET_OBJ(argv[0]); 60139 if (p->class_id == JS_CLASS_ARRAY_BUFFER || 60140 p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { 60141 obj = js_create_from_ctor(ctx, new_target, classid); 60142 if (JS_IsException(obj)) 60143 return JS_EXCEPTION; 60144 if (JS_ToIndex(ctx, &offset, argv[1])) 60145 goto fail; 60146 if ((offset & ((1 << size_log2) - 1)) != 0) 60147 goto invalid_offset; 60148 abuf = p->u.array_buffer; 60149 if (JS_IsUndefined(argv[2])) { 60150 if (abuf->detached) { 60151 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60152 goto fail; 60153 } 60154 if (offset > abuf->byte_length) { 60155 invalid_offset: 60156 JS_ThrowRangeError(ctx, "invalid offset"); 60157 goto fail; 60158 } 60159 track_rab = array_buffer_is_resizable(abuf); 60160 if (!track_rab) { 60161 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) 60162 goto invalid_length; 60163 } 60164 len = (abuf->byte_length - offset) >> size_log2; 60165 } else { 60166 if (JS_ToIndex(ctx, &len, argv[2])) 60167 goto fail; 60168 if (abuf->detached) { 60169 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60170 goto fail; 60171 } 60172 if ((offset + (len << size_log2)) > abuf->byte_length) { 60173 invalid_length: 60174 JS_ThrowRangeError(ctx, "invalid length"); 60175 goto fail; 60176 } 60177 } 60178 buffer = JS_DupValue(ctx, argv[0]); 60179 } else { 60180 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 60181 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 60182 return js_typed_array_constructor_ta(ctx, new_target, argv[0], 60183 classid, p->u.array.count); 60184 } else { 60185 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); 60186 } 60187 } 60188 } 60189 if (typed_array_init(ctx, obj, buffer, offset, len, track_rab)) 60190 goto fail; 60191 return obj; 60192 fail: 60193 JS_FreeValue(ctx, obj); 60194 return JS_EXCEPTION; 60195 } 60196 60197 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) 60198 { 60199 JSObject *p = JS_VALUE_GET_OBJ(val); 60200 JSTypedArray *ta = p->u.typed_array; 60201 if (ta) { 60202 /* during the GC the finalizers are called in an arbitrary 60203 order so the ArrayBuffer finalizer may have been called */ 60204 if (ta->link.next) { 60205 list_del(&ta->link); 60206 } 60207 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 60208 js_free_rt(rt, ta); 60209 } 60210 } 60211 60212 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, 60213 JS_MarkFunc *mark_func) 60214 { 60215 JSObject *p = JS_VALUE_GET_OBJ(val); 60216 JSTypedArray *ta = p->u.typed_array; 60217 if (ta) { 60218 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); 60219 } 60220 } 60221 60222 static JSValue js_dataview_constructor(JSContext *ctx, 60223 JSValueConst new_target, 60224 int argc, JSValueConst *argv) 60225 { 60226 BOOL recompute_len = FALSE; 60227 BOOL track_rab = FALSE; 60228 JSArrayBuffer *abuf; 60229 uint64_t offset; 60230 uint32_t len; 60231 JSValueConst buffer; 60232 JSValue obj; 60233 JSTypedArray *ta; 60234 JSObject *p; 60235 60236 buffer = argv[0]; 60237 abuf = js_get_array_buffer(ctx, buffer); 60238 if (!abuf) 60239 return JS_EXCEPTION; 60240 offset = 0; 60241 if (argc > 1) { 60242 if (JS_ToIndex(ctx, &offset, argv[1])) 60243 return JS_EXCEPTION; 60244 } 60245 if (abuf->detached) 60246 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60247 if (offset > abuf->byte_length) 60248 return JS_ThrowRangeError(ctx, "invalid byteOffset"); 60249 len = abuf->byte_length - offset; 60250 if (argc > 2 && !JS_IsUndefined(argv[2])) { 60251 uint64_t l; 60252 if (JS_ToIndex(ctx, &l, argv[2])) 60253 return JS_EXCEPTION; 60254 if (l > len) 60255 return JS_ThrowRangeError(ctx, "invalid byteLength"); 60256 len = l; 60257 } else { 60258 recompute_len = TRUE; 60259 track_rab = array_buffer_is_resizable(abuf); 60260 } 60261 60262 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); 60263 if (JS_IsException(obj)) 60264 return JS_EXCEPTION; 60265 if (abuf->detached) { 60266 /* could have been detached in js_create_from_ctor() */ 60267 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60268 goto fail; 60269 } 60270 // RAB could have been resized in js_create_from_ctor() 60271 if (offset > abuf->byte_length) { 60272 goto out_of_bound; 60273 } else if (recompute_len) { 60274 len = abuf->byte_length - offset; 60275 } else if (offset + len > abuf->byte_length) { 60276 out_of_bound: 60277 JS_ThrowRangeError(ctx, "invalid byteOffset or byteLength"); 60278 goto fail; 60279 } 60280 ta = js_malloc(ctx, sizeof(*ta)); 60281 if (!ta) { 60282 fail: 60283 JS_FreeValue(ctx, obj); 60284 return JS_EXCEPTION; 60285 } 60286 p = JS_VALUE_GET_OBJ(obj); 60287 ta->obj = p; 60288 ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); 60289 ta->offset = offset; 60290 ta->length = len; 60291 ta->track_rab = track_rab; 60292 list_add_tail(&ta->link, &abuf->array_list); 60293 p->u.typed_array = ta; 60294 return obj; 60295 } 60296 60297 // is the DataView out of bounds relative to its parent arraybuffer? 60298 static BOOL dataview_is_oob(JSObject *p) 60299 { 60300 JSArrayBuffer *abuf; 60301 JSTypedArray *ta; 60302 60303 assert(p->class_id == JS_CLASS_DATAVIEW); 60304 ta = p->u.typed_array; 60305 abuf = ta->buffer->u.array_buffer; 60306 if (abuf->detached) 60307 return TRUE; 60308 if (ta->offset > abuf->byte_length) 60309 return TRUE; 60310 if (ta->track_rab) 60311 return FALSE; 60312 return (int64_t)ta->offset + ta->length > abuf->byte_length; 60313 } 60314 60315 static JSObject *get_dataview(JSContext *ctx, JSValueConst this_val) 60316 { 60317 JSObject *p; 60318 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 60319 goto fail; 60320 p = JS_VALUE_GET_OBJ(this_val); 60321 if (p->class_id != JS_CLASS_DATAVIEW) { 60322 fail: 60323 JS_ThrowTypeError(ctx, "not a DataView"); 60324 return NULL; 60325 } 60326 return p; 60327 } 60328 60329 static JSValue js_dataview_get_buffer(JSContext *ctx, JSValueConst this_val) 60330 { 60331 JSObject *p; 60332 JSTypedArray *ta; 60333 p = get_dataview(ctx, this_val); 60334 if (!p) 60335 return JS_EXCEPTION; 60336 ta = p->u.typed_array; 60337 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 60338 } 60339 60340 static JSValue js_dataview_get_byteLength(JSContext *ctx, JSValueConst this_val) 60341 { 60342 JSArrayBuffer *abuf; 60343 JSTypedArray *ta; 60344 JSObject *p; 60345 60346 p = get_dataview(ctx, this_val); 60347 if (!p) 60348 return JS_EXCEPTION; 60349 if (dataview_is_oob(p)) 60350 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 60351 ta = p->u.typed_array; 60352 if (ta->track_rab) { 60353 abuf = ta->buffer->u.array_buffer; 60354 return JS_NewUint32(ctx, abuf->byte_length - ta->offset); 60355 } 60356 return JS_NewUint32(ctx, ta->length); 60357 } 60358 60359 static JSValue js_dataview_get_byteOffset(JSContext *ctx, JSValueConst this_val) 60360 { 60361 JSTypedArray *ta; 60362 JSObject *p; 60363 60364 p = get_dataview(ctx, this_val); 60365 if (!p) 60366 return JS_EXCEPTION; 60367 if (dataview_is_oob(p)) 60368 return JS_ThrowTypeErrorArrayBufferOOB(ctx); 60369 ta = p->u.typed_array; 60370 return JS_NewUint32(ctx, ta->offset); 60371 } 60372 60373 static JSValue js_dataview_getValue(JSContext *ctx, 60374 JSValueConst this_obj, 60375 int argc, JSValueConst *argv, int class_id) 60376 { 60377 JSTypedArray *ta; 60378 JSArrayBuffer *abuf; 60379 BOOL littleEndian, is_swap; 60380 int size; 60381 uint8_t *ptr; 60382 uint32_t v; 60383 uint64_t pos; 60384 60385 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); 60386 if (!ta) 60387 return JS_EXCEPTION; 60388 size = 1 << typed_array_size_log2(class_id); 60389 if (JS_ToIndex(ctx, &pos, argv[0])) 60390 return JS_EXCEPTION; 60391 littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]); 60392 is_swap = littleEndian ^ !is_be(); 60393 abuf = ta->buffer->u.array_buffer; 60394 if (abuf->detached) 60395 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60396 // order matters: this check should come before the next one 60397 if ((pos + size) > ta->length) 60398 return JS_ThrowRangeError(ctx, "out of bound"); 60399 // test262 expects a TypeError for this and V8, in its infinite wisdom, 60400 // throws a "detached array buffer" exception, but IMO that doesn't make 60401 // sense because the buffer is not in fact detached, it's still there 60402 if ((int64_t)ta->offset + ta->length > abuf->byte_length) 60403 return JS_ThrowTypeError(ctx, "out of bound"); 60404 ptr = abuf->data + ta->offset + pos; 60405 60406 switch(class_id) { 60407 case JS_CLASS_INT8_ARRAY: 60408 return JS_NewInt32(ctx, *(int8_t *)ptr); 60409 case JS_CLASS_UINT8_ARRAY: 60410 return JS_NewInt32(ctx, *(uint8_t *)ptr); 60411 case JS_CLASS_INT16_ARRAY: 60412 v = get_u16(ptr); 60413 if (is_swap) 60414 v = bswap16(v); 60415 return JS_NewInt32(ctx, (int16_t)v); 60416 case JS_CLASS_UINT16_ARRAY: 60417 v = get_u16(ptr); 60418 if (is_swap) 60419 v = bswap16(v); 60420 return JS_NewInt32(ctx, v); 60421 case JS_CLASS_INT32_ARRAY: 60422 v = get_u32(ptr); 60423 if (is_swap) 60424 v = bswap32(v); 60425 return JS_NewInt32(ctx, v); 60426 case JS_CLASS_UINT32_ARRAY: 60427 v = get_u32(ptr); 60428 if (is_swap) 60429 v = bswap32(v); 60430 return JS_NewUint32(ctx, v); 60431 case JS_CLASS_BIG_INT64_ARRAY: 60432 { 60433 uint64_t v; 60434 v = get_u64(ptr); 60435 if (is_swap) 60436 v = bswap64(v); 60437 return JS_NewBigInt64(ctx, v); 60438 } 60439 break; 60440 case JS_CLASS_BIG_UINT64_ARRAY: 60441 { 60442 uint64_t v; 60443 v = get_u64(ptr); 60444 if (is_swap) 60445 v = bswap64(v); 60446 return JS_NewBigUint64(ctx, v); 60447 } 60448 break; 60449 case JS_CLASS_FLOAT16_ARRAY: 60450 { 60451 uint16_t v; 60452 v = get_u16(ptr); 60453 if (is_swap) 60454 v = bswap16(v); 60455 return __JS_NewFloat64(ctx, fromfp16(v)); 60456 } 60457 case JS_CLASS_FLOAT32_ARRAY: 60458 { 60459 union { 60460 float f; 60461 uint32_t i; 60462 } u; 60463 v = get_u32(ptr); 60464 if (is_swap) 60465 v = bswap32(v); 60466 u.i = v; 60467 return __JS_NewFloat64(ctx, u.f); 60468 } 60469 case JS_CLASS_FLOAT64_ARRAY: 60470 { 60471 union { 60472 double f; 60473 uint64_t i; 60474 } u; 60475 u.i = get_u64(ptr); 60476 if (is_swap) 60477 u.i = bswap64(u.i); 60478 return __JS_NewFloat64(ctx, u.f); 60479 } 60480 default: 60481 abort(); 60482 } 60483 } 60484 60485 static JSValue js_dataview_setValue(JSContext *ctx, 60486 JSValueConst this_obj, 60487 int argc, JSValueConst *argv, int class_id) 60488 { 60489 JSTypedArray *ta; 60490 JSArrayBuffer *abuf; 60491 BOOL littleEndian, is_swap; 60492 int size; 60493 uint8_t *ptr; 60494 uint64_t v64; 60495 uint32_t v; 60496 uint64_t pos; 60497 JSValueConst val; 60498 60499 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); 60500 if (!ta) 60501 return JS_EXCEPTION; 60502 size = 1 << typed_array_size_log2(class_id); 60503 if (JS_ToIndex(ctx, &pos, argv[0])) 60504 return JS_EXCEPTION; 60505 val = argv[1]; 60506 v = 0; /* avoid warning */ 60507 v64 = 0; /* avoid warning */ 60508 if (class_id <= JS_CLASS_UINT32_ARRAY) { 60509 if (JS_ToUint32(ctx, &v, val)) 60510 return JS_EXCEPTION; 60511 } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { 60512 if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) 60513 return JS_EXCEPTION; 60514 } else { 60515 double d; 60516 if (JS_ToFloat64(ctx, &d, val)) 60517 return JS_EXCEPTION; 60518 if (class_id == JS_CLASS_FLOAT16_ARRAY) { 60519 v = tofp16(d); 60520 } else if (class_id == JS_CLASS_FLOAT32_ARRAY) { 60521 union { 60522 float f; 60523 uint32_t i; 60524 } u; 60525 u.f = d; 60526 v = u.i; 60527 } else { 60528 JSFloat64Union u; 60529 u.d = d; 60530 v64 = u.u64; 60531 } 60532 } 60533 littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]); 60534 is_swap = littleEndian ^ !is_be(); 60535 abuf = ta->buffer->u.array_buffer; 60536 if (abuf->detached) 60537 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60538 // order matters: this check should come before the next one 60539 if ((pos + size) > ta->length) 60540 return JS_ThrowRangeError(ctx, "out of bound"); 60541 // test262 expects a TypeError for this and V8, in its infinite wisdom, 60542 // throws a "detached array buffer" exception, but IMO that doesn't make 60543 // sense because the buffer is not in fact detached, it's still there 60544 if ((int64_t)ta->offset + ta->length > abuf->byte_length) 60545 return JS_ThrowTypeError(ctx, "out of bound"); 60546 ptr = abuf->data + ta->offset + pos; 60547 60548 switch(class_id) { 60549 case JS_CLASS_INT8_ARRAY: 60550 case JS_CLASS_UINT8_ARRAY: 60551 *ptr = v; 60552 break; 60553 case JS_CLASS_INT16_ARRAY: 60554 case JS_CLASS_UINT16_ARRAY: 60555 case JS_CLASS_FLOAT16_ARRAY: 60556 if (is_swap) 60557 v = bswap16(v); 60558 put_u16(ptr, v); 60559 break; 60560 case JS_CLASS_INT32_ARRAY: 60561 case JS_CLASS_UINT32_ARRAY: 60562 case JS_CLASS_FLOAT32_ARRAY: 60563 if (is_swap) 60564 v = bswap32(v); 60565 put_u32(ptr, v); 60566 break; 60567 case JS_CLASS_BIG_INT64_ARRAY: 60568 case JS_CLASS_BIG_UINT64_ARRAY: 60569 case JS_CLASS_FLOAT64_ARRAY: 60570 if (is_swap) 60571 v64 = bswap64(v64); 60572 put_u64(ptr, v64); 60573 break; 60574 default: 60575 abort(); 60576 } 60577 return JS_UNDEFINED; 60578 } 60579 60580 static const JSCFunctionListEntry js_dataview_proto_funcs[] = { 60581 JS_CGETSET_DEF("buffer", js_dataview_get_buffer, NULL ), 60582 JS_CGETSET_DEF("byteLength", js_dataview_get_byteLength, NULL ), 60583 JS_CGETSET_DEF("byteOffset", js_dataview_get_byteOffset, NULL ), 60584 JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), 60585 JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), 60586 JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), 60587 JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), 60588 JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), 60589 JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), 60590 JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), 60591 JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), 60592 JS_CFUNC_MAGIC_DEF("getFloat16", 1, js_dataview_getValue, JS_CLASS_FLOAT16_ARRAY ), 60593 JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), 60594 JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), 60595 JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), 60596 JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ), 60597 JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ), 60598 JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), 60599 JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), 60600 JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), 60601 JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), 60602 JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), 60603 JS_CFUNC_MAGIC_DEF("setFloat16", 2, js_dataview_setValue, JS_CLASS_FLOAT16_ARRAY ), 60604 JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), 60605 JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), 60606 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), 60607 }; 60608 60609 /* Atomics */ 60610 #ifdef CONFIG_ATOMICS 60611 60612 typedef enum AtomicsOpEnum { 60613 ATOMICS_OP_ADD, 60614 ATOMICS_OP_AND, 60615 ATOMICS_OP_OR, 60616 ATOMICS_OP_SUB, 60617 ATOMICS_OP_XOR, 60618 ATOMICS_OP_EXCHANGE, 60619 ATOMICS_OP_COMPARE_EXCHANGE, 60620 ATOMICS_OP_LOAD, 60621 } AtomicsOpEnum; 60622 60623 static JSObject *js_atomics_get_buf(JSContext *ctx, 60624 JSValueConst obj, JSValueConst idx_val, 60625 uint64_t *pidx, int is_waitable) 60626 { 60627 JSObject *p; 60628 JSTypedArray *ta; 60629 JSArrayBuffer *abuf; 60630 uint64_t idx; 60631 BOOL err; 60632 int old_len; 60633 60634 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 60635 goto fail; 60636 p = JS_VALUE_GET_OBJ(obj); 60637 if (is_waitable) 60638 err = (p->class_id != JS_CLASS_INT32_ARRAY && 60639 p->class_id != JS_CLASS_BIG_INT64_ARRAY); 60640 else 60641 err = !(p->class_id >= JS_CLASS_INT8_ARRAY && 60642 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); 60643 if (err) { 60644 fail: 60645 JS_ThrowTypeError(ctx, "integer TypedArray expected"); 60646 return NULL; 60647 } 60648 ta = p->u.typed_array; 60649 abuf = ta->buffer->u.array_buffer; 60650 if (!abuf->shared) { 60651 if (is_waitable == 2) { 60652 JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); 60653 return NULL; 60654 } 60655 if (abuf->detached) { 60656 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60657 return NULL; 60658 } 60659 } 60660 old_len = p->u.array.count; 60661 60662 if (JS_ToIndex(ctx, &idx, idx_val)) { 60663 return NULL; 60664 } 60665 60666 if (idx >= old_len) 60667 goto oob; 60668 60669 if (is_waitable != 1) { 60670 /* RevalidateAtomicAccess() */ 60671 if (typed_array_is_oob(p)) { 60672 JS_ThrowTypeErrorArrayBufferOOB(ctx); 60673 return NULL; 60674 } 60675 if (idx >= p->u.array.count) { 60676 oob: 60677 JS_ThrowRangeError(ctx, "out-of-bound access"); 60678 return NULL; 60679 } 60680 } 60681 60682 *pidx = idx; 60683 return p; 60684 } 60685 60686 static JSValue js_atomics_op(JSContext *ctx, 60687 JSValueConst this_obj, 60688 int argc, JSValueConst *argv, int op) 60689 { 60690 int size_log2; 60691 uint64_t v, a, rep_val, idx; 60692 void *ptr; 60693 JSValue ret; 60694 JSObject *p; 60695 60696 p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0); 60697 if (!p) 60698 return JS_EXCEPTION; 60699 size_log2 = typed_array_size_log2(p->class_id); 60700 rep_val = 0; 60701 if (op == ATOMICS_OP_LOAD) { 60702 v = 0; 60703 } else { 60704 if (size_log2 == 3) { 60705 int64_t v64; 60706 if (JS_ToBigInt64(ctx, &v64, argv[2])) 60707 return JS_EXCEPTION; 60708 v = v64; 60709 if (op == ATOMICS_OP_COMPARE_EXCHANGE) { 60710 if (JS_ToBigInt64(ctx, &v64, argv[3])) 60711 return JS_EXCEPTION; 60712 rep_val = v64; 60713 } 60714 } else { 60715 uint32_t v32; 60716 if (JS_ToUint32(ctx, &v32, argv[2])) 60717 return JS_EXCEPTION; 60718 v = v32; 60719 if (op == ATOMICS_OP_COMPARE_EXCHANGE) { 60720 if (JS_ToUint32(ctx, &v32, argv[3])) 60721 return JS_EXCEPTION; 60722 rep_val = v32; 60723 } 60724 } 60725 if (typed_array_is_oob(p)) 60726 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60727 if (idx >= p->u.array.count) 60728 return JS_ThrowRangeError(ctx, "out-of-bound access"); 60729 } 60730 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); 60731 60732 switch(op | (size_log2 << 3)) { 60733 60734 #define OP(op_name, func_name) \ 60735 case ATOMICS_OP_ ## op_name | (0 << 3): \ 60736 a = func_name((_Atomic(uint8_t) *)ptr, v); \ 60737 break; \ 60738 case ATOMICS_OP_ ## op_name | (1 << 3): \ 60739 a = func_name((_Atomic(uint16_t) *)ptr, v); \ 60740 break; \ 60741 case ATOMICS_OP_ ## op_name | (2 << 3): \ 60742 a = func_name((_Atomic(uint32_t) *)ptr, v); \ 60743 break; \ 60744 case ATOMICS_OP_ ## op_name | (3 << 3): \ 60745 a = func_name((_Atomic(uint64_t) *)ptr, v); \ 60746 break; 60747 60748 OP(ADD, atomic_fetch_add) 60749 OP(AND, atomic_fetch_and) 60750 OP(OR, atomic_fetch_or) 60751 OP(SUB, atomic_fetch_sub) 60752 OP(XOR, atomic_fetch_xor) 60753 OP(EXCHANGE, atomic_exchange) 60754 #undef OP 60755 60756 case ATOMICS_OP_LOAD | (0 << 3): 60757 a = atomic_load((_Atomic(uint8_t) *)ptr); 60758 break; 60759 case ATOMICS_OP_LOAD | (1 << 3): 60760 a = atomic_load((_Atomic(uint16_t) *)ptr); 60761 break; 60762 case ATOMICS_OP_LOAD | (2 << 3): 60763 a = atomic_load((_Atomic(uint32_t) *)ptr); 60764 break; 60765 case ATOMICS_OP_LOAD | (3 << 3): 60766 a = atomic_load((_Atomic(uint64_t) *)ptr); 60767 break; 60768 60769 case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): 60770 { 60771 uint8_t v1 = v; 60772 atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); 60773 a = v1; 60774 } 60775 break; 60776 case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): 60777 { 60778 uint16_t v1 = v; 60779 atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); 60780 a = v1; 60781 } 60782 break; 60783 case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): 60784 { 60785 uint32_t v1 = v; 60786 atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); 60787 a = v1; 60788 } 60789 break; 60790 case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): 60791 { 60792 uint64_t v1 = v; 60793 atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); 60794 a = v1; 60795 } 60796 break; 60797 default: 60798 abort(); 60799 } 60800 60801 switch(p->class_id) { 60802 case JS_CLASS_INT8_ARRAY: 60803 a = (int8_t)a; 60804 goto done; 60805 case JS_CLASS_UINT8_ARRAY: 60806 a = (uint8_t)a; 60807 goto done; 60808 case JS_CLASS_INT16_ARRAY: 60809 a = (int16_t)a; 60810 goto done; 60811 case JS_CLASS_UINT16_ARRAY: 60812 a = (uint16_t)a; 60813 goto done; 60814 case JS_CLASS_INT32_ARRAY: 60815 done: 60816 ret = JS_NewInt32(ctx, a); 60817 break; 60818 case JS_CLASS_UINT32_ARRAY: 60819 ret = JS_NewUint32(ctx, a); 60820 break; 60821 case JS_CLASS_BIG_INT64_ARRAY: 60822 ret = JS_NewBigInt64(ctx, a); 60823 break; 60824 case JS_CLASS_BIG_UINT64_ARRAY: 60825 ret = JS_NewBigUint64(ctx, a); 60826 break; 60827 default: 60828 abort(); 60829 } 60830 return ret; 60831 } 60832 60833 static JSValue js_atomics_store(JSContext *ctx, 60834 JSValueConst this_obj, 60835 int argc, JSValueConst *argv) 60836 { 60837 int size_log2; 60838 void *ptr; 60839 JSValue ret; 60840 JSObject *p; 60841 uint64_t idx; 60842 int64_t v; 60843 60844 p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 0); 60845 if (!p) 60846 return JS_EXCEPTION; 60847 size_log2 = typed_array_size_log2(p->class_id); 60848 if (size_log2 == 3) { 60849 ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2])); 60850 if (JS_IsException(ret)) 60851 return ret; 60852 if (JS_ToBigInt64(ctx, &v, ret)) { 60853 JS_FreeValue(ctx, ret); 60854 return JS_EXCEPTION; 60855 } 60856 } else { 60857 uint32_t v32; 60858 /* XXX: spec, would be simpler to return the written value */ 60859 ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); 60860 if (JS_IsException(ret)) 60861 return ret; 60862 if (JS_ToUint32(ctx, &v32, ret)) { 60863 JS_FreeValue(ctx, ret); 60864 return JS_EXCEPTION; 60865 } 60866 v = v32; 60867 } 60868 if (typed_array_is_oob(p)) 60869 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 60870 if (idx >= p->u.array.count) 60871 return JS_ThrowRangeError(ctx, "out-of-bound access"); 60872 60873 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); 60874 60875 switch(size_log2) { 60876 case 0: 60877 atomic_store((_Atomic(uint8_t) *)ptr, v); 60878 break; 60879 case 1: 60880 atomic_store((_Atomic(uint16_t) *)ptr, v); 60881 break; 60882 case 2: 60883 atomic_store((_Atomic(uint32_t) *)ptr, v); 60884 break; 60885 case 3: 60886 atomic_store((_Atomic(uint64_t) *)ptr, v); 60887 break; 60888 default: 60889 abort(); 60890 } 60891 return ret; 60892 } 60893 60894 static JSValue js_atomics_isLockFree(JSContext *ctx, 60895 JSValueConst this_obj, 60896 int argc, JSValueConst *argv) 60897 { 60898 int v, ret; 60899 if (JS_ToInt32Sat(ctx, &v, argv[0])) 60900 return JS_EXCEPTION; 60901 ret = (v == 1 || v == 2 || v == 4 || v == 8); 60902 return JS_NewBool(ctx, ret); 60903 } 60904 60905 typedef struct JSAtomicsWaiter { 60906 struct list_head link; 60907 BOOL linked; 60908 pthread_cond_t cond; 60909 int32_t *ptr; 60910 } JSAtomicsWaiter; 60911 60912 static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; 60913 static struct list_head js_atomics_waiter_list = 60914 LIST_HEAD_INIT(js_atomics_waiter_list); 60915 60916 #if defined(__aarch64__) 60917 static inline void cpu_pause(void) 60918 { 60919 asm volatile("yield" ::: "memory"); 60920 } 60921 #elif defined(__x86_64) || defined(__i386__) 60922 static inline void cpu_pause(void) 60923 { 60924 asm volatile("pause" ::: "memory"); 60925 } 60926 #else 60927 static inline void cpu_pause(void) 60928 { 60929 } 60930 #endif 60931 60932 // no-op: Atomics.pause() is not allowed to block or yield to another 60933 // thread, only to hint the CPU that it should back off for a bit; 60934 // the amount of work we do here is a good enough substitute 60935 static JSValue js_atomics_pause(JSContext *ctx, JSValueConst this_obj, 60936 int argc, JSValueConst *argv) 60937 { 60938 double d; 60939 60940 if (argc > 0) { 60941 switch (JS_VALUE_GET_NORM_TAG(argv[0])) { 60942 case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0 60943 d = JS_VALUE_GET_FLOAT64(argv[0]); 60944 if (isfinite(d)) 60945 if (0 == modf(d, &d)) 60946 break; 60947 // fallthru 60948 default: 60949 return JS_ThrowTypeError(ctx, "not an integral number"); 60950 case JS_TAG_UNDEFINED: 60951 case JS_TAG_INT: 60952 break; 60953 } 60954 } 60955 cpu_pause(); 60956 return JS_UNDEFINED; 60957 } 60958 60959 static JSValue js_atomics_wait(JSContext *ctx, 60960 JSValueConst this_obj, 60961 int argc, JSValueConst *argv) 60962 { 60963 JSObject *p; 60964 int64_t v; 60965 int32_t v32; 60966 uint64_t idx; 60967 void *ptr; 60968 int64_t timeout; 60969 struct timespec ts; 60970 JSAtomicsWaiter waiter_s, *waiter; 60971 int ret, size_log2, res; 60972 double d; 60973 60974 p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 2); 60975 if (!p) 60976 return JS_EXCEPTION; 60977 size_log2 = typed_array_size_log2(p->class_id); 60978 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); 60979 60980 /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */ 60981 if (size_log2 == 3) { 60982 if (JS_ToBigInt64(ctx, &v, argv[2])) 60983 return JS_EXCEPTION; 60984 } else { 60985 if (JS_ToInt32(ctx, &v32, argv[2])) 60986 return JS_EXCEPTION; 60987 v = v32; 60988 } 60989 if (JS_ToFloat64(ctx, &d, argv[3])) 60990 return JS_EXCEPTION; 60991 /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ 60992 if (isnan(d) || d >= 0x1p63) 60993 timeout = INT64_MAX; 60994 else if (d < 0) 60995 timeout = 0; 60996 else 60997 timeout = (int64_t)d; 60998 if (!ctx->rt->can_block) 60999 return JS_ThrowTypeError(ctx, "cannot block in this thread"); 61000 61001 /* XXX: inefficient if large number of waiters, should hash on 61002 'ptr' value */ 61003 /* XXX: use Linux futexes when available ? */ 61004 pthread_mutex_lock(&js_atomics_mutex); 61005 if (size_log2 == 3) { 61006 res = *(int64_t *)ptr != v; 61007 } else { 61008 res = *(int32_t *)ptr != v; 61009 } 61010 if (res) { 61011 pthread_mutex_unlock(&js_atomics_mutex); 61012 return JS_AtomToString(ctx, JS_ATOM_not_equal); 61013 } 61014 61015 waiter = &waiter_s; 61016 waiter->ptr = ptr; 61017 pthread_cond_init(&waiter->cond, NULL); 61018 waiter->linked = TRUE; 61019 list_add_tail(&waiter->link, &js_atomics_waiter_list); 61020 61021 if (timeout == INT64_MAX) { 61022 pthread_cond_wait(&waiter->cond, &js_atomics_mutex); 61023 ret = 0; 61024 } else { 61025 /* XXX: use clock monotonic */ 61026 clock_gettime(CLOCK_REALTIME, &ts); 61027 ts.tv_sec += timeout / 1000; 61028 ts.tv_nsec += (timeout % 1000) * 1000000; 61029 if (ts.tv_nsec >= 1000000000) { 61030 ts.tv_nsec -= 1000000000; 61031 ts.tv_sec++; 61032 } 61033 ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, 61034 &ts); 61035 } 61036 if (waiter->linked) 61037 list_del(&waiter->link); 61038 pthread_mutex_unlock(&js_atomics_mutex); 61039 pthread_cond_destroy(&waiter->cond); 61040 if (ret == ETIMEDOUT) { 61041 return JS_AtomToString(ctx, JS_ATOM_timed_out); 61042 } else { 61043 return JS_AtomToString(ctx, JS_ATOM_ok); 61044 } 61045 } 61046 61047 static JSValue js_atomics_notify(JSContext *ctx, 61048 JSValueConst this_obj, 61049 int argc, JSValueConst *argv) 61050 { 61051 struct list_head *el, *el1, waiter_list; 61052 int32_t count, n; 61053 uint64_t idx; 61054 int size_log2; 61055 void *ptr; 61056 JSAtomicsWaiter *waiter; 61057 JSArrayBuffer *abuf; 61058 JSObject *p; 61059 61060 p = js_atomics_get_buf(ctx, argv[0], argv[1], &idx, 1); 61061 if (!p) 61062 return JS_EXCEPTION; 61063 size_log2 = typed_array_size_log2(p->class_id); 61064 61065 if (JS_IsUndefined(argv[2])) { 61066 count = INT32_MAX; 61067 } else { 61068 if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) 61069 return JS_EXCEPTION; 61070 } 61071 61072 n = 0; 61073 abuf = p->u.typed_array->buffer->u.array_buffer; 61074 if (abuf->shared && count > 0) { 61075 /* 'argv[0]' is a SharedArrayBuffer so it cannot be detached nor reduced */ 61076 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); 61077 pthread_mutex_lock(&js_atomics_mutex); 61078 init_list_head(&waiter_list); 61079 list_for_each_safe(el, el1, &js_atomics_waiter_list) { 61080 waiter = list_entry(el, JSAtomicsWaiter, link); 61081 if (waiter->ptr == ptr) { 61082 list_del(&waiter->link); 61083 waiter->linked = FALSE; 61084 list_add_tail(&waiter->link, &waiter_list); 61085 n++; 61086 if (n >= count) 61087 break; 61088 } 61089 } 61090 list_for_each(el, &waiter_list) { 61091 waiter = list_entry(el, JSAtomicsWaiter, link); 61092 pthread_cond_signal(&waiter->cond); 61093 } 61094 pthread_mutex_unlock(&js_atomics_mutex); 61095 } 61096 return JS_NewInt32(ctx, n); 61097 } 61098 61099 static const JSCFunctionListEntry js_atomics_funcs[] = { 61100 JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), 61101 JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), 61102 JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), 61103 JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), 61104 JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), 61105 JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), 61106 JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), 61107 JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), 61108 JS_CFUNC_DEF("store", 3, js_atomics_store ), 61109 JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), 61110 JS_CFUNC_DEF("pause", 0, js_atomics_pause ), 61111 JS_CFUNC_DEF("wait", 4, js_atomics_wait ), 61112 JS_CFUNC_DEF("notify", 3, js_atomics_notify ), 61113 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), 61114 }; 61115 61116 static const JSCFunctionListEntry js_atomics_obj[] = { 61117 JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 61118 }; 61119 61120 static int JS_AddIntrinsicAtomics(JSContext *ctx) 61121 { 61122 /* add Atomics as autoinit object */ 61123 return JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); 61124 } 61125 61126 #endif /* CONFIG_ATOMICS */ 61127 61128 int JS_AddIntrinsicTypedArrays(JSContext *ctx) 61129 { 61130 JSValue typed_array_base_func, typed_array_base_proto, obj; 61131 int i, ret; 61132 61133 obj = JS_NewCConstructor(ctx, JS_CLASS_ARRAY_BUFFER, "ArrayBuffer", 61134 js_array_buffer_constructor, 1, JS_CFUNC_constructor, 0, 61135 JS_UNDEFINED, 61136 js_array_buffer_funcs, countof(js_array_buffer_funcs), 61137 js_array_buffer_proto_funcs, countof(js_array_buffer_proto_funcs), 61138 0); 61139 if (JS_IsException(obj)) 61140 return -1; 61141 JS_FreeValue(ctx, obj); 61142 61143 obj = JS_NewCConstructor(ctx, JS_CLASS_SHARED_ARRAY_BUFFER, "SharedArrayBuffer", 61144 js_shared_array_buffer_constructor, 1, JS_CFUNC_constructor, 0, 61145 JS_UNDEFINED, 61146 js_shared_array_buffer_funcs, countof(js_shared_array_buffer_funcs), 61147 js_shared_array_buffer_proto_funcs, countof(js_shared_array_buffer_proto_funcs), 61148 0); 61149 if (JS_IsException(obj)) 61150 return -1; 61151 JS_FreeValue(ctx, obj); 61152 61153 61154 typed_array_base_func = 61155 JS_NewCConstructor(ctx, -1, "TypedArray", 61156 js_typed_array_base_constructor, 0, JS_CFUNC_constructor_or_func, 0, 61157 JS_UNDEFINED, 61158 js_typed_array_base_funcs, countof(js_typed_array_base_funcs), 61159 js_typed_array_base_proto_funcs, countof(js_typed_array_base_proto_funcs), 61160 JS_NEW_CTOR_NO_GLOBAL); 61161 if (JS_IsException(typed_array_base_func)) 61162 return -1; 61163 61164 /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ 61165 obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); 61166 if (JS_IsException(obj)) 61167 goto fail; 61168 /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ 61169 typed_array_base_proto = JS_GetProperty(ctx, typed_array_base_func, JS_ATOM_prototype); 61170 if (JS_IsException(typed_array_base_proto)) 61171 goto fail; 61172 ret = JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, 61173 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 61174 JS_FreeValue(ctx, typed_array_base_proto); 61175 if (ret < 0) 61176 goto fail; 61177 61178 /* Used to squelch a -Wcast-function-type warning. */ 61179 JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; 61180 for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { 61181 char buf[ATOM_GET_STR_BUF_SIZE]; 61182 const char *name; 61183 61184 name = JS_AtomGetStr(ctx, buf, sizeof(buf), 61185 JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); 61186 if (i == JS_CLASS_UINT8_ARRAY) { 61187 obj = JS_NewCConstructor(ctx, i, name, 61188 ft.generic, 3, JS_CFUNC_constructor_magic, i, 61189 typed_array_base_func, 61190 js_uint8array_funcs, countof(js_uint8array_funcs), 61191 js_uint8array_proto_funcs, countof(js_uint8array_proto_funcs), 61192 0); 61193 } else { 61194 const JSCFunctionListEntry *bpe = js_typed_array_funcs + typed_array_size_log2(i); 61195 obj = JS_NewCConstructor(ctx, i, name, 61196 ft.generic, 3, JS_CFUNC_constructor_magic, i, 61197 typed_array_base_func, 61198 bpe, 1, 61199 bpe, 1, 61200 0); 61201 } 61202 if (JS_IsException(obj)) { 61203 fail: 61204 JS_FreeValue(ctx, typed_array_base_func); 61205 return -1; 61206 } 61207 JS_FreeValue(ctx, obj); 61208 } 61209 JS_FreeValue(ctx, typed_array_base_func); 61210 61211 /* DataView */ 61212 obj = JS_NewCConstructor(ctx, JS_CLASS_DATAVIEW, "DataView", 61213 js_dataview_constructor, 1, JS_CFUNC_constructor, 0, 61214 JS_UNDEFINED, 61215 NULL, 0, 61216 js_dataview_proto_funcs, countof(js_dataview_proto_funcs), 61217 0); 61218 if (JS_IsException(obj)) 61219 return -1; 61220 JS_FreeValue(ctx, obj); 61221 61222 /* Atomics */ 61223 #ifdef CONFIG_ATOMICS 61224 if (JS_AddIntrinsicAtomics(ctx)) 61225 return -1; 61226 #endif 61227 return 0; 61228 } 61229 61230 /* WeakRef */ 61231 61232 typedef struct JSWeakRefData { 61233 JSWeakRefHeader weakref_header; 61234 JSValue target; 61235 } JSWeakRefData; 61236 61237 static void js_weakref_finalizer(JSRuntime *rt, JSValue val) 61238 { 61239 JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF); 61240 if (!wrd) 61241 return; 61242 js_weakref_free(rt, wrd->target); 61243 list_del(&wrd->weakref_header.link); 61244 js_free_rt(rt, wrd); 61245 } 61246 61247 static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh) 61248 { 61249 JSWeakRefData *wrd = container_of(wh, JSWeakRefData, weakref_header); 61250 61251 if (!js_weakref_is_live(wrd->target)) { 61252 js_weakref_free(rt, wrd->target); 61253 wrd->target = JS_UNDEFINED; 61254 } 61255 } 61256 61257 static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target, 61258 int argc, JSValueConst *argv) 61259 { 61260 JSValueConst arg; 61261 JSValue obj; 61262 61263 if (JS_IsUndefined(new_target)) 61264 return JS_ThrowTypeError(ctx, "constructor requires 'new'"); 61265 arg = argv[0]; 61266 if (!js_weakref_is_target(arg)) 61267 return JS_ThrowTypeError(ctx, "invalid target"); 61268 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF); 61269 if (JS_IsException(obj)) 61270 return JS_EXCEPTION; 61271 JSWeakRefData *wrd = js_mallocz(ctx, sizeof(*wrd)); 61272 if (!wrd) { 61273 JS_FreeValue(ctx, obj); 61274 return JS_EXCEPTION; 61275 } 61276 wrd->target = js_weakref_new(ctx, arg); 61277 wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF; 61278 list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list); 61279 JS_SetOpaque(obj, wrd); 61280 return obj; 61281 } 61282 61283 static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) 61284 { 61285 JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF); 61286 if (!wrd) 61287 return JS_EXCEPTION; 61288 if (js_weakref_is_live(wrd->target)) 61289 return JS_DupValue(ctx, wrd->target); 61290 else 61291 return JS_UNDEFINED; 61292 } 61293 61294 static const JSCFunctionListEntry js_weakref_proto_funcs[] = { 61295 JS_CFUNC_DEF("deref", 0, js_weakref_deref ), 61296 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ), 61297 }; 61298 61299 static const JSClassShortDef js_weakref_class_def[] = { 61300 { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */ 61301 }; 61302 61303 typedef struct JSFinRecEntry { 61304 struct list_head link; 61305 JSValue target; 61306 JSValue held_val; 61307 JSValue token; 61308 } JSFinRecEntry; 61309 61310 typedef struct JSFinalizationRegistryData { 61311 JSWeakRefHeader weakref_header; 61312 struct list_head entries; /* list of JSFinRecEntry.link */ 61313 JSContext *realm; 61314 JSValue cb; 61315 } JSFinalizationRegistryData; 61316 61317 static void js_finrec_finalizer(JSRuntime *rt, JSValue val) 61318 { 61319 JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); 61320 if (frd) { 61321 struct list_head *el, *el1; 61322 list_for_each_safe(el, el1, &frd->entries) { 61323 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); 61324 js_weakref_free(rt, fre->target); 61325 js_weakref_free(rt, fre->token); 61326 JS_FreeValueRT(rt, fre->held_val); 61327 js_free_rt(rt, fre); 61328 } 61329 JS_FreeValueRT(rt, frd->cb); 61330 JS_FreeContext(frd->realm); 61331 list_del(&frd->weakref_header.link); 61332 js_free_rt(rt, frd); 61333 } 61334 } 61335 61336 static void js_finrec_mark(JSRuntime *rt, JSValueConst val, 61337 JS_MarkFunc *mark_func) 61338 { 61339 JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY); 61340 struct list_head *el; 61341 if (frd) { 61342 list_for_each(el, &frd->entries) { 61343 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); 61344 JS_MarkValue(rt, fre->held_val, mark_func); 61345 } 61346 JS_MarkValue(rt, frd->cb, mark_func); 61347 mark_func(rt, &frd->realm->header); 61348 } 61349 } 61350 61351 static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv) 61352 { 61353 return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]); 61354 } 61355 61356 static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh) 61357 { 61358 JSFinalizationRegistryData *frd = container_of(wh, JSFinalizationRegistryData, weakref_header); 61359 struct list_head *el, *el1; 61360 61361 list_for_each_safe(el, el1, &frd->entries) { 61362 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); 61363 61364 if (!js_weakref_is_live(fre->token)) { 61365 js_weakref_free(rt, fre->token); 61366 fre->token = JS_UNDEFINED; 61367 } 61368 61369 if (!js_weakref_is_live(fre->target)) { 61370 JSValueConst args[2]; 61371 args[0] = frd->cb; 61372 args[1] = fre->held_val; 61373 /* no exception is raised to avoid recursing into the GC */ 61374 JS_EnqueueJob2(frd->realm, js_finrec_job, 2, args, TRUE); 61375 61376 js_weakref_free(rt, fre->target); 61377 js_weakref_free(rt, fre->token); 61378 JS_FreeValueRT(rt, fre->held_val); 61379 list_del(&fre->link); 61380 js_free_rt(rt, fre); 61381 } 61382 } 61383 } 61384 61385 static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target, 61386 int argc, JSValueConst *argv) 61387 { 61388 JSValueConst cb; 61389 JSValue obj; 61390 JSFinalizationRegistryData *frd; 61391 61392 if (JS_IsUndefined(new_target)) 61393 return JS_ThrowTypeError(ctx, "constructor requires 'new'"); 61394 cb = argv[0]; 61395 if (!JS_IsFunction(ctx, cb)) 61396 return JS_ThrowTypeError(ctx, "argument must be a function"); 61397 61398 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY); 61399 if (JS_IsException(obj)) 61400 return JS_EXCEPTION; 61401 frd = js_mallocz(ctx, sizeof(*frd)); 61402 if (!frd) { 61403 JS_FreeValue(ctx, obj); 61404 return JS_EXCEPTION; 61405 } 61406 frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC; 61407 list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list); 61408 init_list_head(&frd->entries); 61409 frd->realm = JS_DupContext(ctx); 61410 frd->cb = JS_DupValue(ctx, cb); 61411 JS_SetOpaque(obj, frd); 61412 return obj; 61413 } 61414 61415 static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val, 61416 int argc, JSValueConst *argv) 61417 { 61418 JSValueConst target, held_val, token; 61419 JSFinalizationRegistryData *frd; 61420 JSFinRecEntry *fre; 61421 61422 frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); 61423 if (!frd) 61424 return JS_EXCEPTION; 61425 target = argv[0]; 61426 held_val = argv[1]; 61427 token = argc > 2 ? argv[2] : JS_UNDEFINED; 61428 61429 if (!js_weakref_is_target(target)) 61430 return JS_ThrowTypeError(ctx, "invalid target"); 61431 if (js_same_value(ctx, target, held_val)) 61432 return JS_ThrowTypeError(ctx, "held value cannot be the target"); 61433 if (!JS_IsUndefined(token) && !js_weakref_is_target(token)) 61434 return JS_ThrowTypeError(ctx, "invalid unregister token"); 61435 fre = js_malloc(ctx, sizeof(*fre)); 61436 if (!fre) 61437 return JS_EXCEPTION; 61438 fre->target = js_weakref_new(ctx, target); 61439 fre->held_val = JS_DupValue(ctx, held_val); 61440 fre->token = js_weakref_new(ctx, token); 61441 list_add_tail(&fre->link, &frd->entries); 61442 return JS_UNDEFINED; 61443 } 61444 61445 static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) 61446 { 61447 JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY); 61448 JSValueConst token; 61449 BOOL removed; 61450 struct list_head *el, *el1; 61451 61452 if (!frd) 61453 return JS_EXCEPTION; 61454 token = argv[0]; 61455 if (!js_weakref_is_target(token)) 61456 return JS_ThrowTypeError(ctx, "invalid unregister token"); 61457 61458 removed = FALSE; 61459 list_for_each_safe(el, el1, &frd->entries) { 61460 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link); 61461 if (js_weakref_is_live(fre->token) && js_same_value(ctx, fre->token, token)) { 61462 js_weakref_free(ctx->rt, fre->target); 61463 js_weakref_free(ctx->rt, fre->token); 61464 JS_FreeValue(ctx, fre->held_val); 61465 list_del(&fre->link); 61466 js_free(ctx, fre); 61467 removed = TRUE; 61468 } 61469 } 61470 return JS_NewBool(ctx, removed); 61471 } 61472 61473 static const JSCFunctionListEntry js_finrec_proto_funcs[] = { 61474 JS_CFUNC_DEF("register", 2, js_finrec_register ), 61475 JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ), 61476 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ), 61477 }; 61478 61479 static const JSClassShortDef js_finrec_class_def[] = { 61480 { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */ 61481 }; 61482 61483 int JS_AddIntrinsicWeakRef(JSContext *ctx) 61484 { 61485 JSRuntime *rt = ctx->rt; 61486 JSValue obj; 61487 61488 /* WeakRef */ 61489 if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) { 61490 if (init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF, 61491 countof(js_weakref_class_def))) 61492 return -1; 61493 } 61494 obj = JS_NewCConstructor(ctx, JS_CLASS_WEAK_REF, "WeakRef", 61495 js_weakref_constructor, 1, JS_CFUNC_constructor_or_func, 0, 61496 JS_UNDEFINED, 61497 NULL, 0, 61498 js_weakref_proto_funcs, countof(js_weakref_proto_funcs), 61499 0); 61500 if (JS_IsException(obj)) 61501 return -1; 61502 JS_FreeValue(ctx, obj); 61503 61504 /* FinalizationRegistry */ 61505 if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) { 61506 if (init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY, 61507 countof(js_finrec_class_def))) 61508 return -1; 61509 } 61510 61511 obj = JS_NewCConstructor(ctx, JS_CLASS_FINALIZATION_REGISTRY, "FinalizationRegistry", 61512 js_finrec_constructor, 1, JS_CFUNC_constructor_or_func, 0, 61513 JS_UNDEFINED, 61514 NULL, 0, 61515 js_finrec_proto_funcs, countof(js_finrec_proto_funcs), 61516 0); 61517 if (JS_IsException(obj)) 61518 return -1; 61519 JS_FreeValue(ctx, obj); 61520 return 0; 61521 }