quickjs.c (1831380B)
1 /* 2 * QuickJS Javascript Engine 3 * 4 * Copyright (c) 2017-2021 Fabrice Bellard 5 * Copyright (c) 2017-2021 Charlie Gordon 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 * THE SOFTWARE. 24 */ 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <stdarg.h> 28 #include <inttypes.h> 29 #include <string.h> 30 #include <assert.h> 31 #include <sys/time.h> 32 #include <time.h> 33 #include <fenv.h> 34 #include <math.h> 35 #if defined(__APPLE__) 36 #include <malloc/malloc.h> 37 #elif defined(__linux__) || 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 "libbf.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) && !defined(__APPLE__) 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_PROMISE 107 //#define DUMP_READ_OBJECT 108 109 /* test the GC by forcing it before each object allocation */ 110 //#define FORCE_GC_AT_MALLOC 111 112 /* use function call trampolines for better output in profiling tools */ 113 //#define PERF_TRAMPOLINE 114 115 #ifdef CONFIG_ATOMICS 116 #include <pthread.h> 117 #include <stdatomic.h> 118 #include <errno.h> 119 #endif 120 121 enum { 122 /* classid tag */ /* union usage | properties */ 123 JS_CLASS_OBJECT = 1, /* must be first */ 124 JS_CLASS_ARRAY, /* u.array | length */ 125 JS_CLASS_ERROR, 126 JS_CLASS_NUMBER, /* u.object_data */ 127 JS_CLASS_STRING, /* u.object_data */ 128 JS_CLASS_BOOLEAN, /* u.object_data */ 129 JS_CLASS_SYMBOL, /* u.object_data */ 130 JS_CLASS_ARGUMENTS, /* u.array | length */ 131 JS_CLASS_MAPPED_ARGUMENTS, /* | length */ 132 JS_CLASS_DATE, /* u.object_data */ 133 JS_CLASS_MODULE_NS, 134 JS_CLASS_C_FUNCTION, /* u.cfunc */ 135 JS_CLASS_BYTECODE_FUNCTION, /* u.func */ 136 JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ 137 JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ 138 JS_CLASS_GENERATOR_FUNCTION, /* u.func */ 139 JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ 140 JS_CLASS_REGEXP, /* u.regexp */ 141 JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ 142 JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ 143 JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ 144 JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ 145 JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ 146 JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ 147 JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ 148 JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ 149 JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ 150 JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ 151 JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ 152 JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ 153 JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ 154 JS_CLASS_DATAVIEW, /* u.typed_array */ 155 JS_CLASS_BIG_INT, /* u.object_data */ 156 #ifdef CONFIG_BIGNUM 157 JS_CLASS_BIG_FLOAT, /* u.object_data */ 158 JS_CLASS_FLOAT_ENV, /* u.float_env */ 159 JS_CLASS_BIG_DECIMAL, /* u.object_data */ 160 JS_CLASS_OPERATOR_SET, /* u.operator_set */ 161 #endif 162 JS_CLASS_MAP, /* u.map_state */ 163 JS_CLASS_SET, /* u.map_state */ 164 JS_CLASS_WEAKMAP, /* u.map_state */ 165 JS_CLASS_WEAKSET, /* u.map_state */ 166 JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ 167 JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ 168 JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ 169 JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ 170 JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ 171 JS_CLASS_GENERATOR, /* u.generator_data */ 172 JS_CLASS_PROXY, /* u.proxy_data */ 173 JS_CLASS_PROMISE, /* u.promise_data */ 174 JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ 175 JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ 176 JS_CLASS_ASYNC_FUNCTION, /* u.func */ 177 JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ 178 JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ 179 JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ 180 JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ 181 JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ 182 183 JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ 184 }; 185 186 /* number of typed array types */ 187 #define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) 188 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; 189 #define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) 190 191 typedef enum JSErrorEnum { 192 JS_EVAL_ERROR, 193 JS_RANGE_ERROR, 194 JS_REFERENCE_ERROR, 195 JS_SYNTAX_ERROR, 196 JS_TYPE_ERROR, 197 JS_URI_ERROR, 198 JS_INTERNAL_ERROR, 199 JS_AGGREGATE_ERROR, 200 201 JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ 202 } JSErrorEnum; 203 204 #define JS_MAX_LOCAL_VARS 65535 205 #define JS_STACK_SIZE_MAX 65534 206 #define JS_STRING_LEN_MAX ((1 << 30) - 1) 207 208 #define __exception __attribute__((warn_unused_result)) 209 210 typedef struct JSShape JSShape; 211 typedef struct JSString JSString; 212 typedef struct JSString JSAtomStruct; 213 214 typedef enum { 215 JS_GC_PHASE_NONE, 216 JS_GC_PHASE_DECREF, 217 JS_GC_PHASE_REMOVE_CYCLES, 218 } JSGCPhaseEnum; 219 220 typedef enum OPCodeEnum OPCodeEnum; 221 222 /* function pointers are used for numeric operations so that it is 223 possible to remove some numeric types */ 224 typedef struct { 225 JSValue (*to_string)(JSContext *ctx, JSValueConst val); 226 JSValue (*from_string)(JSContext *ctx, const char *buf, 227 int radix, int flags, slimb_t *pexponent); 228 int (*unary_arith)(JSContext *ctx, 229 JSValue *pres, OPCodeEnum op, JSValue op1); 230 int (*binary_arith)(JSContext *ctx, OPCodeEnum op, 231 JSValue *pres, JSValue op1, JSValue op2); 232 int (*compare)(JSContext *ctx, OPCodeEnum op, 233 JSValue op1, JSValue op2); 234 /* only for bigfloat: */ 235 JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a, 236 int64_t exponent); 237 int (*mul_pow10)(JSContext *ctx, JSValue *sp); 238 } JSNumericOperations; 239 240 struct JSRuntime { 241 JSMallocFunctions mf; 242 JSMallocState malloc_state; 243 const char *rt_info; 244 245 int atom_hash_size; /* power of two */ 246 int atom_count; 247 int atom_size; 248 int atom_count_resize; /* resize hash table at this count */ 249 uint32_t *atom_hash; 250 JSAtomStruct **atom_array; 251 int atom_free_index; /* 0 = none */ 252 253 int class_count; /* size of class_array */ 254 JSClass *class_array; 255 256 struct list_head context_list; /* list of JSContext.link */ 257 /* list of JSGCObjectHeader.link. List of allocated GC objects (used 258 by the garbage collector) */ 259 struct list_head gc_obj_list; 260 /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ 261 struct list_head gc_zero_ref_count_list; 262 struct list_head tmp_obj_list; /* used during GC */ 263 JSGCPhaseEnum gc_phase : 8; 264 size_t malloc_gc_threshold; 265 #ifdef DUMP_LEAKS 266 struct list_head string_list; /* list of JSString.link */ 267 #endif 268 /* stack limitation */ 269 uintptr_t stack_size; /* in bytes, 0 if no limit */ 270 uintptr_t stack_top; 271 uintptr_t stack_limit; /* lower stack limit */ 272 273 JSValue current_exception; 274 /* true if inside an out of memory error, to avoid recursing */ 275 BOOL in_out_of_memory : 8; 276 277 struct JSStackFrame *current_stack_frame; 278 279 JSInterruptHandler *interrupt_handler; 280 void *interrupt_opaque; 281 282 JSHostPromiseRejectionTracker *host_promise_rejection_tracker; 283 void *host_promise_rejection_tracker_opaque; 284 285 struct list_head job_list; /* list of JSJobEntry.link */ 286 287 JSModuleNormalizeFunc *module_normalize_func; 288 JSModuleLoaderFunc *module_loader_func; 289 void *module_loader_opaque; 290 /* timestamp for internal use in module evaluation */ 291 int64_t module_async_evaluation_next_timestamp; 292 293 BOOL can_block : 8; /* TRUE if Atomics.wait can block */ 294 /* used to allocate, free and clone SharedArrayBuffers */ 295 JSSharedArrayBufferFunctions sab_funcs; 296 297 /* Shape hash table */ 298 int shape_hash_bits; 299 int shape_hash_size; 300 int shape_hash_count; /* number of hashed shapes */ 301 JSShape **shape_hash; 302 bf_context_t bf_ctx; 303 JSNumericOperations bigint_ops; 304 #ifdef CONFIG_BIGNUM 305 JSNumericOperations bigfloat_ops; 306 JSNumericOperations bigdecimal_ops; 307 uint32_t operator_count; 308 #endif 309 void *user_opaque; 310 }; 311 312 struct JSClass { 313 uint32_t class_id; /* 0 means free entry */ 314 JSAtom class_name; 315 JSClassFinalizer *finalizer; 316 JSClassGCMark *gc_mark; 317 JSClassCall *call; 318 /* pointers for exotic behavior, can be NULL if none are present */ 319 const JSClassExoticMethods *exotic; 320 }; 321 322 #define JS_MODE_STRICT (1 << 0) 323 #define JS_MODE_STRIP (1 << 1) 324 #define JS_MODE_MATH (1 << 2) 325 #define JS_MODE_ASYNC (1 << 3) /* async function */ 326 327 typedef struct JSStackFrame { 328 struct JSStackFrame *prev_frame; /* NULL if first stack frame */ 329 JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ 330 JSValue *arg_buf; /* arguments */ 331 JSValue *var_buf; /* variables */ 332 struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */ 333 const uint8_t *cur_pc; /* only used in bytecode functions : PC of the 334 instruction after the call */ 335 int arg_count; 336 int js_mode; /* for C functions, only JS_MODE_MATH may be set */ 337 /* only used in generators. Current stack pointer value. NULL if 338 the function is running. */ 339 JSValue *cur_sp; 340 } JSStackFrame; 341 342 typedef enum { 343 JS_GC_OBJ_TYPE_JS_OBJECT, 344 JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, 345 JS_GC_OBJ_TYPE_SHAPE, 346 JS_GC_OBJ_TYPE_VAR_REF, 347 JS_GC_OBJ_TYPE_ASYNC_FUNCTION, 348 JS_GC_OBJ_TYPE_JS_CONTEXT, 349 } JSGCObjectTypeEnum; 350 351 /* header for GC objects. GC objects are C data structures with a 352 reference count that can reference other GC objects. JS Objects are 353 a particular type of GC object. */ 354 struct JSGCObjectHeader { 355 int ref_count; /* must come first, 32-bit */ 356 JSGCObjectTypeEnum gc_obj_type : 4; 357 uint8_t mark : 4; /* used by the GC */ 358 uint8_t dummy1; /* not used by the GC */ 359 uint16_t dummy2; /* not used by the GC */ 360 struct list_head link; 361 }; 362 363 typedef struct JSVarRef { 364 union { 365 JSGCObjectHeader header; /* must come first */ 366 struct { 367 int __gc_ref_count; /* corresponds to header.ref_count */ 368 uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ 369 uint8_t is_detached : 1; 370 uint8_t is_arg : 1; 371 uint16_t var_idx; /* index of the corresponding function variable on 372 the stack */ 373 }; 374 }; 375 JSValue *pvalue; /* pointer to the value, either on the stack or 376 to 'value' */ 377 union { 378 JSValue value; /* used when is_detached = TRUE */ 379 struct { 380 struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */ 381 struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */ 382 }; /* used when is_detached = FALSE */ 383 }; 384 } JSVarRef; 385 386 /* the same structure is used for big integers and big floats. Big 387 integers are never infinite or NaNs */ 388 typedef struct JSBigFloat { 389 JSRefCountHeader header; /* must come first, 32-bit */ 390 bf_t num; 391 } JSBigFloat; 392 393 #ifdef CONFIG_BIGNUM 394 typedef struct JSFloatEnv { 395 limb_t prec; 396 bf_flags_t flags; 397 unsigned int status; 398 } JSFloatEnv; 399 400 typedef struct JSBigDecimal { 401 JSRefCountHeader header; /* must come first, 32-bit */ 402 bfdec_t num; 403 } JSBigDecimal; 404 #endif 405 406 typedef enum { 407 JS_AUTOINIT_ID_PROTOTYPE, 408 JS_AUTOINIT_ID_MODULE_NS, 409 JS_AUTOINIT_ID_PROP, 410 } JSAutoInitIDEnum; 411 412 /* must be large enough to have a negligible runtime cost and small 413 enough to call the interrupt callback often. */ 414 #define JS_INTERRUPT_COUNTER_INIT 10000 415 416 struct JSContext { 417 JSGCObjectHeader header; /* must come first */ 418 JSRuntime *rt; 419 struct list_head link; 420 421 uint16_t binary_object_count; 422 int binary_object_size; 423 424 JSShape *array_shape; /* initial shape for Array objects */ 425 426 JSValue *class_proto; 427 JSValue function_proto; 428 JSValue function_ctor; 429 JSValue array_ctor; 430 JSValue regexp_ctor; 431 JSValue promise_ctor; 432 JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; 433 JSValue iterator_proto; 434 JSValue async_iterator_proto; 435 JSValue array_proto_values; 436 JSValue throw_type_error; 437 JSValue eval_obj; 438 439 JSValue global_obj; /* global object */ 440 JSValue global_var_obj; /* contains the global let/const definitions */ 441 442 uint64_t random_state; 443 bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ 444 #ifdef CONFIG_BIGNUM 445 JSFloatEnv fp_env; /* global FP environment */ 446 BOOL bignum_ext : 8; /* enable math mode */ 447 BOOL allow_operator_overloading : 8; 448 #endif 449 /* when the counter reaches zero, JSRutime.interrupt_handler is called */ 450 int interrupt_counter; 451 452 struct list_head loaded_modules; /* list of JSModuleDef.link */ 453 454 /* if NULL, RegExp compilation is not supported */ 455 JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern, 456 JSValueConst flags); 457 /* if NULL, eval is not supported */ 458 JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, 459 const char *input, size_t input_len, 460 const char *filename, int flags, int scope_idx); 461 void *user_opaque; 462 }; 463 464 typedef union JSFloat64Union { 465 double d; 466 uint64_t u64; 467 uint32_t u32[2]; 468 } JSFloat64Union; 469 470 enum { 471 JS_ATOM_TYPE_STRING = 1, 472 JS_ATOM_TYPE_GLOBAL_SYMBOL, 473 JS_ATOM_TYPE_SYMBOL, 474 JS_ATOM_TYPE_PRIVATE, 475 }; 476 477 enum { 478 JS_ATOM_HASH_SYMBOL, 479 JS_ATOM_HASH_PRIVATE, 480 }; 481 482 typedef enum { 483 JS_ATOM_KIND_STRING, 484 JS_ATOM_KIND_SYMBOL, 485 JS_ATOM_KIND_PRIVATE, 486 } JSAtomKindEnum; 487 488 #define JS_ATOM_HASH_MASK ((1 << 30) - 1) 489 490 struct JSString { 491 JSRefCountHeader header; /* must come first, 32-bit */ 492 uint32_t len : 31; 493 uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ 494 /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, 495 for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 496 XXX: could change encoding to have one more bit in hash */ 497 uint32_t hash : 30; 498 uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ 499 uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ 500 #ifdef DUMP_LEAKS 501 struct list_head link; /* string list */ 502 #endif 503 union { 504 uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ 505 uint16_t str16[0]; 506 } u; 507 }; 508 509 typedef struct JSClosureVar { 510 uint8_t is_local : 1; 511 uint8_t is_arg : 1; 512 uint8_t is_const : 1; 513 uint8_t is_lexical : 1; 514 uint8_t var_kind : 4; /* see JSVarKindEnum */ 515 /* 8 bits available */ 516 uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the 517 parent function. otherwise: index to a closure 518 variable of the parent function */ 519 JSAtom var_name; 520 } JSClosureVar; 521 522 #define ARG_SCOPE_INDEX 1 523 #define ARG_SCOPE_END (-2) 524 525 typedef struct JSVarScope { 526 int parent; /* index into fd->scopes of the enclosing scope */ 527 int first; /* index into fd->vars of the last variable in this scope */ 528 } JSVarScope; 529 530 typedef enum { 531 /* XXX: add more variable kinds here instead of using bit fields */ 532 JS_VAR_NORMAL, 533 JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ 534 JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator 535 function declaration */ 536 JS_VAR_CATCH, 537 JS_VAR_FUNCTION_NAME, /* function expression name */ 538 JS_VAR_PRIVATE_FIELD, 539 JS_VAR_PRIVATE_METHOD, 540 JS_VAR_PRIVATE_GETTER, 541 JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ 542 JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ 543 } JSVarKindEnum; 544 545 /* XXX: could use a different structure in bytecode functions to save 546 memory */ 547 typedef struct JSVarDef { 548 JSAtom var_name; 549 /* index into fd->scopes of this variable lexical scope */ 550 int scope_level; 551 /* during compilation: 552 - if scope_level = 0: scope in which the variable is defined 553 - if scope_level != 0: index into fd->vars of the next 554 variable in the same or enclosing lexical scope 555 in a bytecode function: 556 index into fd->vars of the next 557 variable in the same or enclosing lexical scope 558 */ 559 int scope_next; 560 uint8_t is_const : 1; 561 uint8_t is_lexical : 1; 562 uint8_t is_captured : 1; 563 uint8_t is_static_private : 1; /* only used during private class field parsing */ 564 uint8_t var_kind : 4; /* see JSVarKindEnum */ 565 /* only used during compilation: function pool index for lexical 566 variables with var_kind = 567 JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of 568 the definition of the 'var' variables (they have scope_level = 569 0) */ 570 int func_pool_idx : 24; /* only used during compilation : index in 571 the constant pool for hoisted function 572 definition */ 573 } JSVarDef; 574 575 /* for the encoding of the pc2line table */ 576 #define PC2LINE_BASE (-1) 577 #define PC2LINE_RANGE 5 578 #define PC2LINE_OP_FIRST 1 579 #define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) 580 581 typedef enum JSFunctionKindEnum { 582 JS_FUNC_NORMAL = 0, 583 JS_FUNC_GENERATOR = (1 << 0), 584 JS_FUNC_ASYNC = (1 << 1), 585 JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), 586 } JSFunctionKindEnum; 587 588 typedef struct JSFunctionBytecode { 589 JSGCObjectHeader header; /* must come first */ 590 uint8_t js_mode; 591 uint8_t has_prototype : 1; /* true if a prototype field is necessary */ 592 uint8_t has_simple_parameter_list : 1; 593 uint8_t is_derived_class_constructor : 1; 594 /* true if home_object needs to be initialized */ 595 uint8_t need_home_object : 1; 596 uint8_t func_kind : 2; 597 uint8_t new_target_allowed : 1; 598 uint8_t super_call_allowed : 1; 599 uint8_t super_allowed : 1; 600 uint8_t arguments_allowed : 1; 601 uint8_t has_debug : 1; 602 uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ 603 uint8_t read_only_bytecode : 1; 604 uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */ 605 /* XXX: 10 bits available */ 606 uint8_t *byte_code_buf; /* (self pointer) */ 607 int byte_code_len; 608 JSAtom func_name; 609 JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ 610 JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ 611 uint16_t arg_count; 612 uint16_t var_count; 613 uint16_t defined_arg_count; /* for length function property */ 614 uint16_t stack_size; /* maximum stack size */ 615 JSContext *realm; /* function realm */ 616 JSValue *cpool; /* constant pool (self pointer) */ 617 int cpool_count; 618 int closure_var_count; 619 void *perf_trampoline; 620 struct { 621 /* debug info, move to separate structure to save memory? */ 622 JSAtom filename; 623 int line_num; 624 int source_len; 625 int pc2line_len; 626 uint8_t *pc2line_buf; 627 char *source; 628 } debug; 629 } JSFunctionBytecode; 630 631 typedef struct JSBoundFunction { 632 JSValue func_obj; 633 JSValue this_val; 634 int argc; 635 JSValue argv[0]; 636 } JSBoundFunction; 637 638 typedef enum JSIteratorKindEnum { 639 JS_ITERATOR_KIND_KEY, 640 JS_ITERATOR_KIND_VALUE, 641 JS_ITERATOR_KIND_KEY_AND_VALUE, 642 } JSIteratorKindEnum; 643 644 typedef struct JSForInIterator { 645 JSValue obj; 646 uint32_t idx; 647 uint32_t atom_count; 648 uint8_t in_prototype_chain; 649 uint8_t is_array; 650 JSPropertyEnum *tab_atom; /* is_array = FALSE */ 651 } JSForInIterator; 652 653 typedef struct JSRegExp { 654 JSString *pattern; 655 JSString *bytecode; /* also contains the flags */ 656 } JSRegExp; 657 658 typedef struct JSProxyData { 659 JSValue target; 660 JSValue handler; 661 uint8_t is_func; 662 uint8_t is_revoked; 663 } JSProxyData; 664 665 typedef struct JSArrayBuffer { 666 int byte_length; /* 0 if detached */ 667 uint8_t detached; 668 uint8_t shared; /* if shared, the array buffer cannot be detached */ 669 uint8_t *data; /* NULL if detached */ 670 struct list_head array_list; 671 void *opaque; 672 JSFreeArrayBufferDataFunc *free_func; 673 } JSArrayBuffer; 674 675 typedef struct JSTypedArray { 676 struct list_head link; /* link to arraybuffer */ 677 JSObject *obj; /* back pointer to the TypedArray/DataView object */ 678 JSObject *buffer; /* based array buffer */ 679 uint32_t offset; /* offset in the array buffer */ 680 uint32_t length; /* length in the array buffer */ 681 } JSTypedArray; 682 683 typedef struct JSAsyncFunctionState { 684 JSGCObjectHeader header; 685 JSValue this_val; /* 'this' argument */ 686 int argc; /* number of function arguments */ 687 BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ 688 BOOL is_completed; /* TRUE if the function has returned. The stack 689 frame is no longer valid */ 690 JSValue resolving_funcs[2]; /* only used in JS async functions */ 691 JSStackFrame frame; 692 } JSAsyncFunctionState; 693 694 typedef enum { 695 /* binary operators */ 696 JS_OVOP_ADD, 697 JS_OVOP_SUB, 698 JS_OVOP_MUL, 699 JS_OVOP_DIV, 700 JS_OVOP_MOD, 701 JS_OVOP_POW, 702 JS_OVOP_OR, 703 JS_OVOP_AND, 704 JS_OVOP_XOR, 705 JS_OVOP_SHL, 706 JS_OVOP_SAR, 707 JS_OVOP_SHR, 708 JS_OVOP_EQ, 709 JS_OVOP_LESS, 710 711 JS_OVOP_BINARY_COUNT, 712 /* unary operators */ 713 JS_OVOP_POS = JS_OVOP_BINARY_COUNT, 714 JS_OVOP_NEG, 715 JS_OVOP_INC, 716 JS_OVOP_DEC, 717 JS_OVOP_NOT, 718 719 JS_OVOP_COUNT, 720 } JSOverloadableOperatorEnum; 721 722 typedef struct { 723 uint32_t operator_index; 724 JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */ 725 } JSBinaryOperatorDefEntry; 726 727 typedef struct { 728 int count; 729 JSBinaryOperatorDefEntry *tab; 730 } JSBinaryOperatorDef; 731 732 typedef struct { 733 uint32_t operator_counter; 734 BOOL is_primitive; /* OperatorSet for a primitive type */ 735 /* NULL if no operator is defined */ 736 JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */ 737 JSBinaryOperatorDef left; 738 JSBinaryOperatorDef right; 739 } JSOperatorSetData; 740 741 typedef struct JSReqModuleEntry { 742 JSAtom module_name; 743 JSModuleDef *module; /* used using resolution */ 744 } JSReqModuleEntry; 745 746 typedef enum JSExportTypeEnum { 747 JS_EXPORT_TYPE_LOCAL, 748 JS_EXPORT_TYPE_INDIRECT, 749 } JSExportTypeEnum; 750 751 typedef struct JSExportEntry { 752 union { 753 struct { 754 int var_idx; /* closure variable index */ 755 JSVarRef *var_ref; /* if != NULL, reference to the variable */ 756 } local; /* for local export */ 757 int req_module_idx; /* module for indirect export */ 758 } u; 759 JSExportTypeEnum export_type; 760 JSAtom local_name; /* '*' if export ns from. not used for local 761 export after compilation */ 762 JSAtom export_name; /* exported variable name */ 763 } JSExportEntry; 764 765 typedef struct JSStarExportEntry { 766 int req_module_idx; /* in req_module_entries */ 767 } JSStarExportEntry; 768 769 typedef struct JSImportEntry { 770 int var_idx; /* closure variable index */ 771 JSAtom import_name; 772 int req_module_idx; /* in req_module_entries */ 773 } JSImportEntry; 774 775 typedef enum { 776 JS_MODULE_STATUS_UNLINKED, 777 JS_MODULE_STATUS_LINKING, 778 JS_MODULE_STATUS_LINKED, 779 JS_MODULE_STATUS_EVALUATING, 780 JS_MODULE_STATUS_EVALUATING_ASYNC, 781 JS_MODULE_STATUS_EVALUATED, 782 } JSModuleStatus; 783 784 struct JSModuleDef { 785 JSRefCountHeader header; /* must come first, 32-bit */ 786 JSAtom module_name; 787 struct list_head link; 788 789 JSReqModuleEntry *req_module_entries; 790 int req_module_entries_count; 791 int req_module_entries_size; 792 793 JSExportEntry *export_entries; 794 int export_entries_count; 795 int export_entries_size; 796 797 JSStarExportEntry *star_export_entries; 798 int star_export_entries_count; 799 int star_export_entries_size; 800 801 JSImportEntry *import_entries; 802 int import_entries_count; 803 int import_entries_size; 804 805 JSValue module_ns; 806 JSValue func_obj; /* only used for JS modules */ 807 JSModuleInitFunc *init_func; /* only used for C modules */ 808 BOOL has_tla : 8; /* true if func_obj contains await */ 809 BOOL resolved : 8; 810 BOOL func_created : 8; 811 JSModuleStatus status : 8; 812 /* temp use during js_module_link() & js_module_evaluate() */ 813 int dfs_index, dfs_ancestor_index; 814 JSModuleDef *stack_prev; 815 /* temp use during js_module_evaluate() */ 816 JSModuleDef **async_parent_modules; 817 int async_parent_modules_count; 818 int async_parent_modules_size; 819 int pending_async_dependencies; 820 BOOL async_evaluation; 821 int64_t async_evaluation_timestamp; 822 JSModuleDef *cycle_root; 823 JSValue promise; /* corresponds to spec field: capability */ 824 JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ 825 826 /* true if evaluation yielded an exception. It is saved in 827 eval_exception */ 828 BOOL eval_has_exception : 8; 829 JSValue eval_exception; 830 JSValue meta_obj; /* for import.meta */ 831 }; 832 833 typedef struct JSJobEntry { 834 struct list_head link; 835 JSContext *ctx; 836 JSJobFunc *job_func; 837 int argc; 838 JSValue argv[0]; 839 } JSJobEntry; 840 841 typedef struct JSProperty { 842 union { 843 JSValue value; /* JS_PROP_NORMAL */ 844 struct { /* JS_PROP_GETSET */ 845 JSObject *getter; /* NULL if undefined */ 846 JSObject *setter; /* NULL if undefined */ 847 } getset; 848 JSVarRef *var_ref; /* JS_PROP_VARREF */ 849 struct { /* JS_PROP_AUTOINIT */ 850 /* in order to use only 2 pointers, we compress the realm 851 and the init function pointer */ 852 uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) 853 in the 2 low bits */ 854 void *opaque; 855 } init; 856 } u; 857 } JSProperty; 858 859 #define JS_PROP_INITIAL_SIZE 2 860 #define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ 861 #define JS_ARRAY_INITIAL_SIZE 2 862 863 typedef struct JSShapeProperty { 864 uint32_t hash_next : 26; /* 0 if last in list */ 865 uint32_t flags : 6; /* JS_PROP_XXX */ 866 JSAtom atom; /* JS_ATOM_NULL = free property entry */ 867 } JSShapeProperty; 868 869 struct JSShape { 870 /* hash table of size hash_mask + 1 before the start of the 871 structure (see prop_hash_end()). */ 872 JSGCObjectHeader header; 873 /* true if the shape is inserted in the shape hash table. If not, 874 JSShape.hash is not valid */ 875 uint8_t is_hashed; 876 /* If true, the shape may have small array index properties 'n' with 0 877 <= n <= 2^31-1. If false, the shape is guaranteed not to have 878 small array index properties */ 879 uint8_t has_small_array_index; 880 uint32_t hash; /* current hash value */ 881 uint32_t prop_hash_mask; 882 int prop_size; /* allocated properties */ 883 int prop_count; /* include deleted properties */ 884 int deleted_prop_count; 885 JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ 886 JSObject *proto; 887 JSShapeProperty prop[0]; /* prop_size elements */ 888 }; 889 890 struct JSObject { 891 union { 892 JSGCObjectHeader header; 893 struct { 894 int __gc_ref_count; /* corresponds to header.ref_count */ 895 uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ 896 897 uint8_t extensible : 1; 898 uint8_t free_mark : 1; /* only used when freeing objects with cycles */ 899 uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ 900 uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ 901 uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ 902 uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ 903 uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ 904 uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ 905 uint16_t class_id; /* see JS_CLASS_x */ 906 }; 907 }; 908 /* byte offsets: 16/24 */ 909 JSShape *shape; /* prototype and property names + flag */ 910 JSProperty *prop; /* array of properties */ 911 /* byte offsets: 24/40 */ 912 struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ 913 /* byte offsets: 28/48 */ 914 union { 915 void *opaque; 916 struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ 917 struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ 918 struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ 919 struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ 920 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ 921 #ifdef CONFIG_BIGNUM 922 struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */ 923 struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */ 924 #endif 925 struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ 926 struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ 927 struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ 928 struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ 929 struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ 930 struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ 931 struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ 932 struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ 933 struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ 934 struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ 935 struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ 936 struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ 937 /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ 938 struct JSFunctionBytecode *function_bytecode; 939 JSVarRef **var_refs; 940 JSObject *home_object; /* for 'super' access */ 941 } func; 942 struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ 943 JSContext *realm; 944 JSCFunctionType c_function; 945 uint8_t length; 946 uint8_t cproto; 947 int16_t magic; 948 } cfunc; 949 /* array part for fast arrays and typed arrays */ 950 struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 951 union { 952 uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ 953 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 954 } u1; 955 union { 956 JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ 957 void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ 958 int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ 959 uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ 960 int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ 961 uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ 962 int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ 963 uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ 964 int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ 965 uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ 966 float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ 967 double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ 968 } u; 969 uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ 970 } array; /* 12/20 bytes */ 971 JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ 972 JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ 973 } u; 974 /* byte sizes: 40/48/72 */ 975 }; 976 977 enum { 978 __JS_ATOM_NULL = JS_ATOM_NULL, 979 #define DEF(name, str) JS_ATOM_ ## name, 980 #include "quickjs-atom.h" 981 #undef DEF 982 JS_ATOM_END, 983 }; 984 #define JS_ATOM_LAST_KEYWORD JS_ATOM_super 985 #define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield 986 987 static const char js_atom_init[] = 988 #define DEF(name, str) str "\0" 989 #include "quickjs-atom.h" 990 #undef DEF 991 ; 992 993 typedef enum OPCodeFormat { 994 #define FMT(f) OP_FMT_ ## f, 995 #define DEF(id, size, n_pop, n_push, f) 996 #include "quickjs-opcode.h" 997 #undef DEF 998 #undef FMT 999 } OPCodeFormat; 1000 1001 enum OPCodeEnum { 1002 #define FMT(f) 1003 #define DEF(id, size, n_pop, n_push, f) OP_ ## id, 1004 #define def(id, size, n_pop, n_push, f) 1005 #include "quickjs-opcode.h" 1006 #undef def 1007 #undef DEF 1008 #undef FMT 1009 OP_COUNT, /* excluding temporary opcodes */ 1010 /* temporary opcodes : overlap with the short opcodes */ 1011 OP_TEMP_START = OP_nop + 1, 1012 OP___dummy = OP_TEMP_START - 1, 1013 #define FMT(f) 1014 #define DEF(id, size, n_pop, n_push, f) 1015 #define def(id, size, n_pop, n_push, f) OP_ ## id, 1016 #include "quickjs-opcode.h" 1017 #undef def 1018 #undef DEF 1019 #undef FMT 1020 OP_TEMP_END, 1021 }; 1022 1023 static int JS_InitAtoms(JSRuntime *rt); 1024 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, 1025 int atom_type); 1026 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); 1027 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); 1028 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, 1029 JSValueConst this_obj, 1030 int argc, JSValueConst *argv, int flags); 1031 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, 1032 JSValueConst this_obj, 1033 int argc, JSValueConst *argv, int flags); 1034 static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj, 1035 JSValueConst this_obj, JSValueConst new_target, 1036 int argc, JSValue *argv, int flags); 1037 static JSValue JS_CallConstructorInternal(JSContext *ctx, 1038 JSValueConst func_obj, 1039 JSValueConst new_target, 1040 int argc, JSValue *argv, int flags); 1041 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, 1042 int argc, JSValueConst *argv); 1043 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, 1044 int argc, JSValueConst *argv); 1045 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, 1046 JSValue val, BOOL is_array_ctor); 1047 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, 1048 JSValueConst val, int flags, int scope_idx); 1049 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); 1050 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); 1051 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); 1052 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); 1053 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); 1054 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); 1055 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val); 1056 static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); 1057 static __maybe_unused void JS_PrintValue(JSContext *ctx, 1058 const char *str, 1059 JSValueConst val); 1060 static __maybe_unused void JS_DumpShapes(JSRuntime *rt); 1061 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, 1062 int argc, JSValueConst *argv, int magic); 1063 static void js_array_finalizer(JSRuntime *rt, JSValue val); 1064 static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1065 static void js_object_data_finalizer(JSRuntime *rt, JSValue val); 1066 static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1067 static void js_c_function_finalizer(JSRuntime *rt, JSValue val); 1068 static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func); 1069 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); 1070 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, 1071 JS_MarkFunc *mark_func); 1072 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); 1073 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, 1074 JS_MarkFunc *mark_func); 1075 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); 1076 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, 1077 JS_MarkFunc *mark_func); 1078 static void js_regexp_finalizer(JSRuntime *rt, JSValue val); 1079 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); 1080 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); 1081 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, 1082 JS_MarkFunc *mark_func); 1083 static void js_proxy_finalizer(JSRuntime *rt, JSValue val); 1084 static void js_proxy_mark(JSRuntime *rt, JSValueConst val, 1085 JS_MarkFunc *mark_func); 1086 static void js_map_finalizer(JSRuntime *rt, JSValue val); 1087 static void js_map_mark(JSRuntime *rt, JSValueConst val, 1088 JS_MarkFunc *mark_func); 1089 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); 1090 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, 1091 JS_MarkFunc *mark_func); 1092 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); 1093 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, 1094 JS_MarkFunc *mark_func); 1095 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); 1096 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, 1097 JS_MarkFunc *mark_func); 1098 static void js_generator_finalizer(JSRuntime *rt, JSValue obj); 1099 static void js_generator_mark(JSRuntime *rt, JSValueConst val, 1100 JS_MarkFunc *mark_func); 1101 static void js_promise_finalizer(JSRuntime *rt, JSValue val); 1102 static void js_promise_mark(JSRuntime *rt, JSValueConst val, 1103 JS_MarkFunc *mark_func); 1104 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); 1105 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, 1106 JS_MarkFunc *mark_func); 1107 #ifdef CONFIG_BIGNUM 1108 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val); 1109 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, 1110 JS_MarkFunc *mark_func); 1111 #endif 1112 1113 #define HINT_STRING 0 1114 #define HINT_NUMBER 1 1115 #define HINT_NONE 2 1116 #define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive 1117 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); 1118 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); 1119 static int JS_ToBoolFree(JSContext *ctx, JSValue val); 1120 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); 1121 static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); 1122 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); 1123 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, 1124 JSValueConst flags); 1125 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, 1126 JSValue pattern, JSValue bc); 1127 static void gc_decref(JSRuntime *rt); 1128 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, 1129 const JSClassDef *class_def, JSAtom name); 1130 1131 typedef enum JSStrictEqModeEnum { 1132 JS_EQ_STRICT, 1133 JS_EQ_SAME_VALUE, 1134 JS_EQ_SAME_VALUE_ZERO, 1135 } JSStrictEqModeEnum; 1136 1137 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 1138 JSStrictEqModeEnum eq_mode); 1139 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1140 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1141 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2); 1142 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val); 1143 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); 1144 static JSProperty *add_property(JSContext *ctx, 1145 JSObject *p, JSAtom prop, int prop_flags); 1146 static JSValue JS_NewBigInt(JSContext *ctx); 1147 static inline bf_t *JS_GetBigInt(JSValueConst val) 1148 { 1149 JSBigFloat *p = JS_VALUE_GET_PTR(val); 1150 return &p->num; 1151 } 1152 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, 1153 BOOL convert_to_safe_integer); 1154 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); 1155 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); 1156 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val); 1157 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); 1158 #ifdef CONFIG_BIGNUM 1159 static void js_float_env_finalizer(JSRuntime *rt, JSValue val); 1160 static JSValue JS_NewBigFloat(JSContext *ctx); 1161 static inline bf_t *JS_GetBigFloat(JSValueConst val) 1162 { 1163 JSBigFloat *p = JS_VALUE_GET_PTR(val); 1164 return &p->num; 1165 } 1166 static JSValue JS_NewBigDecimal(JSContext *ctx); 1167 static inline bfdec_t *JS_GetBigDecimal(JSValueConst val) 1168 { 1169 JSBigDecimal *p = JS_VALUE_GET_PTR(val); 1170 return &p->num; 1171 } 1172 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val); 1173 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, 1174 BOOL allow_null_or_undefined); 1175 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); 1176 #endif 1177 JSValue JS_ThrowOutOfMemory(JSContext *ctx); 1178 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); 1179 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); 1180 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, 1181 JSValueConst proto_val, BOOL throw_flag); 1182 1183 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); 1184 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); 1185 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); 1186 static int JS_CreateProperty(JSContext *ctx, JSObject *p, 1187 JSAtom prop, JSValueConst val, 1188 JSValueConst getter, JSValueConst setter, 1189 int flags); 1190 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); 1191 static void reset_weak_ref(JSRuntime *rt, JSObject *p); 1192 static JSValue js_array_buffer_constructor3(JSContext *ctx, 1193 JSValueConst new_target, 1194 uint64_t len, JSClassID class_id, 1195 uint8_t *buf, 1196 JSFreeArrayBufferDataFunc *free_func, 1197 void *opaque, BOOL alloc_flag); 1198 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj); 1199 static JSValue js_typed_array_constructor(JSContext *ctx, 1200 JSValueConst this_val, 1201 int argc, JSValueConst *argv, 1202 int classid); 1203 static JSValue js_typed_array_constructor_ta(JSContext *ctx, 1204 JSValueConst new_target, 1205 JSValueConst src_obj, 1206 int classid); 1207 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); 1208 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); 1209 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); 1210 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, 1211 BOOL is_arg); 1212 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); 1213 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); 1214 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, 1215 JSValueConst this_obj, 1216 int argc, JSValueConst *argv, 1217 int flags); 1218 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); 1219 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, 1220 JS_MarkFunc *mark_func); 1221 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 1222 const char *input, size_t input_len, 1223 const char *filename, int flags, int scope_idx); 1224 static void js_free_module_def(JSContext *ctx, JSModuleDef *m); 1225 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, 1226 JS_MarkFunc *mark_func); 1227 static JSValue js_import_meta(JSContext *ctx); 1228 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier); 1229 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); 1230 static JSValue js_new_promise_capability(JSContext *ctx, 1231 JSValue *resolving_funcs, 1232 JSValueConst ctor); 1233 static __exception int perform_promise_then(JSContext *ctx, 1234 JSValueConst promise, 1235 JSValueConst *resolve_reject, 1236 JSValueConst *cap_resolving_funcs); 1237 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, 1238 int argc, JSValueConst *argv, int magic); 1239 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, 1240 int argc, JSValueConst *argv); 1241 static int js_string_compare(JSContext *ctx, 1242 const JSString *p1, const JSString *p2); 1243 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); 1244 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 1245 JSValue prop, JSValue val, int flags); 1246 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val); 1247 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val); 1248 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); 1249 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, 1250 JSObject *p, JSAtom prop); 1251 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); 1252 static void JS_AddIntrinsicBasicObjects(JSContext *ctx); 1253 static void js_free_shape(JSRuntime *rt, JSShape *sh); 1254 static void js_free_shape_null(JSRuntime *rt, JSShape *sh); 1255 static int js_shape_prepare_update(JSContext *ctx, JSObject *p, 1256 JSShapeProperty **pprs); 1257 static int init_shape_hash(JSRuntime *rt); 1258 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, 1259 JSValueConst obj); 1260 static __exception int js_get_length64(JSContext *ctx, int64_t *pres, 1261 JSValueConst obj); 1262 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); 1263 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, 1264 JSValueConst array_arg); 1265 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, 1266 JSValue **arrpp, uint32_t *countp); 1267 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, 1268 JSValueConst sync_iter); 1269 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); 1270 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, 1271 JS_MarkFunc *mark_func); 1272 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, 1273 JSValueConst this_val, 1274 int argc, JSValueConst *argv, int flags); 1275 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); 1276 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, 1277 JSGCObjectTypeEnum type); 1278 static void remove_gc_object(JSGCObjectHeader *h); 1279 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); 1280 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, 1281 void *opaque); 1282 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, 1283 JSAtom atom, void *opaque); 1284 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, 1285 int argc, JSValueConst *argv, int is_map); 1286 1287 static const JSClassExoticMethods js_arguments_exotic_methods; 1288 static const JSClassExoticMethods js_string_exotic_methods; 1289 static const JSClassExoticMethods js_proxy_exotic_methods; 1290 static const JSClassExoticMethods js_module_ns_exotic_methods; 1291 static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT; 1292 1293 static void js_trigger_gc(JSRuntime *rt, size_t size) 1294 { 1295 BOOL force_gc; 1296 #ifdef FORCE_GC_AT_MALLOC 1297 force_gc = TRUE; 1298 #else 1299 force_gc = ((rt->malloc_state.malloc_size + size) > 1300 rt->malloc_gc_threshold); 1301 #endif 1302 if (force_gc) { 1303 #ifdef DUMP_GC 1304 printf("GC: size=%" PRIu64 "\n", 1305 (uint64_t)rt->malloc_state.malloc_size); 1306 #endif 1307 JS_RunGC(rt); 1308 rt->malloc_gc_threshold = rt->malloc_state.malloc_size + 1309 (rt->malloc_state.malloc_size >> 1); 1310 } 1311 } 1312 1313 static size_t js_malloc_usable_size_unknown(const void *ptr) 1314 { 1315 return 0; 1316 } 1317 1318 void *js_malloc_rt(JSRuntime *rt, size_t size) 1319 { 1320 return rt->mf.js_malloc(&rt->malloc_state, size); 1321 } 1322 1323 void js_free_rt(JSRuntime *rt, void *ptr) 1324 { 1325 rt->mf.js_free(&rt->malloc_state, ptr); 1326 } 1327 1328 void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) 1329 { 1330 return rt->mf.js_realloc(&rt->malloc_state, ptr, size); 1331 } 1332 1333 size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) 1334 { 1335 return rt->mf.js_malloc_usable_size(ptr); 1336 } 1337 1338 void *js_mallocz_rt(JSRuntime *rt, size_t size) 1339 { 1340 void *ptr; 1341 ptr = js_malloc_rt(rt, size); 1342 if (!ptr) 1343 return NULL; 1344 return memset(ptr, 0, size); 1345 } 1346 1347 /* called by libbf */ 1348 static void *js_bf_realloc(void *opaque, void *ptr, size_t size) 1349 { 1350 JSRuntime *rt = opaque; 1351 return js_realloc_rt(rt, ptr, size); 1352 } 1353 1354 /* Throw out of memory in case of error */ 1355 void *js_malloc(JSContext *ctx, size_t size) 1356 { 1357 void *ptr; 1358 ptr = js_malloc_rt(ctx->rt, size); 1359 if (unlikely(!ptr)) { 1360 JS_ThrowOutOfMemory(ctx); 1361 return NULL; 1362 } 1363 return ptr; 1364 } 1365 1366 /* Throw out of memory in case of error */ 1367 void *js_mallocz(JSContext *ctx, size_t size) 1368 { 1369 void *ptr; 1370 ptr = js_mallocz_rt(ctx->rt, size); 1371 if (unlikely(!ptr)) { 1372 JS_ThrowOutOfMemory(ctx); 1373 return NULL; 1374 } 1375 return ptr; 1376 } 1377 1378 void js_free(JSContext *ctx, void *ptr) 1379 { 1380 js_free_rt(ctx->rt, ptr); 1381 } 1382 1383 /* Throw out of memory in case of error */ 1384 void *js_realloc(JSContext *ctx, void *ptr, size_t size) 1385 { 1386 void *ret; 1387 ret = js_realloc_rt(ctx->rt, ptr, size); 1388 if (unlikely(!ret && size != 0)) { 1389 JS_ThrowOutOfMemory(ctx); 1390 return NULL; 1391 } 1392 return ret; 1393 } 1394 1395 /* store extra allocated size in *pslack if successful */ 1396 void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) 1397 { 1398 void *ret; 1399 ret = js_realloc_rt(ctx->rt, ptr, size); 1400 if (unlikely(!ret && size != 0)) { 1401 JS_ThrowOutOfMemory(ctx); 1402 return NULL; 1403 } 1404 if (pslack) { 1405 size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); 1406 *pslack = (new_size > size) ? new_size - size : 0; 1407 } 1408 return ret; 1409 } 1410 1411 size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) 1412 { 1413 return js_malloc_usable_size_rt(ctx->rt, ptr); 1414 } 1415 1416 /* Throw out of memory exception in case of error */ 1417 char *js_strndup(JSContext *ctx, const char *s, size_t n) 1418 { 1419 char *ptr; 1420 ptr = js_malloc(ctx, n + 1); 1421 if (ptr) { 1422 memcpy(ptr, s, n); 1423 ptr[n] = '\0'; 1424 } 1425 return ptr; 1426 } 1427 1428 char *js_strdup(JSContext *ctx, const char *str) 1429 { 1430 return js_strndup(ctx, str, strlen(str)); 1431 } 1432 1433 static no_inline int js_realloc_array(JSContext *ctx, void **parray, 1434 int elem_size, int *psize, int req_size) 1435 { 1436 int new_size; 1437 size_t slack; 1438 void *new_array; 1439 /* XXX: potential arithmetic overflow */ 1440 new_size = max_int(req_size, *psize * 3 / 2); 1441 new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); 1442 if (!new_array) 1443 return -1; 1444 new_size += slack / elem_size; 1445 *psize = new_size; 1446 *parray = new_array; 1447 return 0; 1448 } 1449 1450 /* resize the array and update its size if req_size > *psize */ 1451 static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, 1452 int *psize, int req_size) 1453 { 1454 if (unlikely(req_size > *psize)) 1455 return js_realloc_array(ctx, parray, elem_size, psize, req_size); 1456 else 1457 return 0; 1458 } 1459 1460 static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) 1461 { 1462 dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt); 1463 } 1464 1465 static inline int is_digit(int c) { 1466 return c >= '0' && c <= '9'; 1467 } 1468 1469 static inline int string_get(const JSString *p, int idx) { 1470 return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; 1471 } 1472 1473 typedef struct JSClassShortDef { 1474 JSAtom class_name; 1475 JSClassFinalizer *finalizer; 1476 JSClassGCMark *gc_mark; 1477 } JSClassShortDef; 1478 1479 static JSClassShortDef const js_std_class_def[] = { 1480 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ 1481 { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ 1482 { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ 1483 { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ 1484 { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ 1485 { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ 1486 { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ 1487 { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ 1488 { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ 1489 { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ 1490 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ 1491 { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ 1492 { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ 1493 { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ 1494 { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ 1495 { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ 1496 { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ 1497 { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ 1498 { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ 1499 { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ 1500 { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ 1501 { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ 1502 { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ 1503 { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ 1504 { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ 1505 { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ 1506 { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ 1507 { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ 1508 { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ 1509 { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ 1510 { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ 1511 { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ 1512 { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ 1513 #ifdef CONFIG_BIGNUM 1514 { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */ 1515 { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */ 1516 { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */ 1517 { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */ 1518 #endif 1519 { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ 1520 { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ 1521 { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ 1522 { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ 1523 { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ 1524 { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ 1525 { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ 1526 { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ 1527 { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ 1528 { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ 1529 }; 1530 1531 static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, 1532 int start, int count) 1533 { 1534 JSClassDef cm_s, *cm = &cm_s; 1535 int i, class_id; 1536 1537 for(i = 0; i < count; i++) { 1538 class_id = i + start; 1539 memset(cm, 0, sizeof(*cm)); 1540 cm->finalizer = tab[i].finalizer; 1541 cm->gc_mark = tab[i].gc_mark; 1542 if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) 1543 return -1; 1544 } 1545 return 0; 1546 } 1547 1548 static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx) 1549 { 1550 return JS_ThrowTypeError(ctx, "unsupported operation"); 1551 } 1552 1553 static JSValue invalid_to_string(JSContext *ctx, JSValueConst val) 1554 { 1555 return JS_ThrowUnsupportedOperation(ctx); 1556 } 1557 1558 static JSValue invalid_from_string(JSContext *ctx, const char *buf, 1559 int radix, int flags, slimb_t *pexponent) 1560 { 1561 return JS_NAN; 1562 } 1563 1564 static int invalid_unary_arith(JSContext *ctx, 1565 JSValue *pres, OPCodeEnum op, JSValue op1) 1566 { 1567 JS_FreeValue(ctx, op1); 1568 JS_ThrowUnsupportedOperation(ctx); 1569 return -1; 1570 } 1571 1572 static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op, 1573 JSValue *pres, JSValue op1, JSValue op2) 1574 { 1575 JS_FreeValue(ctx, op1); 1576 JS_FreeValue(ctx, op2); 1577 JS_ThrowUnsupportedOperation(ctx); 1578 return -1; 1579 } 1580 1581 static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, 1582 int64_t exponent) 1583 { 1584 return JS_ThrowUnsupportedOperation(ctx); 1585 } 1586 1587 static int invalid_mul_pow10(JSContext *ctx, JSValue *sp) 1588 { 1589 JS_ThrowUnsupportedOperation(ctx); 1590 return -1; 1591 } 1592 1593 static void set_dummy_numeric_ops(JSNumericOperations *ops) 1594 { 1595 ops->to_string = invalid_to_string; 1596 ops->from_string = invalid_from_string; 1597 ops->unary_arith = invalid_unary_arith; 1598 ops->binary_arith = invalid_binary_arith; 1599 ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64; 1600 ops->mul_pow10 = invalid_mul_pow10; 1601 } 1602 1603 #if !defined(CONFIG_STACK_CHECK) 1604 /* no stack limitation */ 1605 static inline uintptr_t js_get_stack_pointer(void) 1606 { 1607 return 0; 1608 } 1609 1610 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) 1611 { 1612 return FALSE; 1613 } 1614 #else 1615 /* Note: OS and CPU dependent */ 1616 static inline uintptr_t js_get_stack_pointer(void) 1617 { 1618 return (uintptr_t)__builtin_frame_address(0); 1619 } 1620 1621 static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) 1622 { 1623 uintptr_t sp; 1624 sp = js_get_stack_pointer() - alloca_size; 1625 return unlikely(sp < rt->stack_limit); 1626 } 1627 #endif 1628 1629 JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) 1630 { 1631 JSRuntime *rt; 1632 JSMallocState ms; 1633 1634 memset(&ms, 0, sizeof(ms)); 1635 ms.opaque = opaque; 1636 ms.malloc_limit = -1; 1637 1638 rt = mf->js_malloc(&ms, sizeof(JSRuntime)); 1639 if (!rt) 1640 return NULL; 1641 memset(rt, 0, sizeof(*rt)); 1642 rt->mf = *mf; 1643 if (!rt->mf.js_malloc_usable_size) { 1644 /* use dummy function if none provided */ 1645 rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; 1646 } 1647 rt->malloc_state = ms; 1648 rt->malloc_gc_threshold = 256 * 1024; 1649 1650 bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); 1651 set_dummy_numeric_ops(&rt->bigint_ops); 1652 #ifdef CONFIG_BIGNUM 1653 set_dummy_numeric_ops(&rt->bigfloat_ops); 1654 set_dummy_numeric_ops(&rt->bigdecimal_ops); 1655 #endif 1656 1657 init_list_head(&rt->context_list); 1658 init_list_head(&rt->gc_obj_list); 1659 init_list_head(&rt->gc_zero_ref_count_list); 1660 rt->gc_phase = JS_GC_PHASE_NONE; 1661 1662 #ifdef DUMP_LEAKS 1663 init_list_head(&rt->string_list); 1664 #endif 1665 init_list_head(&rt->job_list); 1666 1667 if (JS_InitAtoms(rt)) 1668 goto fail; 1669 1670 /* create the object, array and function classes */ 1671 if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, 1672 countof(js_std_class_def)) < 0) 1673 goto fail; 1674 rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; 1675 rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; 1676 rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; 1677 1678 rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; 1679 rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; 1680 rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; 1681 rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; 1682 if (init_shape_hash(rt)) 1683 goto fail; 1684 1685 rt->stack_size = JS_DEFAULT_STACK_SIZE; 1686 JS_UpdateStackTop(rt); 1687 1688 rt->current_exception = JS_UNINITIALIZED; 1689 1690 return rt; 1691 fail: 1692 JS_FreeRuntime(rt); 1693 return NULL; 1694 } 1695 1696 void *JS_GetRuntimeOpaque(JSRuntime *rt) 1697 { 1698 return rt->user_opaque; 1699 } 1700 1701 void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) 1702 { 1703 rt->user_opaque = opaque; 1704 } 1705 1706 /* default memory allocation functions with memory limitation */ 1707 static size_t js_def_malloc_usable_size(const void *ptr) 1708 { 1709 #if defined(__APPLE__) 1710 return malloc_size(ptr); 1711 #elif defined(_WIN32) 1712 return _msize((void *)ptr); 1713 #elif defined(EMSCRIPTEN) 1714 return 0; 1715 #elif defined(__linux__) || defined(__GLIBC__) 1716 return malloc_usable_size((void *)ptr); 1717 #else 1718 /* change this to `return 0;` if compilation fails */ 1719 return malloc_usable_size((void *)ptr); 1720 #endif 1721 } 1722 1723 static void *js_def_malloc(JSMallocState *s, size_t size) 1724 { 1725 void *ptr; 1726 1727 /* Do not allocate zero bytes: behavior is platform dependent */ 1728 assert(size != 0); 1729 1730 if (unlikely(s->malloc_size + size > s->malloc_limit)) 1731 return NULL; 1732 1733 ptr = malloc(size); 1734 if (!ptr) 1735 return NULL; 1736 1737 s->malloc_count++; 1738 s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 1739 return ptr; 1740 } 1741 1742 static void js_def_free(JSMallocState *s, void *ptr) 1743 { 1744 if (!ptr) 1745 return; 1746 1747 s->malloc_count--; 1748 s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD; 1749 free(ptr); 1750 } 1751 1752 static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) 1753 { 1754 size_t old_size; 1755 1756 if (!ptr) { 1757 if (size == 0) 1758 return NULL; 1759 return js_def_malloc(s, size); 1760 } 1761 old_size = js_def_malloc_usable_size(ptr); 1762 if (size == 0) { 1763 s->malloc_count--; 1764 s->malloc_size -= old_size + MALLOC_OVERHEAD; 1765 free(ptr); 1766 return NULL; 1767 } 1768 if (s->malloc_size + size - old_size > s->malloc_limit) 1769 return NULL; 1770 1771 ptr = realloc(ptr, size); 1772 if (!ptr) 1773 return NULL; 1774 1775 s->malloc_size += js_def_malloc_usable_size(ptr) - old_size; 1776 return ptr; 1777 } 1778 1779 static const JSMallocFunctions def_malloc_funcs = { 1780 js_def_malloc, 1781 js_def_free, 1782 js_def_realloc, 1783 js_def_malloc_usable_size, 1784 }; 1785 1786 JSRuntime *JS_NewRuntime(void) 1787 { 1788 return JS_NewRuntime2(&def_malloc_funcs, NULL); 1789 } 1790 1791 void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) 1792 { 1793 rt->malloc_state.malloc_limit = limit; 1794 } 1795 1796 /* use -1 to disable automatic GC */ 1797 void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) 1798 { 1799 rt->malloc_gc_threshold = gc_threshold; 1800 } 1801 1802 #define malloc(s) malloc_is_forbidden(s) 1803 #define free(p) free_is_forbidden(p) 1804 #define realloc(p,s) realloc_is_forbidden(p,s) 1805 1806 void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) 1807 { 1808 rt->interrupt_handler = cb; 1809 rt->interrupt_opaque = opaque; 1810 } 1811 1812 void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) 1813 { 1814 rt->can_block = can_block; 1815 } 1816 1817 void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, 1818 const JSSharedArrayBufferFunctions *sf) 1819 { 1820 rt->sab_funcs = *sf; 1821 } 1822 1823 /* return 0 if OK, < 0 if exception */ 1824 int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, 1825 int argc, JSValueConst *argv) 1826 { 1827 JSRuntime *rt = ctx->rt; 1828 JSJobEntry *e; 1829 int i; 1830 1831 e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); 1832 if (!e) 1833 return -1; 1834 e->ctx = ctx; 1835 e->job_func = job_func; 1836 e->argc = argc; 1837 for(i = 0; i < argc; i++) { 1838 e->argv[i] = JS_DupValue(ctx, argv[i]); 1839 } 1840 list_add_tail(&e->link, &rt->job_list); 1841 return 0; 1842 } 1843 1844 BOOL JS_IsJobPending(JSRuntime *rt) 1845 { 1846 return !list_empty(&rt->job_list); 1847 } 1848 1849 /* return < 0 if exception, 0 if no job pending, 1 if a job was 1850 executed successfully. the context of the job is stored in '*pctx' */ 1851 int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) 1852 { 1853 JSContext *ctx; 1854 JSJobEntry *e; 1855 JSValue res; 1856 int i, ret; 1857 1858 if (list_empty(&rt->job_list)) { 1859 *pctx = NULL; 1860 return 0; 1861 } 1862 1863 /* get the first pending job and execute it */ 1864 e = list_entry(rt->job_list.next, JSJobEntry, link); 1865 list_del(&e->link); 1866 ctx = e->ctx; 1867 res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv); 1868 for(i = 0; i < e->argc; i++) 1869 JS_FreeValue(ctx, e->argv[i]); 1870 if (JS_IsException(res)) 1871 ret = -1; 1872 else 1873 ret = 1; 1874 JS_FreeValue(ctx, res); 1875 js_free(ctx, e); 1876 *pctx = ctx; 1877 return ret; 1878 } 1879 1880 static inline uint32_t atom_get_free(const JSAtomStruct *p) 1881 { 1882 return (uintptr_t)p >> 1; 1883 } 1884 1885 static inline BOOL atom_is_free(const JSAtomStruct *p) 1886 { 1887 return (uintptr_t)p & 1; 1888 } 1889 1890 static inline JSAtomStruct *atom_set_free(uint32_t v) 1891 { 1892 return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); 1893 } 1894 1895 /* Note: the string contents are uninitialized */ 1896 static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) 1897 { 1898 JSString *str; 1899 str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); 1900 if (unlikely(!str)) 1901 return NULL; 1902 str->header.ref_count = 1; 1903 str->is_wide_char = is_wide_char; 1904 str->len = max_len; 1905 str->atom_type = 0; 1906 str->hash = 0; /* optional but costless */ 1907 str->hash_next = 0; /* optional */ 1908 #ifdef DUMP_LEAKS 1909 list_add_tail(&str->link, &rt->string_list); 1910 #endif 1911 return str; 1912 } 1913 1914 static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) 1915 { 1916 JSString *p; 1917 p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); 1918 if (unlikely(!p)) { 1919 JS_ThrowOutOfMemory(ctx); 1920 return NULL; 1921 } 1922 return p; 1923 } 1924 1925 /* same as JS_FreeValueRT() but faster */ 1926 static inline void js_free_string(JSRuntime *rt, JSString *str) 1927 { 1928 if (--str->header.ref_count <= 0) { 1929 if (str->atom_type) { 1930 JS_FreeAtomStruct(rt, str); 1931 } else { 1932 #ifdef DUMP_LEAKS 1933 list_del(&str->link); 1934 #endif 1935 js_free_rt(rt, str); 1936 } 1937 } 1938 } 1939 1940 void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) 1941 { 1942 if (rt) 1943 rt->rt_info = s; 1944 } 1945 1946 void JS_FreeRuntime(JSRuntime *rt) 1947 { 1948 struct list_head *el, *el1; 1949 int i; 1950 1951 JS_FreeValueRT(rt, rt->current_exception); 1952 1953 list_for_each_safe(el, el1, &rt->job_list) { 1954 JSJobEntry *e = list_entry(el, JSJobEntry, link); 1955 for(i = 0; i < e->argc; i++) 1956 JS_FreeValueRT(rt, e->argv[i]); 1957 js_free_rt(rt, e); 1958 } 1959 init_list_head(&rt->job_list); 1960 1961 JS_RunGC(rt); 1962 1963 #ifdef DUMP_LEAKS 1964 /* leaking objects */ 1965 { 1966 BOOL header_done; 1967 JSGCObjectHeader *p; 1968 int count; 1969 1970 /* remove the internal refcounts to display only the object 1971 referenced externally */ 1972 list_for_each(el, &rt->gc_obj_list) { 1973 p = list_entry(el, JSGCObjectHeader, link); 1974 p->mark = 0; 1975 } 1976 gc_decref(rt); 1977 1978 header_done = FALSE; 1979 list_for_each(el, &rt->gc_obj_list) { 1980 p = list_entry(el, JSGCObjectHeader, link); 1981 if (p->ref_count != 0) { 1982 if (!header_done) { 1983 printf("Object leaks:\n"); 1984 JS_DumpObjectHeader(rt); 1985 header_done = TRUE; 1986 } 1987 JS_DumpGCObject(rt, p); 1988 } 1989 } 1990 1991 count = 0; 1992 list_for_each(el, &rt->gc_obj_list) { 1993 p = list_entry(el, JSGCObjectHeader, link); 1994 if (p->ref_count == 0) { 1995 count++; 1996 } 1997 } 1998 if (count != 0) 1999 printf("Secondary object leaks: %d\n", count); 2000 } 2001 #endif 2002 assert(list_empty(&rt->gc_obj_list)); 2003 2004 /* free the classes */ 2005 for(i = 0; i < rt->class_count; i++) { 2006 JSClass *cl = &rt->class_array[i]; 2007 if (cl->class_id != 0) { 2008 JS_FreeAtomRT(rt, cl->class_name); 2009 } 2010 } 2011 js_free_rt(rt, rt->class_array); 2012 2013 bf_context_end(&rt->bf_ctx); 2014 2015 #ifdef DUMP_LEAKS 2016 /* only the atoms defined in JS_InitAtoms() should be left */ 2017 { 2018 BOOL header_done = FALSE; 2019 2020 for(i = 0; i < rt->atom_size; i++) { 2021 JSAtomStruct *p = rt->atom_array[i]; 2022 if (!atom_is_free(p) /* && p->str*/) { 2023 if (i >= JS_ATOM_END || p->header.ref_count != 1) { 2024 if (!header_done) { 2025 header_done = TRUE; 2026 if (rt->rt_info) { 2027 printf("%s:1: atom leakage:", rt->rt_info); 2028 } else { 2029 printf("Atom leaks:\n" 2030 " %6s %6s %s\n", 2031 "ID", "REFCNT", "NAME"); 2032 } 2033 } 2034 if (rt->rt_info) { 2035 printf(" "); 2036 } else { 2037 printf(" %6u %6u ", i, p->header.ref_count); 2038 } 2039 switch (p->atom_type) { 2040 case JS_ATOM_TYPE_STRING: 2041 JS_DumpString(rt, p); 2042 break; 2043 case JS_ATOM_TYPE_GLOBAL_SYMBOL: 2044 printf("Symbol.for("); 2045 JS_DumpString(rt, p); 2046 printf(")"); 2047 break; 2048 case JS_ATOM_TYPE_SYMBOL: 2049 if (p->hash == JS_ATOM_HASH_SYMBOL) { 2050 printf("Symbol("); 2051 JS_DumpString(rt, p); 2052 printf(")"); 2053 } else { 2054 printf("Private("); 2055 JS_DumpString(rt, p); 2056 printf(")"); 2057 } 2058 break; 2059 } 2060 if (rt->rt_info) { 2061 printf(":%u", p->header.ref_count); 2062 } else { 2063 printf("\n"); 2064 } 2065 } 2066 } 2067 } 2068 if (rt->rt_info && header_done) 2069 printf("\n"); 2070 } 2071 #endif 2072 2073 /* free the atoms */ 2074 for(i = 0; i < rt->atom_size; i++) { 2075 JSAtomStruct *p = rt->atom_array[i]; 2076 if (!atom_is_free(p)) { 2077 #ifdef DUMP_LEAKS 2078 list_del(&p->link); 2079 #endif 2080 js_free_rt(rt, p); 2081 } 2082 } 2083 js_free_rt(rt, rt->atom_array); 2084 js_free_rt(rt, rt->atom_hash); 2085 js_free_rt(rt, rt->shape_hash); 2086 #ifdef DUMP_LEAKS 2087 if (!list_empty(&rt->string_list)) { 2088 if (rt->rt_info) { 2089 printf("%s:1: string leakage:", rt->rt_info); 2090 } else { 2091 printf("String leaks:\n" 2092 " %6s %s\n", 2093 "REFCNT", "VALUE"); 2094 } 2095 list_for_each_safe(el, el1, &rt->string_list) { 2096 JSString *str = list_entry(el, JSString, link); 2097 if (rt->rt_info) { 2098 printf(" "); 2099 } else { 2100 printf(" %6u ", str->header.ref_count); 2101 } 2102 JS_DumpString(rt, str); 2103 if (rt->rt_info) { 2104 printf(":%u", str->header.ref_count); 2105 } else { 2106 printf("\n"); 2107 } 2108 list_del(&str->link); 2109 js_free_rt(rt, str); 2110 } 2111 if (rt->rt_info) 2112 printf("\n"); 2113 } 2114 { 2115 JSMallocState *s = &rt->malloc_state; 2116 if (s->malloc_count > 1) { 2117 if (rt->rt_info) 2118 printf("%s:1: ", rt->rt_info); 2119 printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", 2120 (uint64_t)(s->malloc_size - sizeof(JSRuntime)), 2121 (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); 2122 } 2123 } 2124 #endif 2125 2126 { 2127 JSMallocState ms = rt->malloc_state; 2128 rt->mf.js_free(&ms, rt); 2129 } 2130 } 2131 2132 JSContext *JS_NewContextRaw(JSRuntime *rt) 2133 { 2134 JSContext *ctx; 2135 int i; 2136 2137 ctx = js_mallocz_rt(rt, sizeof(JSContext)); 2138 if (!ctx) 2139 return NULL; 2140 ctx->header.ref_count = 1; 2141 add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); 2142 2143 ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * 2144 rt->class_count); 2145 if (!ctx->class_proto) { 2146 js_free_rt(rt, ctx); 2147 return NULL; 2148 } 2149 ctx->rt = rt; 2150 list_add_tail(&ctx->link, &rt->context_list); 2151 ctx->bf_ctx = &rt->bf_ctx; 2152 #ifdef CONFIG_BIGNUM 2153 ctx->fp_env.prec = 113; 2154 ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; 2155 #endif 2156 for(i = 0; i < rt->class_count; i++) 2157 ctx->class_proto[i] = JS_NULL; 2158 ctx->array_ctor = JS_NULL; 2159 ctx->regexp_ctor = JS_NULL; 2160 ctx->promise_ctor = JS_NULL; 2161 init_list_head(&ctx->loaded_modules); 2162 2163 JS_AddIntrinsicBasicObjects(ctx); 2164 return ctx; 2165 } 2166 2167 JSContext *JS_NewContext(JSRuntime *rt) 2168 { 2169 JSContext *ctx; 2170 2171 ctx = JS_NewContextRaw(rt); 2172 if (!ctx) 2173 return NULL; 2174 2175 JS_AddIntrinsicBaseObjects(ctx); 2176 JS_AddIntrinsicDate(ctx); 2177 JS_AddIntrinsicEval(ctx); 2178 JS_AddIntrinsicStringNormalize(ctx); 2179 JS_AddIntrinsicRegExp(ctx); 2180 JS_AddIntrinsicJSON(ctx); 2181 JS_AddIntrinsicProxy(ctx); 2182 JS_AddIntrinsicMapSet(ctx); 2183 JS_AddIntrinsicTypedArrays(ctx); 2184 JS_AddIntrinsicPromise(ctx); 2185 JS_AddIntrinsicBigInt(ctx); 2186 return ctx; 2187 } 2188 2189 void *JS_GetContextOpaque(JSContext *ctx) 2190 { 2191 return ctx->user_opaque; 2192 } 2193 2194 void JS_SetContextOpaque(JSContext *ctx, void *opaque) 2195 { 2196 ctx->user_opaque = opaque; 2197 } 2198 2199 /* set the new value and free the old value after (freeing the value 2200 can reallocate the object data) */ 2201 static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) 2202 { 2203 JSValue old_val; 2204 old_val = *pval; 2205 *pval = new_val; 2206 JS_FreeValue(ctx, old_val); 2207 } 2208 2209 void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) 2210 { 2211 JSRuntime *rt = ctx->rt; 2212 assert(class_id < rt->class_count); 2213 set_value(ctx, &ctx->class_proto[class_id], obj); 2214 } 2215 2216 JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) 2217 { 2218 JSRuntime *rt = ctx->rt; 2219 assert(class_id < rt->class_count); 2220 return JS_DupValue(ctx, ctx->class_proto[class_id]); 2221 } 2222 2223 typedef enum JSFreeModuleEnum { 2224 JS_FREE_MODULE_ALL, 2225 JS_FREE_MODULE_NOT_RESOLVED, 2226 } JSFreeModuleEnum; 2227 2228 /* XXX: would be more efficient with separate module lists */ 2229 static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) 2230 { 2231 struct list_head *el, *el1; 2232 list_for_each_safe(el, el1, &ctx->loaded_modules) { 2233 JSModuleDef *m = list_entry(el, JSModuleDef, link); 2234 if (flag == JS_FREE_MODULE_ALL || 2235 (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { 2236 js_free_module_def(ctx, m); 2237 } 2238 } 2239 } 2240 2241 JSContext *JS_DupContext(JSContext *ctx) 2242 { 2243 ctx->header.ref_count++; 2244 return ctx; 2245 } 2246 2247 /* used by the GC */ 2248 static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, 2249 JS_MarkFunc *mark_func) 2250 { 2251 int i; 2252 struct list_head *el; 2253 2254 /* modules are not seen by the GC, so we directly mark the objects 2255 referenced by each module */ 2256 list_for_each(el, &ctx->loaded_modules) { 2257 JSModuleDef *m = list_entry(el, JSModuleDef, link); 2258 js_mark_module_def(rt, m, mark_func); 2259 } 2260 2261 JS_MarkValue(rt, ctx->global_obj, mark_func); 2262 JS_MarkValue(rt, ctx->global_var_obj, mark_func); 2263 2264 JS_MarkValue(rt, ctx->throw_type_error, mark_func); 2265 JS_MarkValue(rt, ctx->eval_obj, mark_func); 2266 2267 JS_MarkValue(rt, ctx->array_proto_values, mark_func); 2268 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 2269 JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); 2270 } 2271 for(i = 0; i < rt->class_count; i++) { 2272 JS_MarkValue(rt, ctx->class_proto[i], mark_func); 2273 } 2274 JS_MarkValue(rt, ctx->iterator_proto, mark_func); 2275 JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); 2276 JS_MarkValue(rt, ctx->promise_ctor, mark_func); 2277 JS_MarkValue(rt, ctx->array_ctor, mark_func); 2278 JS_MarkValue(rt, ctx->regexp_ctor, mark_func); 2279 JS_MarkValue(rt, ctx->function_ctor, mark_func); 2280 JS_MarkValue(rt, ctx->function_proto, mark_func); 2281 2282 if (ctx->array_shape) 2283 mark_func(rt, &ctx->array_shape->header); 2284 } 2285 2286 void JS_FreeContext(JSContext *ctx) 2287 { 2288 JSRuntime *rt = ctx->rt; 2289 int i; 2290 2291 if (--ctx->header.ref_count > 0) 2292 return; 2293 assert(ctx->header.ref_count == 0); 2294 2295 #ifdef DUMP_ATOMS 2296 JS_DumpAtoms(ctx->rt); 2297 #endif 2298 #ifdef DUMP_SHAPES 2299 JS_DumpShapes(ctx->rt); 2300 #endif 2301 #ifdef DUMP_OBJECTS 2302 { 2303 struct list_head *el; 2304 JSGCObjectHeader *p; 2305 printf("JSObjects: {\n"); 2306 JS_DumpObjectHeader(ctx->rt); 2307 list_for_each(el, &rt->gc_obj_list) { 2308 p = list_entry(el, JSGCObjectHeader, link); 2309 JS_DumpGCObject(rt, p); 2310 } 2311 printf("}\n"); 2312 } 2313 #endif 2314 #ifdef DUMP_MEM 2315 { 2316 JSMemoryUsage stats; 2317 JS_ComputeMemoryUsage(rt, &stats); 2318 JS_DumpMemoryUsage(stdout, &stats, rt); 2319 } 2320 #endif 2321 2322 js_free_modules(ctx, JS_FREE_MODULE_ALL); 2323 2324 JS_FreeValue(ctx, ctx->global_obj); 2325 JS_FreeValue(ctx, ctx->global_var_obj); 2326 2327 JS_FreeValue(ctx, ctx->throw_type_error); 2328 JS_FreeValue(ctx, ctx->eval_obj); 2329 2330 JS_FreeValue(ctx, ctx->array_proto_values); 2331 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 2332 JS_FreeValue(ctx, ctx->native_error_proto[i]); 2333 } 2334 for(i = 0; i < rt->class_count; i++) { 2335 JS_FreeValue(ctx, ctx->class_proto[i]); 2336 } 2337 js_free_rt(rt, ctx->class_proto); 2338 JS_FreeValue(ctx, ctx->iterator_proto); 2339 JS_FreeValue(ctx, ctx->async_iterator_proto); 2340 JS_FreeValue(ctx, ctx->promise_ctor); 2341 JS_FreeValue(ctx, ctx->array_ctor); 2342 JS_FreeValue(ctx, ctx->regexp_ctor); 2343 JS_FreeValue(ctx, ctx->function_ctor); 2344 JS_FreeValue(ctx, ctx->function_proto); 2345 2346 js_free_shape_null(ctx->rt, ctx->array_shape); 2347 2348 list_del(&ctx->link); 2349 remove_gc_object(&ctx->header); 2350 js_free_rt(ctx->rt, ctx); 2351 } 2352 2353 JSRuntime *JS_GetRuntime(JSContext *ctx) 2354 { 2355 return ctx->rt; 2356 } 2357 2358 static void update_stack_limit(JSRuntime *rt) 2359 { 2360 if (rt->stack_size == 0) { 2361 rt->stack_limit = 0; /* no limit */ 2362 } else { 2363 rt->stack_limit = rt->stack_top - rt->stack_size; 2364 } 2365 } 2366 2367 void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) 2368 { 2369 rt->stack_size = stack_size; 2370 update_stack_limit(rt); 2371 } 2372 2373 void JS_UpdateStackTop(JSRuntime *rt) 2374 { 2375 rt->stack_top = js_get_stack_pointer(); 2376 update_stack_limit(rt); 2377 } 2378 2379 static inline BOOL is_strict_mode(JSContext *ctx) 2380 { 2381 JSStackFrame *sf = ctx->rt->current_stack_frame; 2382 return (sf && (sf->js_mode & JS_MODE_STRICT)); 2383 } 2384 2385 #ifdef CONFIG_BIGNUM 2386 static inline BOOL is_math_mode(JSContext *ctx) 2387 { 2388 JSStackFrame *sf = ctx->rt->current_stack_frame; 2389 return (sf && (sf->js_mode & JS_MODE_MATH)); 2390 } 2391 #else 2392 static inline BOOL is_math_mode(JSContext *ctx) 2393 { 2394 return FALSE; 2395 } 2396 #endif 2397 2398 /* JSAtom support */ 2399 2400 #define JS_ATOM_TAG_INT (1U << 31) 2401 #define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) 2402 #define JS_ATOM_MAX ((1U << 30) - 1) 2403 2404 /* return the max count from the hash size */ 2405 #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) 2406 2407 static inline BOOL __JS_AtomIsConst(JSAtom v) 2408 { 2409 #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 2410 return (int32_t)v <= 0; 2411 #else 2412 return (int32_t)v < JS_ATOM_END; 2413 #endif 2414 } 2415 2416 static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) 2417 { 2418 return (v & JS_ATOM_TAG_INT) != 0; 2419 } 2420 2421 static inline JSAtom __JS_AtomFromUInt32(uint32_t v) 2422 { 2423 return v | JS_ATOM_TAG_INT; 2424 } 2425 2426 static inline uint32_t __JS_AtomToUInt32(JSAtom atom) 2427 { 2428 return atom & ~JS_ATOM_TAG_INT; 2429 } 2430 2431 static inline int is_num(int c) 2432 { 2433 return c >= '0' && c <= '9'; 2434 } 2435 2436 /* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ 2437 static inline BOOL is_num_string(uint32_t *pval, const JSString *p) 2438 { 2439 uint32_t n; 2440 uint64_t n64; 2441 int c, i, len; 2442 2443 len = p->len; 2444 if (len == 0 || len > 10) 2445 return FALSE; 2446 c = string_get(p, 0); 2447 if (is_num(c)) { 2448 if (c == '0') { 2449 if (len != 1) 2450 return FALSE; 2451 n = 0; 2452 } else { 2453 n = c - '0'; 2454 for(i = 1; i < len; i++) { 2455 c = string_get(p, i); 2456 if (!is_num(c)) 2457 return FALSE; 2458 n64 = (uint64_t)n * 10 + (c - '0'); 2459 if ((n64 >> 32) != 0) 2460 return FALSE; 2461 n = n64; 2462 } 2463 } 2464 *pval = n; 2465 return TRUE; 2466 } else { 2467 return FALSE; 2468 } 2469 } 2470 2471 /* XXX: could use faster version ? */ 2472 static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) 2473 { 2474 size_t i; 2475 2476 for(i = 0; i < len; i++) 2477 h = h * 263 + str[i]; 2478 return h; 2479 } 2480 2481 static inline uint32_t hash_string16(const uint16_t *str, 2482 size_t len, uint32_t h) 2483 { 2484 size_t i; 2485 2486 for(i = 0; i < len; i++) 2487 h = h * 263 + str[i]; 2488 return h; 2489 } 2490 2491 static uint32_t hash_string(const JSString *str, uint32_t h) 2492 { 2493 if (str->is_wide_char) 2494 h = hash_string16(str->u.str16, str->len, h); 2495 else 2496 h = hash_string8(str->u.str8, str->len, h); 2497 return h; 2498 } 2499 2500 static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep) 2501 { 2502 if (c == sep || c == '\\') { 2503 putchar('\\'); 2504 putchar(c); 2505 } else if (c >= ' ' && c <= 126) { 2506 putchar(c); 2507 } else if (c == '\n') { 2508 putchar('\\'); 2509 putchar('n'); 2510 } else { 2511 printf("\\u%04x", c); 2512 } 2513 } 2514 2515 static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) 2516 { 2517 int i, sep; 2518 2519 if (p == NULL) { 2520 printf("<null>"); 2521 return; 2522 } 2523 printf("%d", p->header.ref_count); 2524 sep = (p->header.ref_count == 1) ? '\"' : '\''; 2525 putchar(sep); 2526 for(i = 0; i < p->len; i++) { 2527 JS_DumpChar(rt, string_get(p, i), sep); 2528 } 2529 putchar(sep); 2530 } 2531 2532 static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) 2533 { 2534 JSAtomStruct *p; 2535 int h, i; 2536 /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ 2537 printf("JSAtom count=%d size=%d hash_size=%d:\n", 2538 rt->atom_count, rt->atom_size, rt->atom_hash_size); 2539 printf("JSAtom hash table: {\n"); 2540 for(i = 0; i < rt->atom_hash_size; i++) { 2541 h = rt->atom_hash[i]; 2542 if (h) { 2543 printf(" %d:", i); 2544 while (h) { 2545 p = rt->atom_array[h]; 2546 printf(" "); 2547 JS_DumpString(rt, p); 2548 h = p->hash_next; 2549 } 2550 printf("\n"); 2551 } 2552 } 2553 printf("}\n"); 2554 printf("JSAtom table: {\n"); 2555 for(i = 0; i < rt->atom_size; i++) { 2556 p = rt->atom_array[i]; 2557 if (!atom_is_free(p)) { 2558 printf(" %d: { %d %08x ", i, p->atom_type, p->hash); 2559 if (!(p->len == 0 && p->is_wide_char != 0)) 2560 JS_DumpString(rt, p); 2561 printf(" %d }\n", p->hash_next); 2562 } 2563 } 2564 printf("}\n"); 2565 } 2566 2567 static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) 2568 { 2569 JSAtomStruct *p; 2570 uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; 2571 2572 assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ 2573 new_hash_mask = new_hash_size - 1; 2574 new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); 2575 if (!new_hash) 2576 return -1; 2577 for(i = 0; i < rt->atom_hash_size; i++) { 2578 h = rt->atom_hash[i]; 2579 while (h != 0) { 2580 p = rt->atom_array[h]; 2581 hash_next1 = p->hash_next; 2582 /* add in new hash table */ 2583 j = p->hash & new_hash_mask; 2584 p->hash_next = new_hash[j]; 2585 new_hash[j] = h; 2586 h = hash_next1; 2587 } 2588 } 2589 js_free_rt(rt, rt->atom_hash); 2590 rt->atom_hash = new_hash; 2591 rt->atom_hash_size = new_hash_size; 2592 rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); 2593 // JS_DumpAtoms(rt); 2594 return 0; 2595 } 2596 2597 static int JS_InitAtoms(JSRuntime *rt) 2598 { 2599 int i, len, atom_type; 2600 const char *p; 2601 2602 rt->atom_hash_size = 0; 2603 rt->atom_hash = NULL; 2604 rt->atom_count = 0; 2605 rt->atom_size = 0; 2606 rt->atom_free_index = 0; 2607 if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ 2608 return -1; 2609 2610 p = js_atom_init; 2611 for(i = 1; i < JS_ATOM_END; i++) { 2612 if (i == JS_ATOM_Private_brand) 2613 atom_type = JS_ATOM_TYPE_PRIVATE; 2614 else if (i >= JS_ATOM_Symbol_toPrimitive) 2615 atom_type = JS_ATOM_TYPE_SYMBOL; 2616 else 2617 atom_type = JS_ATOM_TYPE_STRING; 2618 len = strlen(p); 2619 if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) 2620 return -1; 2621 p = p + len + 1; 2622 } 2623 return 0; 2624 } 2625 2626 static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) 2627 { 2628 JSAtomStruct *p; 2629 2630 if (!__JS_AtomIsConst(v)) { 2631 p = rt->atom_array[v]; 2632 p->header.ref_count++; 2633 } 2634 return v; 2635 } 2636 2637 JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) 2638 { 2639 JSRuntime *rt; 2640 JSAtomStruct *p; 2641 2642 if (!__JS_AtomIsConst(v)) { 2643 rt = ctx->rt; 2644 p = rt->atom_array[v]; 2645 p->header.ref_count++; 2646 } 2647 return v; 2648 } 2649 2650 static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) 2651 { 2652 JSRuntime *rt; 2653 JSAtomStruct *p; 2654 2655 rt = ctx->rt; 2656 if (__JS_AtomIsTaggedInt(v)) 2657 return JS_ATOM_KIND_STRING; 2658 p = rt->atom_array[v]; 2659 switch(p->atom_type) { 2660 case JS_ATOM_TYPE_STRING: 2661 return JS_ATOM_KIND_STRING; 2662 case JS_ATOM_TYPE_GLOBAL_SYMBOL: 2663 return JS_ATOM_KIND_SYMBOL; 2664 case JS_ATOM_TYPE_SYMBOL: 2665 switch(p->hash) { 2666 case JS_ATOM_HASH_SYMBOL: 2667 return JS_ATOM_KIND_SYMBOL; 2668 case JS_ATOM_HASH_PRIVATE: 2669 return JS_ATOM_KIND_PRIVATE; 2670 default: 2671 abort(); 2672 } 2673 default: 2674 abort(); 2675 } 2676 } 2677 2678 static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) 2679 { 2680 return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; 2681 } 2682 2683 static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) 2684 { 2685 uint32_t i = p->hash_next; /* atom_index */ 2686 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { 2687 JSAtomStruct *p1; 2688 2689 i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; 2690 p1 = rt->atom_array[i]; 2691 while (p1 != p) { 2692 assert(i != 0); 2693 i = p1->hash_next; 2694 p1 = rt->atom_array[i]; 2695 } 2696 } 2697 return i; 2698 } 2699 2700 /* string case (internal). Return JS_ATOM_NULL if error. 'str' is 2701 freed. */ 2702 static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) 2703 { 2704 uint32_t h, h1, i; 2705 JSAtomStruct *p; 2706 int len; 2707 2708 #if 0 2709 printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n"); 2710 #endif 2711 if (atom_type < JS_ATOM_TYPE_SYMBOL) { 2712 /* str is not NULL */ 2713 if (str->atom_type == atom_type) { 2714 /* str is the atom, return its index */ 2715 i = js_get_atom_index(rt, str); 2716 /* reduce string refcount and increase atom's unless constant */ 2717 if (__JS_AtomIsConst(i)) 2718 str->header.ref_count--; 2719 return i; 2720 } 2721 /* try and locate an already registered atom */ 2722 len = str->len; 2723 h = hash_string(str, atom_type); 2724 h &= JS_ATOM_HASH_MASK; 2725 h1 = h & (rt->atom_hash_size - 1); 2726 i = rt->atom_hash[h1]; 2727 while (i != 0) { 2728 p = rt->atom_array[i]; 2729 if (p->hash == h && 2730 p->atom_type == atom_type && 2731 p->len == len && 2732 js_string_memcmp(p, str, len) == 0) { 2733 if (!__JS_AtomIsConst(i)) 2734 p->header.ref_count++; 2735 goto done; 2736 } 2737 i = p->hash_next; 2738 } 2739 } else { 2740 h1 = 0; /* avoid warning */ 2741 if (atom_type == JS_ATOM_TYPE_SYMBOL) { 2742 h = JS_ATOM_HASH_SYMBOL; 2743 } else { 2744 h = JS_ATOM_HASH_PRIVATE; 2745 atom_type = JS_ATOM_TYPE_SYMBOL; 2746 } 2747 } 2748 2749 if (rt->atom_free_index == 0) { 2750 /* allow new atom entries */ 2751 uint32_t new_size, start; 2752 JSAtomStruct **new_array; 2753 2754 /* alloc new with size progression 3/2: 2755 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 2756 preallocating space for predefined atoms (at least 195). 2757 */ 2758 new_size = max_int(211, rt->atom_size * 3 / 2); 2759 if (new_size > JS_ATOM_MAX) 2760 goto fail; 2761 /* XXX: should use realloc2 to use slack space */ 2762 new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); 2763 if (!new_array) 2764 goto fail; 2765 /* Note: the atom 0 is not used */ 2766 start = rt->atom_size; 2767 if (start == 0) { 2768 /* JS_ATOM_NULL entry */ 2769 p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); 2770 if (!p) { 2771 js_free_rt(rt, new_array); 2772 goto fail; 2773 } 2774 p->header.ref_count = 1; /* not refcounted */ 2775 p->atom_type = JS_ATOM_TYPE_SYMBOL; 2776 #ifdef DUMP_LEAKS 2777 list_add_tail(&p->link, &rt->string_list); 2778 #endif 2779 new_array[0] = p; 2780 rt->atom_count++; 2781 start = 1; 2782 } 2783 rt->atom_size = new_size; 2784 rt->atom_array = new_array; 2785 rt->atom_free_index = start; 2786 for(i = start; i < new_size; i++) { 2787 uint32_t next; 2788 if (i == (new_size - 1)) 2789 next = 0; 2790 else 2791 next = i + 1; 2792 rt->atom_array[i] = atom_set_free(next); 2793 } 2794 } 2795 2796 if (str) { 2797 if (str->atom_type == 0) { 2798 p = str; 2799 p->atom_type = atom_type; 2800 } else { 2801 p = js_malloc_rt(rt, sizeof(JSString) + 2802 (str->len << str->is_wide_char) + 2803 1 - str->is_wide_char); 2804 if (unlikely(!p)) 2805 goto fail; 2806 p->header.ref_count = 1; 2807 p->is_wide_char = str->is_wide_char; 2808 p->len = str->len; 2809 #ifdef DUMP_LEAKS 2810 list_add_tail(&p->link, &rt->string_list); 2811 #endif 2812 memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + 2813 1 - str->is_wide_char); 2814 js_free_string(rt, str); 2815 } 2816 } else { 2817 p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ 2818 if (!p) 2819 return JS_ATOM_NULL; 2820 p->header.ref_count = 1; 2821 p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ 2822 p->len = 0; 2823 #ifdef DUMP_LEAKS 2824 list_add_tail(&p->link, &rt->string_list); 2825 #endif 2826 } 2827 2828 /* use an already free entry */ 2829 i = rt->atom_free_index; 2830 rt->atom_free_index = atom_get_free(rt->atom_array[i]); 2831 rt->atom_array[i] = p; 2832 2833 p->hash = h; 2834 p->hash_next = i; /* atom_index */ 2835 p->atom_type = atom_type; 2836 2837 rt->atom_count++; 2838 2839 if (atom_type != JS_ATOM_TYPE_SYMBOL) { 2840 p->hash_next = rt->atom_hash[h1]; 2841 rt->atom_hash[h1] = i; 2842 if (unlikely(rt->atom_count >= rt->atom_count_resize)) 2843 JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); 2844 } 2845 2846 // JS_DumpAtoms(rt); 2847 return i; 2848 2849 fail: 2850 i = JS_ATOM_NULL; 2851 done: 2852 if (str) 2853 js_free_string(rt, str); 2854 return i; 2855 } 2856 2857 /* only works with zero terminated 8 bit strings */ 2858 static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, 2859 int atom_type) 2860 { 2861 JSString *p; 2862 p = js_alloc_string_rt(rt, len, 0); 2863 if (!p) 2864 return JS_ATOM_NULL; 2865 memcpy(p->u.str8, str, len); 2866 p->u.str8[len] = '\0'; 2867 return __JS_NewAtom(rt, p, atom_type); 2868 } 2869 2870 /* Warning: str must be ASCII only */ 2871 static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, 2872 int atom_type) 2873 { 2874 uint32_t h, h1, i; 2875 JSAtomStruct *p; 2876 2877 h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); 2878 h &= JS_ATOM_HASH_MASK; 2879 h1 = h & (rt->atom_hash_size - 1); 2880 i = rt->atom_hash[h1]; 2881 while (i != 0) { 2882 p = rt->atom_array[i]; 2883 if (p->hash == h && 2884 p->atom_type == JS_ATOM_TYPE_STRING && 2885 p->len == len && 2886 p->is_wide_char == 0 && 2887 memcmp(p->u.str8, str, len) == 0) { 2888 if (!__JS_AtomIsConst(i)) 2889 p->header.ref_count++; 2890 return i; 2891 } 2892 i = p->hash_next; 2893 } 2894 return JS_ATOM_NULL; 2895 } 2896 2897 static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) 2898 { 2899 #if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */ 2900 if (unlikely(i == JS_ATOM_NULL)) { 2901 p->header.ref_count = INT32_MAX / 2; 2902 return; 2903 } 2904 #endif 2905 uint32_t i = p->hash_next; /* atom_index */ 2906 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { 2907 JSAtomStruct *p0, *p1; 2908 uint32_t h0; 2909 2910 h0 = p->hash & (rt->atom_hash_size - 1); 2911 i = rt->atom_hash[h0]; 2912 p1 = rt->atom_array[i]; 2913 if (p1 == p) { 2914 rt->atom_hash[h0] = p1->hash_next; 2915 } else { 2916 for(;;) { 2917 assert(i != 0); 2918 p0 = p1; 2919 i = p1->hash_next; 2920 p1 = rt->atom_array[i]; 2921 if (p1 == p) { 2922 p0->hash_next = p1->hash_next; 2923 break; 2924 } 2925 } 2926 } 2927 } 2928 /* insert in free atom list */ 2929 rt->atom_array[i] = atom_set_free(rt->atom_free_index); 2930 rt->atom_free_index = i; 2931 /* free the string structure */ 2932 #ifdef DUMP_LEAKS 2933 list_del(&p->link); 2934 #endif 2935 js_free_rt(rt, p); 2936 rt->atom_count--; 2937 assert(rt->atom_count >= 0); 2938 } 2939 2940 static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) 2941 { 2942 JSAtomStruct *p; 2943 2944 p = rt->atom_array[i]; 2945 if (--p->header.ref_count > 0) 2946 return; 2947 JS_FreeAtomStruct(rt, p); 2948 } 2949 2950 /* Warning: 'p' is freed */ 2951 static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) 2952 { 2953 JSRuntime *rt = ctx->rt; 2954 uint32_t n; 2955 if (is_num_string(&n, p)) { 2956 if (n <= JS_ATOM_MAX_INT) { 2957 js_free_string(rt, p); 2958 return __JS_AtomFromUInt32(n); 2959 } 2960 } 2961 /* XXX: should generate an exception */ 2962 return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); 2963 } 2964 2965 /* str is UTF-8 encoded */ 2966 JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) 2967 { 2968 JSValue val; 2969 2970 if (len == 0 || !is_digit(*str)) { 2971 // XXX: this will not work if UTF-8 encoded str contains non ASCII bytes 2972 JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); 2973 if (atom) 2974 return atom; 2975 } 2976 val = JS_NewStringLen(ctx, str, len); 2977 if (JS_IsException(val)) 2978 return JS_ATOM_NULL; 2979 return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); 2980 } 2981 2982 JSAtom JS_NewAtom(JSContext *ctx, const char *str) 2983 { 2984 return JS_NewAtomLen(ctx, str, strlen(str)); 2985 } 2986 2987 JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) 2988 { 2989 if (n <= JS_ATOM_MAX_INT) { 2990 return __JS_AtomFromUInt32(n); 2991 } else { 2992 char buf[11]; 2993 JSValue val; 2994 snprintf(buf, sizeof(buf), "%u", n); 2995 val = JS_NewString(ctx, buf); 2996 if (JS_IsException(val)) 2997 return JS_ATOM_NULL; 2998 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), 2999 JS_ATOM_TYPE_STRING); 3000 } 3001 } 3002 3003 static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) 3004 { 3005 if ((uint64_t)n <= JS_ATOM_MAX_INT) { 3006 return __JS_AtomFromUInt32((uint32_t)n); 3007 } else { 3008 char buf[24]; 3009 JSValue val; 3010 snprintf(buf, sizeof(buf), "%" PRId64 , n); 3011 val = JS_NewString(ctx, buf); 3012 if (JS_IsException(val)) 3013 return JS_ATOM_NULL; 3014 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), 3015 JS_ATOM_TYPE_STRING); 3016 } 3017 } 3018 3019 /* 'p' is freed */ 3020 static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) 3021 { 3022 JSRuntime *rt = ctx->rt; 3023 JSAtom atom; 3024 atom = __JS_NewAtom(rt, p, atom_type); 3025 if (atom == JS_ATOM_NULL) 3026 return JS_ThrowOutOfMemory(ctx); 3027 return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); 3028 } 3029 3030 /* descr must be a non-numeric string atom */ 3031 static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, 3032 int atom_type) 3033 { 3034 JSRuntime *rt = ctx->rt; 3035 JSString *p; 3036 3037 assert(!__JS_AtomIsTaggedInt(descr)); 3038 assert(descr < rt->atom_size); 3039 p = rt->atom_array[descr]; 3040 JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3041 return JS_NewSymbol(ctx, p, atom_type); 3042 } 3043 3044 #define ATOM_GET_STR_BUF_SIZE 64 3045 3046 /* Should only be used for debug. */ 3047 static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, 3048 JSAtom atom) 3049 { 3050 if (__JS_AtomIsTaggedInt(atom)) { 3051 snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); 3052 } else { 3053 JSAtomStruct *p; 3054 assert(atom < rt->atom_size); 3055 if (atom == JS_ATOM_NULL) { 3056 snprintf(buf, buf_size, "<null>"); 3057 } else { 3058 int i, c; 3059 char *q; 3060 JSString *str; 3061 3062 q = buf; 3063 p = rt->atom_array[atom]; 3064 assert(!atom_is_free(p)); 3065 str = p; 3066 if (str) { 3067 if (!str->is_wide_char) { 3068 /* special case ASCII strings */ 3069 c = 0; 3070 for(i = 0; i < str->len; i++) { 3071 c |= str->u.str8[i]; 3072 } 3073 if (c < 0x80) 3074 return (const char *)str->u.str8; 3075 } 3076 for(i = 0; i < str->len; i++) { 3077 c = string_get(str, i); 3078 if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) 3079 break; 3080 if (c < 128) { 3081 *q++ = c; 3082 } else { 3083 q += unicode_to_utf8((uint8_t *)q, c); 3084 } 3085 } 3086 } 3087 *q = '\0'; 3088 } 3089 } 3090 return buf; 3091 } 3092 3093 static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) 3094 { 3095 return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); 3096 } 3097 3098 static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) 3099 { 3100 char buf[ATOM_GET_STR_BUF_SIZE]; 3101 3102 if (__JS_AtomIsTaggedInt(atom)) { 3103 snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); 3104 return JS_NewString(ctx, buf); 3105 } else { 3106 JSRuntime *rt = ctx->rt; 3107 JSAtomStruct *p; 3108 assert(atom < rt->atom_size); 3109 p = rt->atom_array[atom]; 3110 if (p->atom_type == JS_ATOM_TYPE_STRING) { 3111 goto ret_string; 3112 } else if (force_string) { 3113 if (p->len == 0 && p->is_wide_char != 0) { 3114 /* no description string */ 3115 p = rt->atom_array[JS_ATOM_empty_string]; 3116 } 3117 ret_string: 3118 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3119 } else { 3120 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p)); 3121 } 3122 } 3123 } 3124 3125 JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) 3126 { 3127 return __JS_AtomToValue(ctx, atom, FALSE); 3128 } 3129 3130 JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) 3131 { 3132 return __JS_AtomToValue(ctx, atom, TRUE); 3133 } 3134 3135 /* return TRUE if the atom is an array index (i.e. 0 <= index <= 3136 2^32-2 and return its value */ 3137 static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) 3138 { 3139 if (__JS_AtomIsTaggedInt(atom)) { 3140 *pval = __JS_AtomToUInt32(atom); 3141 return TRUE; 3142 } else { 3143 JSRuntime *rt = ctx->rt; 3144 JSAtomStruct *p; 3145 uint32_t val; 3146 3147 assert(atom < rt->atom_size); 3148 p = rt->atom_array[atom]; 3149 if (p->atom_type == JS_ATOM_TYPE_STRING && 3150 is_num_string(&val, p) && val != -1) { 3151 *pval = val; 3152 return TRUE; 3153 } else { 3154 *pval = 0; 3155 return FALSE; 3156 } 3157 } 3158 } 3159 3160 /* This test must be fast if atom is not a numeric index (e.g. a 3161 method name). Return JS_UNDEFINED if not a numeric 3162 index. JS_EXCEPTION can also be returned. */ 3163 static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) 3164 { 3165 JSRuntime *rt = ctx->rt; 3166 JSAtomStruct *p1; 3167 JSString *p; 3168 int c, len, ret; 3169 JSValue num, str; 3170 3171 if (__JS_AtomIsTaggedInt(atom)) 3172 return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); 3173 assert(atom < rt->atom_size); 3174 p1 = rt->atom_array[atom]; 3175 if (p1->atom_type != JS_ATOM_TYPE_STRING) 3176 return JS_UNDEFINED; 3177 p = p1; 3178 len = p->len; 3179 if (p->is_wide_char) { 3180 const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; 3181 if (r >= r_end) 3182 return JS_UNDEFINED; 3183 c = *r; 3184 if (c == '-') { 3185 if (r >= r_end) 3186 return JS_UNDEFINED; 3187 r++; 3188 c = *r; 3189 /* -0 case is specific */ 3190 if (c == '0' && len == 2) 3191 goto minus_zero; 3192 } 3193 /* XXX: should test NaN, but the tests do not check it */ 3194 if (!is_num(c)) { 3195 /* XXX: String should be normalized, therefore 8-bit only */ 3196 const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; 3197 if (!(c =='I' && (r_end - r) == 8 && 3198 !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) 3199 return JS_UNDEFINED; 3200 } 3201 } else { 3202 const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; 3203 if (r >= r_end) 3204 return JS_UNDEFINED; 3205 c = *r; 3206 if (c == '-') { 3207 if (r >= r_end) 3208 return JS_UNDEFINED; 3209 r++; 3210 c = *r; 3211 /* -0 case is specific */ 3212 if (c == '0' && len == 2) { 3213 minus_zero: 3214 return __JS_NewFloat64(ctx, -0.0); 3215 } 3216 } 3217 if (!is_num(c)) { 3218 if (!(c =='I' && (r_end - r) == 8 && 3219 !memcmp(r + 1, "nfinity", 7))) 3220 return JS_UNDEFINED; 3221 } 3222 } 3223 /* XXX: bignum: would be better to only accept integer to avoid 3224 relying on current floating point precision */ 3225 /* this is ECMA CanonicalNumericIndexString primitive */ 3226 num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3227 if (JS_IsException(num)) 3228 return num; 3229 str = JS_ToString(ctx, num); 3230 if (JS_IsException(str)) { 3231 JS_FreeValue(ctx, num); 3232 return str; 3233 } 3234 ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); 3235 JS_FreeValue(ctx, str); 3236 if (ret == 0) { 3237 return num; 3238 } else { 3239 JS_FreeValue(ctx, num); 3240 return JS_UNDEFINED; 3241 } 3242 } 3243 3244 /* return -1 if exception or TRUE/FALSE */ 3245 static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) 3246 { 3247 JSValue num; 3248 num = JS_AtomIsNumericIndex1(ctx, atom); 3249 if (likely(JS_IsUndefined(num))) 3250 return FALSE; 3251 if (JS_IsException(num)) 3252 return -1; 3253 JS_FreeValue(ctx, num); 3254 return TRUE; 3255 } 3256 3257 void JS_FreeAtom(JSContext *ctx, JSAtom v) 3258 { 3259 if (!__JS_AtomIsConst(v)) 3260 __JS_FreeAtom(ctx->rt, v); 3261 } 3262 3263 void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) 3264 { 3265 if (!__JS_AtomIsConst(v)) 3266 __JS_FreeAtom(rt, v); 3267 } 3268 3269 /* return TRUE if 'v' is a symbol with a string description */ 3270 static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) 3271 { 3272 JSRuntime *rt; 3273 JSAtomStruct *p; 3274 3275 rt = ctx->rt; 3276 if (__JS_AtomIsTaggedInt(v)) 3277 return FALSE; 3278 p = rt->atom_array[v]; 3279 return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && 3280 p->hash == JS_ATOM_HASH_SYMBOL) || 3281 p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && 3282 !(p->len == 0 && p->is_wide_char != 0)); 3283 } 3284 3285 static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) 3286 { 3287 char buf[ATOM_GET_STR_BUF_SIZE]; 3288 const char *p; 3289 int i; 3290 3291 /* XXX: should handle embedded null characters */ 3292 /* XXX: should move encoding code to JS_AtomGetStr */ 3293 p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); 3294 for (i = 0; p[i]; i++) { 3295 int c = (unsigned char)p[i]; 3296 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 3297 (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) 3298 break; 3299 } 3300 if (i > 0 && p[i] == '\0') { 3301 printf("%s", p); 3302 } else { 3303 putchar('"'); 3304 printf("%.*s", i, p); 3305 for (; p[i]; i++) { 3306 int c = (unsigned char)p[i]; 3307 if (c == '\"' || c == '\\') { 3308 putchar('\\'); 3309 putchar(c); 3310 } else if (c >= ' ' && c <= 126) { 3311 putchar(c); 3312 } else if (c == '\n') { 3313 putchar('\\'); 3314 putchar('n'); 3315 } else { 3316 printf("\\u%04x", c); 3317 } 3318 } 3319 putchar('\"'); 3320 } 3321 } 3322 3323 /* free with JS_FreeCString() */ 3324 const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) 3325 { 3326 JSValue str; 3327 const char *cstr; 3328 3329 str = JS_AtomToString(ctx, atom); 3330 if (JS_IsException(str)) 3331 return NULL; 3332 cstr = JS_ToCString(ctx, str); 3333 JS_FreeValue(ctx, str); 3334 return cstr; 3335 } 3336 3337 /* return a string atom containing name concatenated with str1 */ 3338 static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) 3339 { 3340 JSValue str; 3341 JSAtom atom; 3342 const char *cstr; 3343 char *cstr2; 3344 size_t len, len1; 3345 3346 str = JS_AtomToString(ctx, name); 3347 if (JS_IsException(str)) 3348 return JS_ATOM_NULL; 3349 cstr = JS_ToCStringLen(ctx, &len, str); 3350 if (!cstr) 3351 goto fail; 3352 len1 = strlen(str1); 3353 cstr2 = js_malloc(ctx, len + len1 + 1); 3354 if (!cstr2) 3355 goto fail; 3356 memcpy(cstr2, cstr, len); 3357 memcpy(cstr2 + len, str1, len1); 3358 cstr2[len + len1] = '\0'; 3359 atom = JS_NewAtomLen(ctx, cstr2, len + len1); 3360 js_free(ctx, cstr2); 3361 JS_FreeCString(ctx, cstr); 3362 JS_FreeValue(ctx, str); 3363 return atom; 3364 fail: 3365 JS_FreeCString(ctx, cstr); 3366 JS_FreeValue(ctx, str); 3367 return JS_ATOM_NULL; 3368 } 3369 3370 static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) 3371 { 3372 char buf[16]; 3373 snprintf(buf, sizeof(buf), "%u", n); 3374 return js_atom_concat_str(ctx, name, buf); 3375 } 3376 3377 static inline BOOL JS_IsEmptyString(JSValueConst v) 3378 { 3379 return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; 3380 } 3381 3382 /* JSClass support */ 3383 3384 #ifdef CONFIG_ATOMICS 3385 static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER; 3386 #endif 3387 3388 /* a new class ID is allocated if *pclass_id != 0 */ 3389 JSClassID JS_NewClassID(JSClassID *pclass_id) 3390 { 3391 JSClassID class_id; 3392 #ifdef CONFIG_ATOMICS 3393 pthread_mutex_lock(&js_class_id_mutex); 3394 #endif 3395 class_id = *pclass_id; 3396 if (class_id == 0) { 3397 class_id = js_class_id_alloc++; 3398 *pclass_id = class_id; 3399 } 3400 #ifdef CONFIG_ATOMICS 3401 pthread_mutex_unlock(&js_class_id_mutex); 3402 #endif 3403 return class_id; 3404 } 3405 3406 JSClassID JS_GetClassID(JSValue v) 3407 { 3408 JSObject *p; 3409 if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) 3410 return JS_INVALID_CLASS_ID; 3411 p = JS_VALUE_GET_OBJ(v); 3412 return p->class_id; 3413 } 3414 3415 BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) 3416 { 3417 return (class_id < rt->class_count && 3418 rt->class_array[class_id].class_id != 0); 3419 } 3420 3421 /* create a new object internal class. Return -1 if error, 0 if 3422 OK. The finalizer can be NULL if none is needed. */ 3423 static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, 3424 const JSClassDef *class_def, JSAtom name) 3425 { 3426 int new_size, i; 3427 JSClass *cl, *new_class_array; 3428 struct list_head *el; 3429 3430 if (class_id >= (1 << 16)) 3431 return -1; 3432 if (class_id < rt->class_count && 3433 rt->class_array[class_id].class_id != 0) 3434 return -1; 3435 3436 if (class_id >= rt->class_count) { 3437 new_size = max_int(JS_CLASS_INIT_COUNT, 3438 max_int(class_id + 1, rt->class_count * 3 / 2)); 3439 3440 /* reallocate the context class prototype array, if any */ 3441 list_for_each(el, &rt->context_list) { 3442 JSContext *ctx = list_entry(el, JSContext, link); 3443 JSValue *new_tab; 3444 new_tab = js_realloc_rt(rt, ctx->class_proto, 3445 sizeof(ctx->class_proto[0]) * new_size); 3446 if (!new_tab) 3447 return -1; 3448 for(i = rt->class_count; i < new_size; i++) 3449 new_tab[i] = JS_NULL; 3450 ctx->class_proto = new_tab; 3451 } 3452 /* reallocate the class array */ 3453 new_class_array = js_realloc_rt(rt, rt->class_array, 3454 sizeof(JSClass) * new_size); 3455 if (!new_class_array) 3456 return -1; 3457 memset(new_class_array + rt->class_count, 0, 3458 (new_size - rt->class_count) * sizeof(JSClass)); 3459 rt->class_array = new_class_array; 3460 rt->class_count = new_size; 3461 } 3462 cl = &rt->class_array[class_id]; 3463 cl->class_id = class_id; 3464 cl->class_name = JS_DupAtomRT(rt, name); 3465 cl->finalizer = class_def->finalizer; 3466 cl->gc_mark = class_def->gc_mark; 3467 cl->call = class_def->call; 3468 cl->exotic = class_def->exotic; 3469 return 0; 3470 } 3471 3472 int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) 3473 { 3474 int ret, len; 3475 JSAtom name; 3476 3477 len = strlen(class_def->class_name); 3478 name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); 3479 if (name == JS_ATOM_NULL) { 3480 name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); 3481 if (name == JS_ATOM_NULL) 3482 return -1; 3483 } 3484 ret = JS_NewClass1(rt, class_id, class_def, name); 3485 JS_FreeAtomRT(rt, name); 3486 return ret; 3487 } 3488 3489 static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len) 3490 { 3491 JSString *str; 3492 3493 if (len <= 0) { 3494 return JS_AtomToString(ctx, JS_ATOM_empty_string); 3495 } 3496 str = js_alloc_string(ctx, len, 0); 3497 if (!str) 3498 return JS_EXCEPTION; 3499 memcpy(str->u.str8, buf, len); 3500 str->u.str8[len] = '\0'; 3501 return JS_MKPTR(JS_TAG_STRING, str); 3502 } 3503 3504 static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len) 3505 { 3506 JSString *str; 3507 str = js_alloc_string(ctx, len, 1); 3508 if (!str) 3509 return JS_EXCEPTION; 3510 memcpy(str->u.str16, buf, len * 2); 3511 return JS_MKPTR(JS_TAG_STRING, str); 3512 } 3513 3514 static JSValue js_new_string_char(JSContext *ctx, uint16_t c) 3515 { 3516 if (c < 0x100) { 3517 uint8_t ch8 = c; 3518 return js_new_string8(ctx, &ch8, 1); 3519 } else { 3520 uint16_t ch16 = c; 3521 return js_new_string16(ctx, &ch16, 1); 3522 } 3523 } 3524 3525 static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) 3526 { 3527 int len = end - start; 3528 if (start == 0 && end == p->len) { 3529 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 3530 } 3531 if (p->is_wide_char && len > 0) { 3532 JSString *str; 3533 int i; 3534 uint16_t c = 0; 3535 for (i = start; i < end; i++) { 3536 c |= p->u.str16[i]; 3537 } 3538 if (c > 0xFF) 3539 return js_new_string16(ctx, p->u.str16 + start, len); 3540 3541 str = js_alloc_string(ctx, len, 0); 3542 if (!str) 3543 return JS_EXCEPTION; 3544 for (i = 0; i < len; i++) { 3545 str->u.str8[i] = p->u.str16[start + i]; 3546 } 3547 str->u.str8[len] = '\0'; 3548 return JS_MKPTR(JS_TAG_STRING, str); 3549 } else { 3550 return js_new_string8(ctx, p->u.str8 + start, len); 3551 } 3552 } 3553 3554 typedef struct StringBuffer { 3555 JSContext *ctx; 3556 JSString *str; 3557 int len; 3558 int size; 3559 int is_wide_char; 3560 int error_status; 3561 } StringBuffer; 3562 3563 /* It is valid to call string_buffer_end() and all string_buffer functions even 3564 if string_buffer_init() or another string_buffer function returns an error. 3565 If the error_status is set, string_buffer_end() returns JS_EXCEPTION. 3566 */ 3567 static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, 3568 int is_wide) 3569 { 3570 s->ctx = ctx; 3571 s->size = size; 3572 s->len = 0; 3573 s->is_wide_char = is_wide; 3574 s->error_status = 0; 3575 s->str = js_alloc_string(ctx, size, is_wide); 3576 if (unlikely(!s->str)) { 3577 s->size = 0; 3578 return s->error_status = -1; 3579 } 3580 #ifdef DUMP_LEAKS 3581 /* the StringBuffer may reallocate the JSString, only link it at the end */ 3582 list_del(&s->str->link); 3583 #endif 3584 return 0; 3585 } 3586 3587 static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) 3588 { 3589 return string_buffer_init2(ctx, s, size, 0); 3590 } 3591 3592 static void string_buffer_free(StringBuffer *s) 3593 { 3594 js_free(s->ctx, s->str); 3595 s->str = NULL; 3596 } 3597 3598 static int string_buffer_set_error(StringBuffer *s) 3599 { 3600 js_free(s->ctx, s->str); 3601 s->str = NULL; 3602 s->size = 0; 3603 s->len = 0; 3604 return s->error_status = -1; 3605 } 3606 3607 static no_inline int string_buffer_widen(StringBuffer *s, int size) 3608 { 3609 JSString *str; 3610 size_t slack; 3611 int i; 3612 3613 if (s->error_status) 3614 return -1; 3615 3616 str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); 3617 if (!str) 3618 return string_buffer_set_error(s); 3619 size += slack >> 1; 3620 for(i = s->len; i-- > 0;) { 3621 str->u.str16[i] = str->u.str8[i]; 3622 } 3623 s->is_wide_char = 1; 3624 s->size = size; 3625 s->str = str; 3626 return 0; 3627 } 3628 3629 static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) 3630 { 3631 JSString *new_str; 3632 int new_size; 3633 size_t new_size_bytes, slack; 3634 3635 if (s->error_status) 3636 return -1; 3637 3638 if (new_len > JS_STRING_LEN_MAX) { 3639 JS_ThrowInternalError(s->ctx, "string too long"); 3640 return string_buffer_set_error(s); 3641 } 3642 new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); 3643 if (!s->is_wide_char && c >= 0x100) { 3644 return string_buffer_widen(s, new_size); 3645 } 3646 new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; 3647 new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); 3648 if (!new_str) 3649 return string_buffer_set_error(s); 3650 new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); 3651 s->size = new_size; 3652 s->str = new_str; 3653 return 0; 3654 } 3655 3656 static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) 3657 { 3658 if (unlikely(s->len >= s->size)) { 3659 if (string_buffer_realloc(s, s->len + 1, c)) 3660 return -1; 3661 } 3662 if (s->is_wide_char) { 3663 s->str->u.str16[s->len++] = c; 3664 } else if (c < 0x100) { 3665 s->str->u.str8[s->len++] = c; 3666 } else { 3667 if (string_buffer_widen(s, s->size)) 3668 return -1; 3669 s->str->u.str16[s->len++] = c; 3670 } 3671 return 0; 3672 } 3673 3674 /* 0 <= c <= 0xff */ 3675 static int string_buffer_putc8(StringBuffer *s, uint32_t c) 3676 { 3677 if (unlikely(s->len >= s->size)) { 3678 if (string_buffer_realloc(s, s->len + 1, c)) 3679 return -1; 3680 } 3681 if (s->is_wide_char) { 3682 s->str->u.str16[s->len++] = c; 3683 } else { 3684 s->str->u.str8[s->len++] = c; 3685 } 3686 return 0; 3687 } 3688 3689 /* 0 <= c <= 0xffff */ 3690 static int string_buffer_putc16(StringBuffer *s, uint32_t c) 3691 { 3692 if (likely(s->len < s->size)) { 3693 if (s->is_wide_char) { 3694 s->str->u.str16[s->len++] = c; 3695 return 0; 3696 } else if (c < 0x100) { 3697 s->str->u.str8[s->len++] = c; 3698 return 0; 3699 } 3700 } 3701 return string_buffer_putc_slow(s, c); 3702 } 3703 3704 /* 0 <= c <= 0x10ffff */ 3705 static int string_buffer_putc(StringBuffer *s, uint32_t c) 3706 { 3707 if (unlikely(c >= 0x10000)) { 3708 /* surrogate pair */ 3709 if (string_buffer_putc16(s, get_hi_surrogate(c))) 3710 return -1; 3711 c = get_lo_surrogate(c); 3712 } 3713 return string_buffer_putc16(s, c); 3714 } 3715 3716 static int string_getc(const JSString *p, int *pidx) 3717 { 3718 int idx, c, c1; 3719 idx = *pidx; 3720 if (p->is_wide_char) { 3721 c = p->u.str16[idx++]; 3722 if (is_hi_surrogate(c) && idx < p->len) { 3723 c1 = p->u.str16[idx]; 3724 if (is_lo_surrogate(c1)) { 3725 c = from_surrogate(c, c1); 3726 idx++; 3727 } 3728 } 3729 } else { 3730 c = p->u.str8[idx++]; 3731 } 3732 *pidx = idx; 3733 return c; 3734 } 3735 3736 static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) 3737 { 3738 int i; 3739 3740 if (s->len + len > s->size) { 3741 if (string_buffer_realloc(s, s->len + len, 0)) 3742 return -1; 3743 } 3744 if (s->is_wide_char) { 3745 for (i = 0; i < len; i++) { 3746 s->str->u.str16[s->len + i] = p[i]; 3747 } 3748 s->len += len; 3749 } else { 3750 memcpy(&s->str->u.str8[s->len], p, len); 3751 s->len += len; 3752 } 3753 return 0; 3754 } 3755 3756 static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) 3757 { 3758 int c = 0, i; 3759 3760 for (i = 0; i < len; i++) { 3761 c |= p[i]; 3762 } 3763 if (s->len + len > s->size) { 3764 if (string_buffer_realloc(s, s->len + len, c)) 3765 return -1; 3766 } else if (!s->is_wide_char && c >= 0x100) { 3767 if (string_buffer_widen(s, s->size)) 3768 return -1; 3769 } 3770 if (s->is_wide_char) { 3771 memcpy(&s->str->u.str16[s->len], p, len << 1); 3772 s->len += len; 3773 } else { 3774 for (i = 0; i < len; i++) { 3775 s->str->u.str8[s->len + i] = p[i]; 3776 } 3777 s->len += len; 3778 } 3779 return 0; 3780 } 3781 3782 /* appending an ASCII string */ 3783 static int string_buffer_puts8(StringBuffer *s, const char *str) 3784 { 3785 return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); 3786 } 3787 3788 static int string_buffer_concat(StringBuffer *s, const JSString *p, 3789 uint32_t from, uint32_t to) 3790 { 3791 if (to <= from) 3792 return 0; 3793 if (p->is_wide_char) 3794 return string_buffer_write16(s, p->u.str16 + from, to - from); 3795 else 3796 return string_buffer_write8(s, p->u.str8 + from, to - from); 3797 } 3798 3799 static int string_buffer_concat_value(StringBuffer *s, JSValueConst v) 3800 { 3801 JSString *p; 3802 JSValue v1; 3803 int res; 3804 3805 if (s->error_status) { 3806 /* prevent exception overload */ 3807 return -1; 3808 } 3809 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { 3810 v1 = JS_ToString(s->ctx, v); 3811 if (JS_IsException(v1)) 3812 return string_buffer_set_error(s); 3813 p = JS_VALUE_GET_STRING(v1); 3814 res = string_buffer_concat(s, p, 0, p->len); 3815 JS_FreeValue(s->ctx, v1); 3816 return res; 3817 } 3818 p = JS_VALUE_GET_STRING(v); 3819 return string_buffer_concat(s, p, 0, p->len); 3820 } 3821 3822 static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) 3823 { 3824 JSString *p; 3825 int res; 3826 3827 if (s->error_status) { 3828 /* prevent exception overload */ 3829 JS_FreeValue(s->ctx, v); 3830 return -1; 3831 } 3832 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { 3833 v = JS_ToStringFree(s->ctx, v); 3834 if (JS_IsException(v)) 3835 return string_buffer_set_error(s); 3836 } 3837 p = JS_VALUE_GET_STRING(v); 3838 res = string_buffer_concat(s, p, 0, p->len); 3839 JS_FreeValue(s->ctx, v); 3840 return res; 3841 } 3842 3843 static int string_buffer_fill(StringBuffer *s, int c, int count) 3844 { 3845 /* XXX: optimize */ 3846 if (s->len + count > s->size) { 3847 if (string_buffer_realloc(s, s->len + count, c)) 3848 return -1; 3849 } 3850 while (count-- > 0) { 3851 if (string_buffer_putc16(s, c)) 3852 return -1; 3853 } 3854 return 0; 3855 } 3856 3857 static JSValue string_buffer_end(StringBuffer *s) 3858 { 3859 JSString *str; 3860 str = s->str; 3861 if (s->error_status) 3862 return JS_EXCEPTION; 3863 if (s->len == 0) { 3864 js_free(s->ctx, str); 3865 s->str = NULL; 3866 return JS_AtomToString(s->ctx, JS_ATOM_empty_string); 3867 } 3868 if (s->len < s->size) { 3869 /* smaller size so js_realloc should not fail, but OK if it does */ 3870 /* XXX: should add some slack to avoid unnecessary calls */ 3871 /* XXX: might need to use malloc+free to ensure smaller size */ 3872 str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + 3873 (s->len << s->is_wide_char) + 1 - s->is_wide_char); 3874 if (str == NULL) 3875 str = s->str; 3876 s->str = str; 3877 } 3878 if (!s->is_wide_char) 3879 str->u.str8[s->len] = 0; 3880 #ifdef DUMP_LEAKS 3881 list_add_tail(&str->link, &s->ctx->rt->string_list); 3882 #endif 3883 str->is_wide_char = s->is_wide_char; 3884 str->len = s->len; 3885 s->str = NULL; 3886 return JS_MKPTR(JS_TAG_STRING, str); 3887 } 3888 3889 /* create a string from a UTF-8 buffer */ 3890 JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) 3891 { 3892 const uint8_t *p, *p_end, *p_start, *p_next; 3893 uint32_t c; 3894 StringBuffer b_s, *b = &b_s; 3895 size_t len1; 3896 3897 p_start = (const uint8_t *)buf; 3898 p_end = p_start + buf_len; 3899 p = p_start; 3900 while (p < p_end && *p < 128) 3901 p++; 3902 len1 = p - p_start; 3903 if (len1 > JS_STRING_LEN_MAX) 3904 return JS_ThrowInternalError(ctx, "string too long"); 3905 if (p == p_end) { 3906 /* ASCII string */ 3907 return js_new_string8(ctx, (const uint8_t *)buf, buf_len); 3908 } else { 3909 if (string_buffer_init(ctx, b, buf_len)) 3910 goto fail; 3911 string_buffer_write8(b, p_start, len1); 3912 while (p < p_end) { 3913 if (*p < 128) { 3914 string_buffer_putc8(b, *p++); 3915 } else { 3916 /* parse utf-8 sequence, return 0xFFFFFFFF for error */ 3917 c = unicode_from_utf8(p, p_end - p, &p_next); 3918 if (c < 0x10000) { 3919 p = p_next; 3920 } else if (c <= 0x10FFFF) { 3921 p = p_next; 3922 /* surrogate pair */ 3923 string_buffer_putc16(b, get_hi_surrogate(c)); 3924 c = get_lo_surrogate(c); 3925 } else { 3926 /* invalid char */ 3927 c = 0xfffd; 3928 /* skip the invalid chars */ 3929 /* XXX: seems incorrect. Why not just use c = *p++; ? */ 3930 while (p < p_end && (*p >= 0x80 && *p < 0xc0)) 3931 p++; 3932 if (p < p_end) { 3933 p++; 3934 while (p < p_end && (*p >= 0x80 && *p < 0xc0)) 3935 p++; 3936 } 3937 } 3938 string_buffer_putc16(b, c); 3939 } 3940 } 3941 } 3942 return string_buffer_end(b); 3943 3944 fail: 3945 string_buffer_free(b); 3946 return JS_EXCEPTION; 3947 } 3948 3949 static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, 3950 JSValue str2, const char *str3) 3951 { 3952 StringBuffer b_s, *b = &b_s; 3953 int len1, len3; 3954 JSString *p; 3955 3956 if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { 3957 str2 = JS_ToStringFree(ctx, str2); 3958 if (JS_IsException(str2)) 3959 goto fail; 3960 } 3961 p = JS_VALUE_GET_STRING(str2); 3962 len1 = strlen(str1); 3963 len3 = strlen(str3); 3964 3965 if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) 3966 goto fail; 3967 3968 string_buffer_write8(b, (const uint8_t *)str1, len1); 3969 string_buffer_concat(b, p, 0, p->len); 3970 string_buffer_write8(b, (const uint8_t *)str3, len3); 3971 3972 JS_FreeValue(ctx, str2); 3973 return string_buffer_end(b); 3974 3975 fail: 3976 JS_FreeValue(ctx, str2); 3977 return JS_EXCEPTION; 3978 } 3979 3980 JSValue JS_NewString(JSContext *ctx, const char *str) 3981 { 3982 return JS_NewStringLen(ctx, str, strlen(str)); 3983 } 3984 3985 JSValue JS_NewAtomString(JSContext *ctx, const char *str) 3986 { 3987 JSAtom atom = JS_NewAtom(ctx, str); 3988 if (atom == JS_ATOM_NULL) 3989 return JS_EXCEPTION; 3990 JSValue val = JS_AtomToString(ctx, atom); 3991 JS_FreeAtom(ctx, atom); 3992 return val; 3993 } 3994 3995 /* return (NULL, 0) if exception. */ 3996 /* return pointer into a JSString with a live ref_count */ 3997 /* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ 3998 const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8) 3999 { 4000 JSValue val; 4001 JSString *str, *str_new; 4002 int pos, len, c, c1; 4003 uint8_t *q; 4004 4005 if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) { 4006 val = JS_ToString(ctx, val1); 4007 if (JS_IsException(val)) 4008 goto fail; 4009 } else { 4010 val = JS_DupValue(ctx, val1); 4011 } 4012 4013 str = JS_VALUE_GET_STRING(val); 4014 len = str->len; 4015 if (!str->is_wide_char) { 4016 const uint8_t *src = str->u.str8; 4017 int count; 4018 4019 /* count the number of non-ASCII characters */ 4020 /* Scanning the whole string is required for ASCII strings, 4021 and computing the number of non-ASCII bytes is less expensive 4022 than testing each byte, hence this method is faster for ASCII 4023 strings, which is the most common case. 4024 */ 4025 count = 0; 4026 for (pos = 0; pos < len; pos++) { 4027 count += src[pos] >> 7; 4028 } 4029 if (count == 0) { 4030 if (plen) 4031 *plen = len; 4032 return (const char *)src; 4033 } 4034 str_new = js_alloc_string(ctx, len + count, 0); 4035 if (!str_new) 4036 goto fail; 4037 q = str_new->u.str8; 4038 for (pos = 0; pos < len; pos++) { 4039 c = src[pos]; 4040 if (c < 0x80) { 4041 *q++ = c; 4042 } else { 4043 *q++ = (c >> 6) | 0xc0; 4044 *q++ = (c & 0x3f) | 0x80; 4045 } 4046 } 4047 } else { 4048 const uint16_t *src = str->u.str16; 4049 /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may 4050 produce 4 bytes but use 2 code points. 4051 */ 4052 str_new = js_alloc_string(ctx, len * 3, 0); 4053 if (!str_new) 4054 goto fail; 4055 q = str_new->u.str8; 4056 pos = 0; 4057 while (pos < len) { 4058 c = src[pos++]; 4059 if (c < 0x80) { 4060 *q++ = c; 4061 } else { 4062 if (is_hi_surrogate(c)) { 4063 if (pos < len && !cesu8) { 4064 c1 = src[pos]; 4065 if (is_lo_surrogate(c1)) { 4066 pos++; 4067 c = from_surrogate(c, c1); 4068 } else { 4069 /* Keep unmatched surrogate code points */ 4070 /* c = 0xfffd; */ /* error */ 4071 } 4072 } else { 4073 /* Keep unmatched surrogate code points */ 4074 /* c = 0xfffd; */ /* error */ 4075 } 4076 } 4077 q += unicode_to_utf8(q, c); 4078 } 4079 } 4080 } 4081 4082 *q = '\0'; 4083 str_new->len = q - str_new->u.str8; 4084 JS_FreeValue(ctx, val); 4085 if (plen) 4086 *plen = str_new->len; 4087 return (const char *)str_new->u.str8; 4088 fail: 4089 if (plen) 4090 *plen = 0; 4091 return NULL; 4092 } 4093 4094 void JS_FreeCString(JSContext *ctx, const char *ptr) 4095 { 4096 JSString *p; 4097 if (!ptr) 4098 return; 4099 /* purposely removing constness */ 4100 p = container_of(ptr, JSString, u); 4101 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 4102 } 4103 4104 static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) 4105 { 4106 int c, i; 4107 for(i = 0; i < len; i++) { 4108 c = src1[i] - src2[i]; 4109 if (c != 0) 4110 return c; 4111 } 4112 return 0; 4113 } 4114 4115 static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) 4116 { 4117 int c, i; 4118 for(i = 0; i < len; i++) { 4119 c = src1[i] - src2[i]; 4120 if (c != 0) 4121 return c; 4122 } 4123 return 0; 4124 } 4125 4126 static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) 4127 { 4128 int res; 4129 4130 if (likely(!p1->is_wide_char)) { 4131 if (likely(!p2->is_wide_char)) 4132 res = memcmp(p1->u.str8, p2->u.str8, len); 4133 else 4134 res = -memcmp16_8(p2->u.str16, p1->u.str8, len); 4135 } else { 4136 if (!p2->is_wide_char) 4137 res = memcmp16_8(p1->u.str16, p2->u.str8, len); 4138 else 4139 res = memcmp16(p1->u.str16, p2->u.str16, len); 4140 } 4141 return res; 4142 } 4143 4144 /* return < 0, 0 or > 0 */ 4145 static int js_string_compare(JSContext *ctx, 4146 const JSString *p1, const JSString *p2) 4147 { 4148 int res, len; 4149 len = min_int(p1->len, p2->len); 4150 res = js_string_memcmp(p1, p2, len); 4151 if (res == 0) { 4152 if (p1->len == p2->len) 4153 res = 0; 4154 else if (p1->len < p2->len) 4155 res = -1; 4156 else 4157 res = 1; 4158 } 4159 return res; 4160 } 4161 4162 static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) 4163 { 4164 if (p->is_wide_char) { 4165 memcpy(dst, p->u.str16 + offset, len * 2); 4166 } else { 4167 const uint8_t *src1 = p->u.str8 + offset; 4168 int i; 4169 4170 for(i = 0; i < len; i++) 4171 dst[i] = src1[i]; 4172 } 4173 } 4174 4175 static JSValue JS_ConcatString1(JSContext *ctx, 4176 const JSString *p1, const JSString *p2) 4177 { 4178 JSString *p; 4179 uint32_t len; 4180 int is_wide_char; 4181 4182 len = p1->len + p2->len; 4183 if (len > JS_STRING_LEN_MAX) 4184 return JS_ThrowInternalError(ctx, "string too long"); 4185 is_wide_char = p1->is_wide_char | p2->is_wide_char; 4186 p = js_alloc_string(ctx, len, is_wide_char); 4187 if (!p) 4188 return JS_EXCEPTION; 4189 if (!is_wide_char) { 4190 memcpy(p->u.str8, p1->u.str8, p1->len); 4191 memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); 4192 p->u.str8[len] = '\0'; 4193 } else { 4194 copy_str16(p->u.str16, p1, 0, p1->len); 4195 copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); 4196 } 4197 return JS_MKPTR(JS_TAG_STRING, p); 4198 } 4199 4200 static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { 4201 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { 4202 JSString *p2 = JS_VALUE_GET_STRING(op2); 4203 size_t size1; 4204 4205 if (p2->len == 0) 4206 return TRUE; 4207 if (p1->header.ref_count != 1) 4208 return FALSE; 4209 size1 = js_malloc_usable_size(ctx, p1); 4210 if (p1->is_wide_char) { 4211 if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { 4212 if (p2->is_wide_char) { 4213 memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); 4214 p1->len += p2->len; 4215 return TRUE; 4216 } else { 4217 size_t i; 4218 for (i = 0; i < p2->len; i++) { 4219 p1->u.str16[p1->len++] = p2->u.str8[i]; 4220 } 4221 return TRUE; 4222 } 4223 } 4224 } else if (!p2->is_wide_char) { 4225 if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { 4226 memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); 4227 p1->len += p2->len; 4228 p1->u.str8[p1->len] = '\0'; 4229 return TRUE; 4230 } 4231 } 4232 } 4233 return FALSE; 4234 } 4235 4236 /* op1 and op2 are converted to strings. For convenience, op1 or op2 = 4237 JS_EXCEPTION are accepted and return JS_EXCEPTION. */ 4238 static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) 4239 { 4240 JSValue ret; 4241 JSString *p1, *p2; 4242 4243 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { 4244 op1 = JS_ToStringFree(ctx, op1); 4245 if (JS_IsException(op1)) { 4246 JS_FreeValue(ctx, op2); 4247 return JS_EXCEPTION; 4248 } 4249 } 4250 if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { 4251 op2 = JS_ToStringFree(ctx, op2); 4252 if (JS_IsException(op2)) { 4253 JS_FreeValue(ctx, op1); 4254 return JS_EXCEPTION; 4255 } 4256 } 4257 p1 = JS_VALUE_GET_STRING(op1); 4258 if (JS_ConcatStringInPlace(ctx, p1, op2)) { 4259 JS_FreeValue(ctx, op2); 4260 return op1; 4261 } 4262 p2 = JS_VALUE_GET_STRING(op2); 4263 ret = JS_ConcatString1(ctx, p1, p2); 4264 JS_FreeValue(ctx, op1); 4265 JS_FreeValue(ctx, op2); 4266 return ret; 4267 } 4268 4269 /* Shape support */ 4270 4271 static inline size_t get_shape_size(size_t hash_size, size_t prop_size) 4272 { 4273 return hash_size * sizeof(uint32_t) + sizeof(JSShape) + 4274 prop_size * sizeof(JSShapeProperty); 4275 } 4276 4277 static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) 4278 { 4279 return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); 4280 } 4281 4282 static inline uint32_t *prop_hash_end(JSShape *sh) 4283 { 4284 return (uint32_t *)sh; 4285 } 4286 4287 static inline void *get_alloc_from_shape(JSShape *sh) 4288 { 4289 return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); 4290 } 4291 4292 static inline JSShapeProperty *get_shape_prop(JSShape *sh) 4293 { 4294 return sh->prop; 4295 } 4296 4297 static int init_shape_hash(JSRuntime *rt) 4298 { 4299 rt->shape_hash_bits = 4; /* 16 shapes */ 4300 rt->shape_hash_size = 1 << rt->shape_hash_bits; 4301 rt->shape_hash_count = 0; 4302 rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * 4303 rt->shape_hash_size); 4304 if (!rt->shape_hash) 4305 return -1; 4306 return 0; 4307 } 4308 4309 /* same magic hash multiplier as the Linux kernel */ 4310 static uint32_t shape_hash(uint32_t h, uint32_t val) 4311 { 4312 return (h + val) * 0x9e370001; 4313 } 4314 4315 /* truncate the shape hash to 'hash_bits' bits */ 4316 static uint32_t get_shape_hash(uint32_t h, int hash_bits) 4317 { 4318 return h >> (32 - hash_bits); 4319 } 4320 4321 static uint32_t shape_initial_hash(JSObject *proto) 4322 { 4323 uint32_t h; 4324 h = shape_hash(1, (uintptr_t)proto); 4325 if (sizeof(proto) > 4) 4326 h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); 4327 return h; 4328 } 4329 4330 static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) 4331 { 4332 int new_shape_hash_size, i; 4333 uint32_t h; 4334 JSShape **new_shape_hash, *sh, *sh_next; 4335 4336 new_shape_hash_size = 1 << new_shape_hash_bits; 4337 new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * 4338 new_shape_hash_size); 4339 if (!new_shape_hash) 4340 return -1; 4341 for(i = 0; i < rt->shape_hash_size; i++) { 4342 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { 4343 sh_next = sh->shape_hash_next; 4344 h = get_shape_hash(sh->hash, new_shape_hash_bits); 4345 sh->shape_hash_next = new_shape_hash[h]; 4346 new_shape_hash[h] = sh; 4347 } 4348 } 4349 js_free_rt(rt, rt->shape_hash); 4350 rt->shape_hash_bits = new_shape_hash_bits; 4351 rt->shape_hash_size = new_shape_hash_size; 4352 rt->shape_hash = new_shape_hash; 4353 return 0; 4354 } 4355 4356 static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) 4357 { 4358 uint32_t h; 4359 h = get_shape_hash(sh->hash, rt->shape_hash_bits); 4360 sh->shape_hash_next = rt->shape_hash[h]; 4361 rt->shape_hash[h] = sh; 4362 rt->shape_hash_count++; 4363 } 4364 4365 static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) 4366 { 4367 uint32_t h; 4368 JSShape **psh; 4369 4370 h = get_shape_hash(sh->hash, rt->shape_hash_bits); 4371 psh = &rt->shape_hash[h]; 4372 while (*psh != sh) 4373 psh = &(*psh)->shape_hash_next; 4374 *psh = sh->shape_hash_next; 4375 rt->shape_hash_count--; 4376 } 4377 4378 /* create a new empty shape with prototype 'proto' */ 4379 static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, 4380 int hash_size, int prop_size) 4381 { 4382 JSRuntime *rt = ctx->rt; 4383 void *sh_alloc; 4384 JSShape *sh; 4385 4386 /* resize the shape hash table if necessary */ 4387 if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { 4388 resize_shape_hash(rt, rt->shape_hash_bits + 1); 4389 } 4390 4391 sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); 4392 if (!sh_alloc) 4393 return NULL; 4394 sh = get_shape_from_alloc(sh_alloc, hash_size); 4395 sh->header.ref_count = 1; 4396 add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 4397 if (proto) 4398 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto)); 4399 sh->proto = proto; 4400 memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * 4401 hash_size); 4402 sh->prop_hash_mask = hash_size - 1; 4403 sh->prop_size = prop_size; 4404 sh->prop_count = 0; 4405 sh->deleted_prop_count = 0; 4406 4407 /* insert in the hash table */ 4408 sh->hash = shape_initial_hash(proto); 4409 sh->is_hashed = TRUE; 4410 sh->has_small_array_index = FALSE; 4411 js_shape_hash_link(ctx->rt, sh); 4412 return sh; 4413 } 4414 4415 static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) 4416 { 4417 return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, 4418 JS_PROP_INITIAL_SIZE); 4419 } 4420 4421 /* The shape is cloned. The new shape is not inserted in the shape 4422 hash table */ 4423 static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) 4424 { 4425 JSShape *sh; 4426 void *sh_alloc, *sh_alloc1; 4427 size_t size; 4428 JSShapeProperty *pr; 4429 uint32_t i, hash_size; 4430 4431 hash_size = sh1->prop_hash_mask + 1; 4432 size = get_shape_size(hash_size, sh1->prop_size); 4433 sh_alloc = js_malloc(ctx, size); 4434 if (!sh_alloc) 4435 return NULL; 4436 sh_alloc1 = get_alloc_from_shape(sh1); 4437 memcpy(sh_alloc, sh_alloc1, size); 4438 sh = get_shape_from_alloc(sh_alloc, hash_size); 4439 sh->header.ref_count = 1; 4440 add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); 4441 sh->is_hashed = FALSE; 4442 if (sh->proto) { 4443 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 4444 } 4445 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { 4446 JS_DupAtom(ctx, pr->atom); 4447 } 4448 return sh; 4449 } 4450 4451 static JSShape *js_dup_shape(JSShape *sh) 4452 { 4453 sh->header.ref_count++; 4454 return sh; 4455 } 4456 4457 static void js_free_shape0(JSRuntime *rt, JSShape *sh) 4458 { 4459 uint32_t i; 4460 JSShapeProperty *pr; 4461 4462 assert(sh->header.ref_count == 0); 4463 if (sh->is_hashed) 4464 js_shape_hash_unlink(rt, sh); 4465 if (sh->proto != NULL) { 4466 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 4467 } 4468 pr = get_shape_prop(sh); 4469 for(i = 0; i < sh->prop_count; i++) { 4470 JS_FreeAtomRT(rt, pr->atom); 4471 pr++; 4472 } 4473 remove_gc_object(&sh->header); 4474 js_free_rt(rt, get_alloc_from_shape(sh)); 4475 } 4476 4477 static void js_free_shape(JSRuntime *rt, JSShape *sh) 4478 { 4479 if (unlikely(--sh->header.ref_count <= 0)) { 4480 js_free_shape0(rt, sh); 4481 } 4482 } 4483 4484 static void js_free_shape_null(JSRuntime *rt, JSShape *sh) 4485 { 4486 if (sh) 4487 js_free_shape(rt, sh); 4488 } 4489 4490 /* make space to hold at least 'count' properties */ 4491 static no_inline int resize_properties(JSContext *ctx, JSShape **psh, 4492 JSObject *p, uint32_t count) 4493 { 4494 JSShape *sh; 4495 uint32_t new_size, new_hash_size, new_hash_mask, i; 4496 JSShapeProperty *pr; 4497 void *sh_alloc; 4498 intptr_t h; 4499 JSShape *old_sh; 4500 4501 sh = *psh; 4502 new_size = max_int(count, sh->prop_size * 3 / 2); 4503 /* Reallocate prop array first to avoid crash or size inconsistency 4504 in case of memory allocation failure */ 4505 if (p) { 4506 JSProperty *new_prop; 4507 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); 4508 if (unlikely(!new_prop)) 4509 return -1; 4510 p->prop = new_prop; 4511 } 4512 new_hash_size = sh->prop_hash_mask + 1; 4513 while (new_hash_size < new_size) 4514 new_hash_size = 2 * new_hash_size; 4515 /* resize the property shapes. Using js_realloc() is not possible in 4516 case the GC runs during the allocation */ 4517 old_sh = sh; 4518 sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); 4519 if (!sh_alloc) 4520 return -1; 4521 sh = get_shape_from_alloc(sh_alloc, new_hash_size); 4522 list_del(&old_sh->header.link); 4523 /* copy all the shape properties */ 4524 memcpy(sh, old_sh, 4525 sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); 4526 list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); 4527 4528 if (new_hash_size != (sh->prop_hash_mask + 1)) { 4529 /* resize the hash table and the properties */ 4530 new_hash_mask = new_hash_size - 1; 4531 sh->prop_hash_mask = new_hash_mask; 4532 memset(prop_hash_end(sh) - new_hash_size, 0, 4533 sizeof(prop_hash_end(sh)[0]) * new_hash_size); 4534 for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { 4535 if (pr->atom != JS_ATOM_NULL) { 4536 h = ((uintptr_t)pr->atom & new_hash_mask); 4537 pr->hash_next = prop_hash_end(sh)[-h - 1]; 4538 prop_hash_end(sh)[-h - 1] = i + 1; 4539 } 4540 } 4541 } else { 4542 /* just copy the previous hash table */ 4543 memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size, 4544 sizeof(prop_hash_end(sh)[0]) * new_hash_size); 4545 } 4546 js_free(ctx, get_alloc_from_shape(old_sh)); 4547 *psh = sh; 4548 sh->prop_size = new_size; 4549 return 0; 4550 } 4551 4552 /* remove the deleted properties. */ 4553 static int compact_properties(JSContext *ctx, JSObject *p) 4554 { 4555 JSShape *sh, *old_sh; 4556 void *sh_alloc; 4557 intptr_t h; 4558 uint32_t new_hash_size, i, j, new_hash_mask, new_size; 4559 JSShapeProperty *old_pr, *pr; 4560 JSProperty *prop, *new_prop; 4561 4562 sh = p->shape; 4563 assert(!sh->is_hashed); 4564 4565 new_size = max_int(JS_PROP_INITIAL_SIZE, 4566 sh->prop_count - sh->deleted_prop_count); 4567 assert(new_size <= sh->prop_size); 4568 4569 new_hash_size = sh->prop_hash_mask + 1; 4570 while ((new_hash_size / 2) >= new_size) 4571 new_hash_size = new_hash_size / 2; 4572 new_hash_mask = new_hash_size - 1; 4573 4574 /* resize the hash table and the properties */ 4575 old_sh = sh; 4576 sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); 4577 if (!sh_alloc) 4578 return -1; 4579 sh = get_shape_from_alloc(sh_alloc, new_hash_size); 4580 list_del(&old_sh->header.link); 4581 memcpy(sh, old_sh, sizeof(JSShape)); 4582 list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); 4583 4584 memset(prop_hash_end(sh) - new_hash_size, 0, 4585 sizeof(prop_hash_end(sh)[0]) * new_hash_size); 4586 4587 j = 0; 4588 old_pr = old_sh->prop; 4589 pr = sh->prop; 4590 prop = p->prop; 4591 for(i = 0; i < sh->prop_count; i++) { 4592 if (old_pr->atom != JS_ATOM_NULL) { 4593 pr->atom = old_pr->atom; 4594 pr->flags = old_pr->flags; 4595 h = ((uintptr_t)old_pr->atom & new_hash_mask); 4596 pr->hash_next = prop_hash_end(sh)[-h - 1]; 4597 prop_hash_end(sh)[-h - 1] = j + 1; 4598 prop[j] = prop[i]; 4599 j++; 4600 pr++; 4601 } 4602 old_pr++; 4603 } 4604 assert(j == (sh->prop_count - sh->deleted_prop_count)); 4605 sh->prop_hash_mask = new_hash_mask; 4606 sh->prop_size = new_size; 4607 sh->deleted_prop_count = 0; 4608 sh->prop_count = j; 4609 4610 p->shape = sh; 4611 js_free(ctx, get_alloc_from_shape(old_sh)); 4612 4613 /* reduce the size of the object properties */ 4614 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); 4615 if (new_prop) 4616 p->prop = new_prop; 4617 return 0; 4618 } 4619 4620 static int add_shape_property(JSContext *ctx, JSShape **psh, 4621 JSObject *p, JSAtom atom, int prop_flags) 4622 { 4623 JSRuntime *rt = ctx->rt; 4624 JSShape *sh = *psh; 4625 JSShapeProperty *pr, *prop; 4626 uint32_t hash_mask, new_shape_hash = 0; 4627 intptr_t h; 4628 4629 /* update the shape hash */ 4630 if (sh->is_hashed) { 4631 js_shape_hash_unlink(rt, sh); 4632 new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); 4633 } 4634 4635 if (unlikely(sh->prop_count >= sh->prop_size)) { 4636 if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { 4637 /* in case of error, reinsert in the hash table. 4638 sh is still valid if resize_properties() failed */ 4639 if (sh->is_hashed) 4640 js_shape_hash_link(rt, sh); 4641 return -1; 4642 } 4643 sh = *psh; 4644 } 4645 if (sh->is_hashed) { 4646 sh->hash = new_shape_hash; 4647 js_shape_hash_link(rt, sh); 4648 } 4649 /* Initialize the new shape property. 4650 The object property at p->prop[sh->prop_count] is uninitialized */ 4651 prop = get_shape_prop(sh); 4652 pr = &prop[sh->prop_count++]; 4653 pr->atom = JS_DupAtom(ctx, atom); 4654 pr->flags = prop_flags; 4655 sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); 4656 /* add in hash table */ 4657 hash_mask = sh->prop_hash_mask; 4658 h = atom & hash_mask; 4659 pr->hash_next = prop_hash_end(sh)[-h - 1]; 4660 prop_hash_end(sh)[-h - 1] = sh->prop_count; 4661 return 0; 4662 } 4663 4664 /* find a hashed empty shape matching the prototype. Return NULL if 4665 not found */ 4666 static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) 4667 { 4668 JSShape *sh1; 4669 uint32_t h, h1; 4670 4671 h = shape_initial_hash(proto); 4672 h1 = get_shape_hash(h, rt->shape_hash_bits); 4673 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { 4674 if (sh1->hash == h && 4675 sh1->proto == proto && 4676 sh1->prop_count == 0) { 4677 return sh1; 4678 } 4679 } 4680 return NULL; 4681 } 4682 4683 /* find a hashed shape matching sh + (prop, prop_flags). Return NULL if 4684 not found */ 4685 static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, 4686 JSAtom atom, int prop_flags) 4687 { 4688 JSShape *sh1; 4689 uint32_t h, h1, i, n; 4690 4691 h = sh->hash; 4692 h = shape_hash(h, atom); 4693 h = shape_hash(h, prop_flags); 4694 h1 = get_shape_hash(h, rt->shape_hash_bits); 4695 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { 4696 /* we test the hash first so that the rest is done only if the 4697 shapes really match */ 4698 if (sh1->hash == h && 4699 sh1->proto == sh->proto && 4700 sh1->prop_count == ((n = sh->prop_count) + 1)) { 4701 for(i = 0; i < n; i++) { 4702 if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || 4703 unlikely(sh1->prop[i].flags != sh->prop[i].flags)) 4704 goto next; 4705 } 4706 if (unlikely(sh1->prop[n].atom != atom) || 4707 unlikely(sh1->prop[n].flags != prop_flags)) 4708 goto next; 4709 return sh1; 4710 } 4711 next: ; 4712 } 4713 return NULL; 4714 } 4715 4716 static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) 4717 { 4718 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 4719 int j; 4720 4721 /* XXX: should output readable class prototype */ 4722 printf("%5d %3d%c %14p %5d %5d", i, 4723 sh->header.ref_count, " *"[sh->is_hashed], 4724 (void *)sh->proto, sh->prop_size, sh->prop_count); 4725 for(j = 0; j < sh->prop_count; j++) { 4726 printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), 4727 sh->prop[j].atom)); 4728 } 4729 printf("\n"); 4730 } 4731 4732 static __maybe_unused void JS_DumpShapes(JSRuntime *rt) 4733 { 4734 int i; 4735 JSShape *sh; 4736 struct list_head *el; 4737 JSObject *p; 4738 JSGCObjectHeader *gp; 4739 4740 printf("JSShapes: {\n"); 4741 printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); 4742 for(i = 0; i < rt->shape_hash_size; i++) { 4743 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { 4744 JS_DumpShape(rt, i, sh); 4745 assert(sh->is_hashed); 4746 } 4747 } 4748 /* dump non-hashed shapes */ 4749 list_for_each(el, &rt->gc_obj_list) { 4750 gp = list_entry(el, JSGCObjectHeader, link); 4751 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 4752 p = (JSObject *)gp; 4753 if (!p->shape->is_hashed) { 4754 JS_DumpShape(rt, -1, p->shape); 4755 } 4756 } 4757 } 4758 printf("}\n"); 4759 } 4760 4761 static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) 4762 { 4763 JSObject *p; 4764 4765 js_trigger_gc(ctx->rt, sizeof(JSObject)); 4766 p = js_malloc(ctx, sizeof(JSObject)); 4767 if (unlikely(!p)) 4768 goto fail; 4769 p->class_id = class_id; 4770 p->extensible = TRUE; 4771 p->free_mark = 0; 4772 p->is_exotic = 0; 4773 p->fast_array = 0; 4774 p->is_constructor = 0; 4775 p->is_uncatchable_error = 0; 4776 p->tmp_mark = 0; 4777 p->is_HTMLDDA = 0; 4778 p->first_weak_ref = NULL; 4779 p->u.opaque = NULL; 4780 p->shape = sh; 4781 p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); 4782 if (unlikely(!p->prop)) { 4783 js_free(ctx, p); 4784 fail: 4785 js_free_shape(ctx->rt, sh); 4786 return JS_EXCEPTION; 4787 } 4788 4789 switch(class_id) { 4790 case JS_CLASS_OBJECT: 4791 break; 4792 case JS_CLASS_ARRAY: 4793 { 4794 JSProperty *pr; 4795 p->is_exotic = 1; 4796 p->fast_array = 1; 4797 p->u.array.u.values = NULL; 4798 p->u.array.count = 0; 4799 p->u.array.u1.size = 0; 4800 /* the length property is always the first one */ 4801 if (likely(sh == ctx->array_shape)) { 4802 pr = &p->prop[0]; 4803 } else { 4804 /* only used for the first array */ 4805 /* cannot fail */ 4806 pr = add_property(ctx, p, JS_ATOM_length, 4807 JS_PROP_WRITABLE | JS_PROP_LENGTH); 4808 } 4809 pr->u.value = JS_NewInt32(ctx, 0); 4810 } 4811 break; 4812 case JS_CLASS_C_FUNCTION: 4813 p->prop[0].u.value = JS_UNDEFINED; 4814 break; 4815 case JS_CLASS_ARGUMENTS: 4816 case JS_CLASS_UINT8C_ARRAY: 4817 case JS_CLASS_INT8_ARRAY: 4818 case JS_CLASS_UINT8_ARRAY: 4819 case JS_CLASS_INT16_ARRAY: 4820 case JS_CLASS_UINT16_ARRAY: 4821 case JS_CLASS_INT32_ARRAY: 4822 case JS_CLASS_UINT32_ARRAY: 4823 case JS_CLASS_BIG_INT64_ARRAY: 4824 case JS_CLASS_BIG_UINT64_ARRAY: 4825 case JS_CLASS_FLOAT32_ARRAY: 4826 case JS_CLASS_FLOAT64_ARRAY: 4827 p->is_exotic = 1; 4828 p->fast_array = 1; 4829 p->u.array.u.ptr = NULL; 4830 p->u.array.count = 0; 4831 break; 4832 case JS_CLASS_DATAVIEW: 4833 p->u.array.u.ptr = NULL; 4834 p->u.array.count = 0; 4835 break; 4836 case JS_CLASS_NUMBER: 4837 case JS_CLASS_STRING: 4838 case JS_CLASS_BOOLEAN: 4839 case JS_CLASS_SYMBOL: 4840 case JS_CLASS_DATE: 4841 case JS_CLASS_BIG_INT: 4842 #ifdef CONFIG_BIGNUM 4843 case JS_CLASS_BIG_FLOAT: 4844 case JS_CLASS_BIG_DECIMAL: 4845 #endif 4846 p->u.object_data = JS_UNDEFINED; 4847 goto set_exotic; 4848 case JS_CLASS_REGEXP: 4849 p->u.regexp.pattern = NULL; 4850 p->u.regexp.bytecode = NULL; 4851 goto set_exotic; 4852 default: 4853 set_exotic: 4854 if (ctx->rt->class_array[class_id].exotic) { 4855 p->is_exotic = 1; 4856 } 4857 break; 4858 } 4859 p->header.ref_count = 1; 4860 add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); 4861 return JS_MKPTR(JS_TAG_OBJECT, p); 4862 } 4863 4864 static JSObject *get_proto_obj(JSValueConst proto_val) 4865 { 4866 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) 4867 return NULL; 4868 else 4869 return JS_VALUE_GET_OBJ(proto_val); 4870 } 4871 4872 /* WARNING: proto must be an object or JS_NULL */ 4873 JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, 4874 JSClassID class_id) 4875 { 4876 JSShape *sh; 4877 JSObject *proto; 4878 4879 proto = get_proto_obj(proto_val); 4880 sh = find_hashed_shape_proto(ctx->rt, proto); 4881 if (likely(sh)) { 4882 sh = js_dup_shape(sh); 4883 } else { 4884 sh = js_new_shape(ctx, proto); 4885 if (!sh) 4886 return JS_EXCEPTION; 4887 } 4888 return JS_NewObjectFromShape(ctx, sh, class_id); 4889 } 4890 4891 #if 0 4892 static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) 4893 { 4894 JSObject *p; 4895 4896 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 4897 p = JS_VALUE_GET_OBJ(obj); 4898 switch(p->class_id) { 4899 case JS_CLASS_NUMBER: 4900 case JS_CLASS_STRING: 4901 case JS_CLASS_BOOLEAN: 4902 case JS_CLASS_SYMBOL: 4903 case JS_CLASS_DATE: 4904 case JS_CLASS_BIG_INT: 4905 #ifdef CONFIG_BIGNUM 4906 case JS_CLASS_BIG_FLOAT: 4907 case JS_CLASS_BIG_DECIMAL: 4908 #endif 4909 return JS_DupValue(ctx, p->u.object_data); 4910 } 4911 } 4912 return JS_UNDEFINED; 4913 } 4914 #endif 4915 4916 static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) 4917 { 4918 JSObject *p; 4919 4920 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 4921 p = JS_VALUE_GET_OBJ(obj); 4922 switch(p->class_id) { 4923 case JS_CLASS_NUMBER: 4924 case JS_CLASS_STRING: 4925 case JS_CLASS_BOOLEAN: 4926 case JS_CLASS_SYMBOL: 4927 case JS_CLASS_DATE: 4928 case JS_CLASS_BIG_INT: 4929 #ifdef CONFIG_BIGNUM 4930 case JS_CLASS_BIG_FLOAT: 4931 case JS_CLASS_BIG_DECIMAL: 4932 #endif 4933 JS_FreeValue(ctx, p->u.object_data); 4934 p->u.object_data = val; 4935 return 0; 4936 } 4937 } 4938 JS_FreeValue(ctx, val); 4939 if (!JS_IsException(obj)) 4940 JS_ThrowTypeError(ctx, "invalid object type"); 4941 return -1; 4942 } 4943 4944 JSValue JS_NewObjectClass(JSContext *ctx, int class_id) 4945 { 4946 return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); 4947 } 4948 4949 JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto) 4950 { 4951 return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); 4952 } 4953 4954 JSValue JS_NewArray(JSContext *ctx) 4955 { 4956 return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), 4957 JS_CLASS_ARRAY); 4958 } 4959 4960 JSValue JS_NewObject(JSContext *ctx) 4961 { 4962 /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ 4963 return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); 4964 } 4965 4966 static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj, 4967 JSAtom name, int len) 4968 { 4969 /* ES6 feature non compatible with ES5.1: length is configurable */ 4970 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len), 4971 JS_PROP_CONFIGURABLE); 4972 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, 4973 JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); 4974 } 4975 4976 static BOOL js_class_has_bytecode(JSClassID class_id) 4977 { 4978 return (class_id == JS_CLASS_BYTECODE_FUNCTION || 4979 class_id == JS_CLASS_GENERATOR_FUNCTION || 4980 class_id == JS_CLASS_ASYNC_FUNCTION || 4981 class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); 4982 } 4983 4984 /* return NULL without exception if not a function or no bytecode */ 4985 static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val) 4986 { 4987 JSObject *p; 4988 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 4989 return NULL; 4990 p = JS_VALUE_GET_OBJ(val); 4991 if (!js_class_has_bytecode(p->class_id)) 4992 return NULL; 4993 return p->u.func.function_bytecode; 4994 } 4995 4996 static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj, 4997 JSValueConst home_obj) 4998 { 4999 JSObject *p, *p1; 5000 JSFunctionBytecode *b; 5001 5002 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 5003 return; 5004 p = JS_VALUE_GET_OBJ(func_obj); 5005 if (!js_class_has_bytecode(p->class_id)) 5006 return; 5007 b = p->u.func.function_bytecode; 5008 if (b->need_home_object) { 5009 p1 = p->u.func.home_object; 5010 if (p1) { 5011 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 5012 } 5013 if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) 5014 p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj)); 5015 else 5016 p1 = NULL; 5017 p->u.func.home_object = p1; 5018 } 5019 } 5020 5021 static JSValue js_get_function_name(JSContext *ctx, JSAtom name) 5022 { 5023 JSValue name_str; 5024 5025 name_str = JS_AtomToString(ctx, name); 5026 if (JS_AtomSymbolHasDescription(ctx, name)) { 5027 name_str = JS_ConcatString3(ctx, "[", name_str, "]"); 5028 } 5029 return name_str; 5030 } 5031 5032 /* Modify the name of a method according to the atom and 5033 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and 5034 JS_PROP_HAS_SET. Also set the home object of the method. 5035 Return < 0 if exception. */ 5036 static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj, 5037 JSAtom name, int flags, JSValueConst home_obj) 5038 { 5039 JSValue name_str; 5040 5041 name_str = js_get_function_name(ctx, name); 5042 if (flags & JS_PROP_HAS_GET) { 5043 name_str = JS_ConcatString3(ctx, "get ", name_str, ""); 5044 } else if (flags & JS_PROP_HAS_SET) { 5045 name_str = JS_ConcatString3(ctx, "set ", name_str, ""); 5046 } 5047 if (JS_IsException(name_str)) 5048 return -1; 5049 if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, 5050 JS_PROP_CONFIGURABLE) < 0) 5051 return -1; 5052 js_method_set_home_object(ctx, func_obj, home_obj); 5053 return 0; 5054 } 5055 5056 /* Note: at least 'length' arguments will be readable in 'argv' */ 5057 static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, 5058 const char *name, 5059 int length, JSCFunctionEnum cproto, int magic, 5060 JSValueConst proto_val) 5061 { 5062 JSValue func_obj; 5063 JSObject *p; 5064 JSAtom name_atom; 5065 5066 func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); 5067 if (JS_IsException(func_obj)) 5068 return func_obj; 5069 p = JS_VALUE_GET_OBJ(func_obj); 5070 p->u.cfunc.realm = JS_DupContext(ctx); 5071 p->u.cfunc.c_function.generic = func; 5072 p->u.cfunc.length = length; 5073 p->u.cfunc.cproto = cproto; 5074 p->u.cfunc.magic = magic; 5075 p->is_constructor = (cproto == JS_CFUNC_constructor || 5076 cproto == JS_CFUNC_constructor_magic || 5077 cproto == JS_CFUNC_constructor_or_func || 5078 cproto == JS_CFUNC_constructor_or_func_magic); 5079 if (!name) 5080 name = ""; 5081 name_atom = JS_NewAtom(ctx, name); 5082 js_function_set_properties(ctx, func_obj, name_atom, length); 5083 JS_FreeAtom(ctx, name_atom); 5084 return func_obj; 5085 } 5086 5087 /* Note: at least 'length' arguments will be readable in 'argv' */ 5088 JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, 5089 const char *name, 5090 int length, JSCFunctionEnum cproto, int magic) 5091 { 5092 return JS_NewCFunction3(ctx, func, name, length, cproto, magic, 5093 ctx->function_proto); 5094 } 5095 5096 typedef struct JSCFunctionDataRecord { 5097 JSCFunctionData *func; 5098 uint8_t length; 5099 uint8_t data_len; 5100 uint16_t magic; 5101 JSValue data[0]; 5102 } JSCFunctionDataRecord; 5103 5104 static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) 5105 { 5106 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); 5107 int i; 5108 5109 if (s) { 5110 for(i = 0; i < s->data_len; i++) { 5111 JS_FreeValueRT(rt, s->data[i]); 5112 } 5113 js_free_rt(rt, s); 5114 } 5115 } 5116 5117 static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val, 5118 JS_MarkFunc *mark_func) 5119 { 5120 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); 5121 int i; 5122 5123 if (s) { 5124 for(i = 0; i < s->data_len; i++) { 5125 JS_MarkValue(rt, s->data[i], mark_func); 5126 } 5127 } 5128 } 5129 5130 static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj, 5131 JSValueConst this_val, 5132 int argc, JSValueConst *argv, int flags) 5133 { 5134 JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); 5135 JSValueConst *arg_buf; 5136 int i; 5137 5138 /* XXX: could add the function on the stack for debug */ 5139 if (unlikely(argc < s->length)) { 5140 arg_buf = alloca(sizeof(arg_buf[0]) * s->length); 5141 for(i = 0; i < argc; i++) 5142 arg_buf[i] = argv[i]; 5143 for(i = argc; i < s->length; i++) 5144 arg_buf[i] = JS_UNDEFINED; 5145 } else { 5146 arg_buf = argv; 5147 } 5148 5149 return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); 5150 } 5151 5152 JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, 5153 int length, int magic, int data_len, 5154 JSValueConst *data) 5155 { 5156 JSCFunctionDataRecord *s; 5157 JSValue func_obj; 5158 int i; 5159 5160 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 5161 JS_CLASS_C_FUNCTION_DATA); 5162 if (JS_IsException(func_obj)) 5163 return func_obj; 5164 s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); 5165 if (!s) { 5166 JS_FreeValue(ctx, func_obj); 5167 return JS_EXCEPTION; 5168 } 5169 s->func = func; 5170 s->length = length; 5171 s->data_len = data_len; 5172 s->magic = magic; 5173 for(i = 0; i < data_len; i++) 5174 s->data[i] = JS_DupValue(ctx, data[i]); 5175 JS_SetOpaque(func_obj, s); 5176 js_function_set_properties(ctx, func_obj, 5177 JS_ATOM_empty_string, length); 5178 return func_obj; 5179 } 5180 5181 static JSContext *js_autoinit_get_realm(JSProperty *pr) 5182 { 5183 return (JSContext *)(pr->u.init.realm_and_id & ~3); 5184 } 5185 5186 static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) 5187 { 5188 return pr->u.init.realm_and_id & 3; 5189 } 5190 5191 static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) 5192 { 5193 JS_FreeContext(js_autoinit_get_realm(pr)); 5194 } 5195 5196 static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, 5197 JS_MarkFunc *mark_func) 5198 { 5199 mark_func(rt, &js_autoinit_get_realm(pr)->header); 5200 } 5201 5202 static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) 5203 { 5204 if (unlikely(prop_flags & JS_PROP_TMASK)) { 5205 if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 5206 if (pr->u.getset.getter) 5207 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 5208 if (pr->u.getset.setter) 5209 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 5210 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 5211 free_var_ref(rt, pr->u.var_ref); 5212 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 5213 js_autoinit_free(rt, pr); 5214 } 5215 } else { 5216 JS_FreeValueRT(rt, pr->u.value); 5217 } 5218 } 5219 5220 static force_inline JSShapeProperty *find_own_property1(JSObject *p, 5221 JSAtom atom) 5222 { 5223 JSShape *sh; 5224 JSShapeProperty *pr, *prop; 5225 intptr_t h; 5226 sh = p->shape; 5227 h = (uintptr_t)atom & sh->prop_hash_mask; 5228 h = prop_hash_end(sh)[-h - 1]; 5229 prop = get_shape_prop(sh); 5230 while (h) { 5231 pr = &prop[h - 1]; 5232 if (likely(pr->atom == atom)) { 5233 return pr; 5234 } 5235 h = pr->hash_next; 5236 } 5237 return NULL; 5238 } 5239 5240 static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, 5241 JSObject *p, 5242 JSAtom atom) 5243 { 5244 JSShape *sh; 5245 JSShapeProperty *pr, *prop; 5246 intptr_t h; 5247 sh = p->shape; 5248 h = (uintptr_t)atom & sh->prop_hash_mask; 5249 h = prop_hash_end(sh)[-h - 1]; 5250 prop = get_shape_prop(sh); 5251 while (h) { 5252 pr = &prop[h - 1]; 5253 if (likely(pr->atom == atom)) { 5254 *ppr = &p->prop[h - 1]; 5255 /* the compiler should be able to assume that pr != NULL here */ 5256 return pr; 5257 } 5258 h = pr->hash_next; 5259 } 5260 *ppr = NULL; 5261 return NULL; 5262 } 5263 5264 /* indicate that the object may be part of a function prototype cycle */ 5265 static void set_cycle_flag(JSContext *ctx, JSValueConst obj) 5266 { 5267 } 5268 5269 static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) 5270 { 5271 if (var_ref) { 5272 assert(var_ref->header.ref_count > 0); 5273 if (--var_ref->header.ref_count == 0) { 5274 if (var_ref->is_detached) { 5275 JS_FreeValueRT(rt, var_ref->value); 5276 } else { 5277 list_del(&var_ref->var_ref_link); /* still on the stack */ 5278 if (var_ref->async_func) 5279 async_func_free(rt, var_ref->async_func); 5280 } 5281 remove_gc_object(&var_ref->header); 5282 js_free_rt(rt, var_ref); 5283 } 5284 } 5285 } 5286 5287 static void js_array_finalizer(JSRuntime *rt, JSValue val) 5288 { 5289 JSObject *p = JS_VALUE_GET_OBJ(val); 5290 int i; 5291 5292 for(i = 0; i < p->u.array.count; i++) { 5293 JS_FreeValueRT(rt, p->u.array.u.values[i]); 5294 } 5295 js_free_rt(rt, p->u.array.u.values); 5296 } 5297 5298 static void js_array_mark(JSRuntime *rt, JSValueConst val, 5299 JS_MarkFunc *mark_func) 5300 { 5301 JSObject *p = JS_VALUE_GET_OBJ(val); 5302 int i; 5303 5304 for(i = 0; i < p->u.array.count; i++) { 5305 JS_MarkValue(rt, p->u.array.u.values[i], mark_func); 5306 } 5307 } 5308 5309 static void js_object_data_finalizer(JSRuntime *rt, JSValue val) 5310 { 5311 JSObject *p = JS_VALUE_GET_OBJ(val); 5312 JS_FreeValueRT(rt, p->u.object_data); 5313 p->u.object_data = JS_UNDEFINED; 5314 } 5315 5316 static void js_object_data_mark(JSRuntime *rt, JSValueConst val, 5317 JS_MarkFunc *mark_func) 5318 { 5319 JSObject *p = JS_VALUE_GET_OBJ(val); 5320 JS_MarkValue(rt, p->u.object_data, mark_func); 5321 } 5322 5323 static void js_c_function_finalizer(JSRuntime *rt, JSValue val) 5324 { 5325 JSObject *p = JS_VALUE_GET_OBJ(val); 5326 5327 if (p->u.cfunc.realm) 5328 JS_FreeContext(p->u.cfunc.realm); 5329 } 5330 5331 static void js_c_function_mark(JSRuntime *rt, JSValueConst val, 5332 JS_MarkFunc *mark_func) 5333 { 5334 JSObject *p = JS_VALUE_GET_OBJ(val); 5335 5336 if (p->u.cfunc.realm) 5337 mark_func(rt, &p->u.cfunc.realm->header); 5338 } 5339 5340 static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) 5341 { 5342 JSObject *p1, *p = JS_VALUE_GET_OBJ(val); 5343 JSFunctionBytecode *b; 5344 JSVarRef **var_refs; 5345 int i; 5346 5347 p1 = p->u.func.home_object; 5348 if (p1) { 5349 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); 5350 } 5351 b = p->u.func.function_bytecode; 5352 if (b) { 5353 var_refs = p->u.func.var_refs; 5354 if (var_refs) { 5355 for(i = 0; i < b->closure_var_count; i++) 5356 free_var_ref(rt, var_refs[i]); 5357 js_free_rt(rt, var_refs); 5358 } 5359 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); 5360 } 5361 } 5362 5363 static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, 5364 JS_MarkFunc *mark_func) 5365 { 5366 JSObject *p = JS_VALUE_GET_OBJ(val); 5367 JSVarRef **var_refs = p->u.func.var_refs; 5368 JSFunctionBytecode *b = p->u.func.function_bytecode; 5369 int i; 5370 5371 if (p->u.func.home_object) { 5372 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), 5373 mark_func); 5374 } 5375 if (b) { 5376 if (var_refs) { 5377 for(i = 0; i < b->closure_var_count; i++) { 5378 JSVarRef *var_ref = var_refs[i]; 5379 if (var_ref) { 5380 mark_func(rt, &var_ref->header); 5381 } 5382 } 5383 } 5384 /* must mark the function bytecode because template objects may be 5385 part of a cycle */ 5386 JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); 5387 } 5388 } 5389 5390 static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) 5391 { 5392 JSObject *p = JS_VALUE_GET_OBJ(val); 5393 JSBoundFunction *bf = p->u.bound_function; 5394 int i; 5395 5396 JS_FreeValueRT(rt, bf->func_obj); 5397 JS_FreeValueRT(rt, bf->this_val); 5398 for(i = 0; i < bf->argc; i++) { 5399 JS_FreeValueRT(rt, bf->argv[i]); 5400 } 5401 js_free_rt(rt, bf); 5402 } 5403 5404 static void js_bound_function_mark(JSRuntime *rt, JSValueConst val, 5405 JS_MarkFunc *mark_func) 5406 { 5407 JSObject *p = JS_VALUE_GET_OBJ(val); 5408 JSBoundFunction *bf = p->u.bound_function; 5409 int i; 5410 5411 JS_MarkValue(rt, bf->func_obj, mark_func); 5412 JS_MarkValue(rt, bf->this_val, mark_func); 5413 for(i = 0; i < bf->argc; i++) 5414 JS_MarkValue(rt, bf->argv[i], mark_func); 5415 } 5416 5417 static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) 5418 { 5419 JSObject *p = JS_VALUE_GET_OBJ(val); 5420 JSForInIterator *it = p->u.for_in_iterator; 5421 int i; 5422 5423 JS_FreeValueRT(rt, it->obj); 5424 if (!it->is_array) { 5425 for(i = 0; i < it->atom_count; i++) { 5426 JS_FreeAtomRT(rt, it->tab_atom[i].atom); 5427 } 5428 js_free_rt(rt, it->tab_atom); 5429 } 5430 js_free_rt(rt, it); 5431 } 5432 5433 static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val, 5434 JS_MarkFunc *mark_func) 5435 { 5436 JSObject *p = JS_VALUE_GET_OBJ(val); 5437 JSForInIterator *it = p->u.for_in_iterator; 5438 JS_MarkValue(rt, it->obj, mark_func); 5439 } 5440 5441 static void free_object(JSRuntime *rt, JSObject *p) 5442 { 5443 int i; 5444 JSClassFinalizer *finalizer; 5445 JSShape *sh; 5446 JSShapeProperty *pr; 5447 5448 p->free_mark = 1; /* used to tell the object is invalid when 5449 freeing cycles */ 5450 /* free all the fields */ 5451 sh = p->shape; 5452 pr = get_shape_prop(sh); 5453 for(i = 0; i < sh->prop_count; i++) { 5454 free_property(rt, &p->prop[i], pr->flags); 5455 pr++; 5456 } 5457 js_free_rt(rt, p->prop); 5458 /* as an optimization we destroy the shape immediately without 5459 putting it in gc_zero_ref_count_list */ 5460 js_free_shape(rt, sh); 5461 5462 /* fail safe */ 5463 p->shape = NULL; 5464 p->prop = NULL; 5465 5466 if (unlikely(p->first_weak_ref)) { 5467 reset_weak_ref(rt, p); 5468 } 5469 5470 finalizer = rt->class_array[p->class_id].finalizer; 5471 if (finalizer) 5472 (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); 5473 5474 /* fail safe */ 5475 p->class_id = 0; 5476 p->u.opaque = NULL; 5477 p->u.func.var_refs = NULL; 5478 p->u.func.home_object = NULL; 5479 5480 remove_gc_object(&p->header); 5481 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { 5482 list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); 5483 } else { 5484 js_free_rt(rt, p); 5485 } 5486 } 5487 5488 static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) 5489 { 5490 switch(gp->gc_obj_type) { 5491 case JS_GC_OBJ_TYPE_JS_OBJECT: 5492 free_object(rt, (JSObject *)gp); 5493 break; 5494 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 5495 free_function_bytecode(rt, (JSFunctionBytecode *)gp); 5496 break; 5497 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 5498 __async_func_free(rt, (JSAsyncFunctionState *)gp); 5499 break; 5500 default: 5501 abort(); 5502 } 5503 } 5504 5505 static void free_zero_refcount(JSRuntime *rt) 5506 { 5507 struct list_head *el; 5508 JSGCObjectHeader *p; 5509 5510 rt->gc_phase = JS_GC_PHASE_DECREF; 5511 for(;;) { 5512 el = rt->gc_zero_ref_count_list.next; 5513 if (el == &rt->gc_zero_ref_count_list) 5514 break; 5515 p = list_entry(el, JSGCObjectHeader, link); 5516 assert(p->ref_count == 0); 5517 free_gc_object(rt, p); 5518 } 5519 rt->gc_phase = JS_GC_PHASE_NONE; 5520 } 5521 5522 /* called with the ref_count of 'v' reaches zero. */ 5523 void __JS_FreeValueRT(JSRuntime *rt, JSValue v) 5524 { 5525 uint32_t tag = JS_VALUE_GET_TAG(v); 5526 5527 #ifdef DUMP_FREE 5528 { 5529 printf("Freeing "); 5530 if (tag == JS_TAG_OBJECT) { 5531 JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); 5532 } else { 5533 JS_DumpValueShort(rt, v); 5534 printf("\n"); 5535 } 5536 } 5537 #endif 5538 5539 switch(tag) { 5540 case JS_TAG_STRING: 5541 { 5542 JSString *p = JS_VALUE_GET_STRING(v); 5543 if (p->atom_type) { 5544 JS_FreeAtomStruct(rt, p); 5545 } else { 5546 #ifdef DUMP_LEAKS 5547 list_del(&p->link); 5548 #endif 5549 js_free_rt(rt, p); 5550 } 5551 } 5552 break; 5553 case JS_TAG_OBJECT: 5554 case JS_TAG_FUNCTION_BYTECODE: 5555 { 5556 JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); 5557 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { 5558 list_del(&p->link); 5559 list_add(&p->link, &rt->gc_zero_ref_count_list); 5560 if (rt->gc_phase == JS_GC_PHASE_NONE) { 5561 free_zero_refcount(rt); 5562 } 5563 } 5564 } 5565 break; 5566 case JS_TAG_MODULE: 5567 abort(); /* never freed here */ 5568 break; 5569 case JS_TAG_BIG_INT: 5570 #ifdef CONFIG_BIGNUM 5571 case JS_TAG_BIG_FLOAT: 5572 #endif 5573 { 5574 JSBigFloat *bf = JS_VALUE_GET_PTR(v); 5575 bf_delete(&bf->num); 5576 js_free_rt(rt, bf); 5577 } 5578 break; 5579 #ifdef CONFIG_BIGNUM 5580 case JS_TAG_BIG_DECIMAL: 5581 { 5582 JSBigDecimal *bf = JS_VALUE_GET_PTR(v); 5583 bfdec_delete(&bf->num); 5584 js_free_rt(rt, bf); 5585 } 5586 break; 5587 #endif 5588 case JS_TAG_SYMBOL: 5589 { 5590 JSAtomStruct *p = JS_VALUE_GET_PTR(v); 5591 JS_FreeAtomStruct(rt, p); 5592 } 5593 break; 5594 default: 5595 printf("__JS_FreeValue: unknown tag=%d\n", tag); 5596 abort(); 5597 } 5598 } 5599 5600 void __JS_FreeValue(JSContext *ctx, JSValue v) 5601 { 5602 __JS_FreeValueRT(ctx->rt, v); 5603 } 5604 5605 /* garbage collection */ 5606 5607 static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, 5608 JSGCObjectTypeEnum type) 5609 { 5610 h->mark = 0; 5611 h->gc_obj_type = type; 5612 list_add_tail(&h->link, &rt->gc_obj_list); 5613 } 5614 5615 static void remove_gc_object(JSGCObjectHeader *h) 5616 { 5617 list_del(&h->link); 5618 } 5619 5620 void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) 5621 { 5622 if (JS_VALUE_HAS_REF_COUNT(val)) { 5623 switch(JS_VALUE_GET_TAG(val)) { 5624 case JS_TAG_OBJECT: 5625 case JS_TAG_FUNCTION_BYTECODE: 5626 mark_func(rt, JS_VALUE_GET_PTR(val)); 5627 break; 5628 default: 5629 break; 5630 } 5631 } 5632 } 5633 5634 static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, 5635 JS_MarkFunc *mark_func) 5636 { 5637 switch(gp->gc_obj_type) { 5638 case JS_GC_OBJ_TYPE_JS_OBJECT: 5639 { 5640 JSObject *p = (JSObject *)gp; 5641 JSShapeProperty *prs; 5642 JSShape *sh; 5643 int i; 5644 sh = p->shape; 5645 mark_func(rt, &sh->header); 5646 /* mark all the fields */ 5647 prs = get_shape_prop(sh); 5648 for(i = 0; i < sh->prop_count; i++) { 5649 JSProperty *pr = &p->prop[i]; 5650 if (prs->atom != JS_ATOM_NULL) { 5651 if (prs->flags & JS_PROP_TMASK) { 5652 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 5653 if (pr->u.getset.getter) 5654 mark_func(rt, &pr->u.getset.getter->header); 5655 if (pr->u.getset.setter) 5656 mark_func(rt, &pr->u.getset.setter->header); 5657 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 5658 /* Note: the tag does not matter 5659 provided it is a GC object */ 5660 mark_func(rt, &pr->u.var_ref->header); 5661 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 5662 js_autoinit_mark(rt, pr, mark_func); 5663 } 5664 } else { 5665 JS_MarkValue(rt, pr->u.value, mark_func); 5666 } 5667 } 5668 prs++; 5669 } 5670 5671 if (p->class_id != JS_CLASS_OBJECT) { 5672 JSClassGCMark *gc_mark; 5673 gc_mark = rt->class_array[p->class_id].gc_mark; 5674 if (gc_mark) 5675 gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); 5676 } 5677 } 5678 break; 5679 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 5680 /* the template objects can be part of a cycle */ 5681 { 5682 JSFunctionBytecode *b = (JSFunctionBytecode *)gp; 5683 int i; 5684 for(i = 0; i < b->cpool_count; i++) { 5685 JS_MarkValue(rt, b->cpool[i], mark_func); 5686 } 5687 if (b->realm) 5688 mark_func(rt, &b->realm->header); 5689 } 5690 break; 5691 case JS_GC_OBJ_TYPE_VAR_REF: 5692 { 5693 JSVarRef *var_ref = (JSVarRef *)gp; 5694 if (var_ref->is_detached) { 5695 JS_MarkValue(rt, *var_ref->pvalue, mark_func); 5696 } else if (var_ref->async_func) { 5697 mark_func(rt, &var_ref->async_func->header); 5698 } 5699 } 5700 break; 5701 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 5702 { 5703 JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp; 5704 JSStackFrame *sf = &s->frame; 5705 JSValue *sp; 5706 5707 if (!s->is_completed) { 5708 JS_MarkValue(rt, sf->cur_func, mark_func); 5709 JS_MarkValue(rt, s->this_val, mark_func); 5710 /* sf->cur_sp = NULL if the function is running */ 5711 if (sf->cur_sp) { 5712 /* if the function is running, cur_sp is not known so we 5713 cannot mark the stack. Marking the variables is not needed 5714 because a running function cannot be part of a removable 5715 cycle */ 5716 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) 5717 JS_MarkValue(rt, *sp, mark_func); 5718 } 5719 } 5720 JS_MarkValue(rt, s->resolving_funcs[0], mark_func); 5721 JS_MarkValue(rt, s->resolving_funcs[1], mark_func); 5722 } 5723 break; 5724 case JS_GC_OBJ_TYPE_SHAPE: 5725 { 5726 JSShape *sh = (JSShape *)gp; 5727 if (sh->proto != NULL) { 5728 mark_func(rt, &sh->proto->header); 5729 } 5730 } 5731 break; 5732 case JS_GC_OBJ_TYPE_JS_CONTEXT: 5733 { 5734 JSContext *ctx = (JSContext *)gp; 5735 JS_MarkContext(rt, ctx, mark_func); 5736 } 5737 break; 5738 default: 5739 abort(); 5740 } 5741 } 5742 5743 static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) 5744 { 5745 assert(p->ref_count > 0); 5746 p->ref_count--; 5747 if (p->ref_count == 0 && p->mark == 1) { 5748 list_del(&p->link); 5749 list_add_tail(&p->link, &rt->tmp_obj_list); 5750 } 5751 } 5752 5753 static void gc_decref(JSRuntime *rt) 5754 { 5755 struct list_head *el, *el1; 5756 JSGCObjectHeader *p; 5757 5758 init_list_head(&rt->tmp_obj_list); 5759 5760 /* decrement the refcount of all the children of all the GC 5761 objects and move the GC objects with zero refcount to 5762 tmp_obj_list */ 5763 list_for_each_safe(el, el1, &rt->gc_obj_list) { 5764 p = list_entry(el, JSGCObjectHeader, link); 5765 assert(p->mark == 0); 5766 mark_children(rt, p, gc_decref_child); 5767 p->mark = 1; 5768 if (p->ref_count == 0) { 5769 list_del(&p->link); 5770 list_add_tail(&p->link, &rt->tmp_obj_list); 5771 } 5772 } 5773 } 5774 5775 static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) 5776 { 5777 p->ref_count++; 5778 if (p->ref_count == 1) { 5779 /* ref_count was 0: remove from tmp_obj_list and add at the 5780 end of gc_obj_list */ 5781 list_del(&p->link); 5782 list_add_tail(&p->link, &rt->gc_obj_list); 5783 p->mark = 0; /* reset the mark for the next GC call */ 5784 } 5785 } 5786 5787 static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) 5788 { 5789 p->ref_count++; 5790 } 5791 5792 static void gc_scan(JSRuntime *rt) 5793 { 5794 struct list_head *el; 5795 JSGCObjectHeader *p; 5796 5797 /* keep the objects with a refcount > 0 and their children. */ 5798 list_for_each(el, &rt->gc_obj_list) { 5799 p = list_entry(el, JSGCObjectHeader, link); 5800 assert(p->ref_count > 0); 5801 p->mark = 0; /* reset the mark for the next GC call */ 5802 mark_children(rt, p, gc_scan_incref_child); 5803 } 5804 5805 /* restore the refcount of the objects to be deleted. */ 5806 list_for_each(el, &rt->tmp_obj_list) { 5807 p = list_entry(el, JSGCObjectHeader, link); 5808 mark_children(rt, p, gc_scan_incref_child2); 5809 } 5810 } 5811 5812 static void gc_free_cycles(JSRuntime *rt) 5813 { 5814 struct list_head *el, *el1; 5815 JSGCObjectHeader *p; 5816 #ifdef DUMP_GC_FREE 5817 BOOL header_done = FALSE; 5818 #endif 5819 5820 rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; 5821 5822 for(;;) { 5823 el = rt->tmp_obj_list.next; 5824 if (el == &rt->tmp_obj_list) 5825 break; 5826 p = list_entry(el, JSGCObjectHeader, link); 5827 /* Only need to free the GC object associated with JS values 5828 or async functions. The rest will be automatically removed 5829 because they must be referenced by them. */ 5830 switch(p->gc_obj_type) { 5831 case JS_GC_OBJ_TYPE_JS_OBJECT: 5832 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 5833 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 5834 #ifdef DUMP_GC_FREE 5835 if (!header_done) { 5836 printf("Freeing cycles:\n"); 5837 JS_DumpObjectHeader(rt); 5838 header_done = TRUE; 5839 } 5840 JS_DumpGCObject(rt, p); 5841 #endif 5842 free_gc_object(rt, p); 5843 break; 5844 default: 5845 list_del(&p->link); 5846 list_add_tail(&p->link, &rt->gc_zero_ref_count_list); 5847 break; 5848 } 5849 } 5850 rt->gc_phase = JS_GC_PHASE_NONE; 5851 5852 list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { 5853 p = list_entry(el, JSGCObjectHeader, link); 5854 assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || 5855 p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || 5856 p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION); 5857 js_free_rt(rt, p); 5858 } 5859 5860 init_list_head(&rt->gc_zero_ref_count_list); 5861 } 5862 5863 void JS_RunGC(JSRuntime *rt) 5864 { 5865 /* decrement the reference of the children of each object. mark = 5866 1 after this pass. */ 5867 gc_decref(rt); 5868 5869 /* keep the GC objects with a non zero refcount and their childs */ 5870 gc_scan(rt); 5871 5872 /* free the GC objects in a cycle */ 5873 gc_free_cycles(rt); 5874 } 5875 5876 /* Return false if not an object or if the object has already been 5877 freed (zombie objects are visible in finalizers when freeing 5878 cycles). */ 5879 BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj) 5880 { 5881 JSObject *p; 5882 if (!JS_IsObject(obj)) 5883 return FALSE; 5884 p = JS_VALUE_GET_OBJ(obj); 5885 return !p->free_mark; 5886 } 5887 5888 /* Compute memory used by various object types */ 5889 /* XXX: poor man's approach to handling multiply referenced objects */ 5890 typedef struct JSMemoryUsage_helper { 5891 double memory_used_count; 5892 double str_count; 5893 double str_size; 5894 int64_t js_func_count; 5895 double js_func_size; 5896 int64_t js_func_code_size; 5897 int64_t js_func_pc2line_count; 5898 int64_t js_func_pc2line_size; 5899 } JSMemoryUsage_helper; 5900 5901 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp); 5902 5903 static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) 5904 { 5905 if (!str->atom_type) { /* atoms are handled separately */ 5906 double s_ref_count = str->header.ref_count; 5907 hp->str_count += 1 / s_ref_count; 5908 hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + 5909 1 - str->is_wide_char) / s_ref_count); 5910 } 5911 } 5912 5913 static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) 5914 { 5915 int memory_used_count, js_func_size, i; 5916 5917 memory_used_count = 0; 5918 js_func_size = offsetof(JSFunctionBytecode, debug); 5919 if (b->vardefs) { 5920 js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); 5921 } 5922 if (b->cpool) { 5923 js_func_size += b->cpool_count * sizeof(*b->cpool); 5924 for (i = 0; i < b->cpool_count; i++) { 5925 JSValueConst val = b->cpool[i]; 5926 compute_value_size(val, hp); 5927 } 5928 } 5929 if (b->closure_var) { 5930 js_func_size += b->closure_var_count * sizeof(*b->closure_var); 5931 } 5932 if (!b->read_only_bytecode && b->byte_code_buf) { 5933 hp->js_func_code_size += b->byte_code_len; 5934 } 5935 if (b->has_debug) { 5936 js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug); 5937 if (b->debug.source) { 5938 memory_used_count++; 5939 js_func_size += b->debug.source_len + 1; 5940 } 5941 if (b->debug.pc2line_len) { 5942 memory_used_count++; 5943 hp->js_func_pc2line_count += 1; 5944 hp->js_func_pc2line_size += b->debug.pc2line_len; 5945 } 5946 } 5947 hp->js_func_size += js_func_size; 5948 hp->js_func_count += 1; 5949 hp->memory_used_count += memory_used_count; 5950 } 5951 5952 static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) 5953 { 5954 switch(JS_VALUE_GET_TAG(val)) { 5955 case JS_TAG_STRING: 5956 compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); 5957 break; 5958 case JS_TAG_BIG_INT: 5959 #ifdef CONFIG_BIGNUM 5960 case JS_TAG_BIG_FLOAT: 5961 case JS_TAG_BIG_DECIMAL: 5962 #endif 5963 /* should track JSBigFloat usage */ 5964 break; 5965 } 5966 } 5967 5968 void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) 5969 { 5970 struct list_head *el, *el1; 5971 int i; 5972 JSMemoryUsage_helper mem = { 0 }, *hp = &mem; 5973 5974 memset(s, 0, sizeof(*s)); 5975 s->malloc_count = rt->malloc_state.malloc_count; 5976 s->malloc_size = rt->malloc_state.malloc_size; 5977 s->malloc_limit = rt->malloc_state.malloc_limit; 5978 5979 s->memory_used_count = 2; /* rt + rt->class_array */ 5980 s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count; 5981 5982 list_for_each(el, &rt->context_list) { 5983 JSContext *ctx = list_entry(el, JSContext, link); 5984 JSShape *sh = ctx->array_shape; 5985 s->memory_used_count += 2; /* ctx + ctx->class_proto */ 5986 s->memory_used_size += sizeof(JSContext) + 5987 sizeof(JSValue) * rt->class_count; 5988 s->binary_object_count += ctx->binary_object_count; 5989 s->binary_object_size += ctx->binary_object_size; 5990 5991 /* the hashed shapes are counted separately */ 5992 if (sh && !sh->is_hashed) { 5993 int hash_size = sh->prop_hash_mask + 1; 5994 s->shape_count++; 5995 s->shape_size += get_shape_size(hash_size, sh->prop_size); 5996 } 5997 list_for_each(el1, &ctx->loaded_modules) { 5998 JSModuleDef *m = list_entry(el1, JSModuleDef, link); 5999 s->memory_used_count += 1; 6000 s->memory_used_size += sizeof(*m); 6001 if (m->req_module_entries) { 6002 s->memory_used_count += 1; 6003 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); 6004 } 6005 if (m->export_entries) { 6006 s->memory_used_count += 1; 6007 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); 6008 for (i = 0; i < m->export_entries_count; i++) { 6009 JSExportEntry *me = &m->export_entries[i]; 6010 if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { 6011 /* potential multiple count */ 6012 s->memory_used_count += 1; 6013 compute_value_size(me->u.local.var_ref->value, hp); 6014 } 6015 } 6016 } 6017 if (m->star_export_entries) { 6018 s->memory_used_count += 1; 6019 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); 6020 } 6021 if (m->import_entries) { 6022 s->memory_used_count += 1; 6023 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); 6024 } 6025 compute_value_size(m->module_ns, hp); 6026 compute_value_size(m->func_obj, hp); 6027 } 6028 } 6029 6030 list_for_each(el, &rt->gc_obj_list) { 6031 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); 6032 JSObject *p; 6033 JSShape *sh; 6034 JSShapeProperty *prs; 6035 6036 /* XXX: could count the other GC object types too */ 6037 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { 6038 compute_bytecode_size((JSFunctionBytecode *)gp, hp); 6039 continue; 6040 } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { 6041 continue; 6042 } 6043 p = (JSObject *)gp; 6044 sh = p->shape; 6045 s->obj_count++; 6046 if (p->prop) { 6047 s->memory_used_count++; 6048 s->prop_size += sh->prop_size * sizeof(*p->prop); 6049 s->prop_count += sh->prop_count; 6050 prs = get_shape_prop(sh); 6051 for(i = 0; i < sh->prop_count; i++) { 6052 JSProperty *pr = &p->prop[i]; 6053 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { 6054 compute_value_size(pr->u.value, hp); 6055 } 6056 prs++; 6057 } 6058 } 6059 /* the hashed shapes are counted separately */ 6060 if (!sh->is_hashed) { 6061 int hash_size = sh->prop_hash_mask + 1; 6062 s->shape_count++; 6063 s->shape_size += get_shape_size(hash_size, sh->prop_size); 6064 } 6065 6066 switch(p->class_id) { 6067 case JS_CLASS_ARRAY: /* u.array | length */ 6068 case JS_CLASS_ARGUMENTS: /* u.array | length */ 6069 s->array_count++; 6070 if (p->fast_array) { 6071 s->fast_array_count++; 6072 if (p->u.array.u.values) { 6073 s->memory_used_count++; 6074 s->memory_used_size += p->u.array.count * 6075 sizeof(*p->u.array.u.values); 6076 s->fast_array_elements += p->u.array.count; 6077 for (i = 0; i < p->u.array.count; i++) { 6078 compute_value_size(p->u.array.u.values[i], hp); 6079 } 6080 } 6081 } 6082 break; 6083 case JS_CLASS_NUMBER: /* u.object_data */ 6084 case JS_CLASS_STRING: /* u.object_data */ 6085 case JS_CLASS_BOOLEAN: /* u.object_data */ 6086 case JS_CLASS_SYMBOL: /* u.object_data */ 6087 case JS_CLASS_DATE: /* u.object_data */ 6088 case JS_CLASS_BIG_INT: /* u.object_data */ 6089 #ifdef CONFIG_BIGNUM 6090 case JS_CLASS_BIG_FLOAT: /* u.object_data */ 6091 case JS_CLASS_BIG_DECIMAL: /* u.object_data */ 6092 #endif 6093 compute_value_size(p->u.object_data, hp); 6094 break; 6095 case JS_CLASS_C_FUNCTION: /* u.cfunc */ 6096 s->c_func_count++; 6097 break; 6098 case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ 6099 { 6100 JSFunctionBytecode *b = p->u.func.function_bytecode; 6101 JSVarRef **var_refs = p->u.func.var_refs; 6102 /* home_object: object will be accounted for in list scan */ 6103 if (var_refs) { 6104 s->memory_used_count++; 6105 s->js_func_size += b->closure_var_count * sizeof(*var_refs); 6106 for (i = 0; i < b->closure_var_count; i++) { 6107 if (var_refs[i]) { 6108 double ref_count = var_refs[i]->header.ref_count; 6109 s->memory_used_count += 1 / ref_count; 6110 s->js_func_size += sizeof(*var_refs[i]) / ref_count; 6111 /* handle non object closed values */ 6112 if (var_refs[i]->pvalue == &var_refs[i]->value) { 6113 /* potential multiple count */ 6114 compute_value_size(var_refs[i]->value, hp); 6115 } 6116 } 6117 } 6118 } 6119 } 6120 break; 6121 case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ 6122 { 6123 JSBoundFunction *bf = p->u.bound_function; 6124 /* func_obj and this_val are objects */ 6125 for (i = 0; i < bf->argc; i++) { 6126 compute_value_size(bf->argv[i], hp); 6127 } 6128 s->memory_used_count += 1; 6129 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); 6130 } 6131 break; 6132 case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ 6133 { 6134 JSCFunctionDataRecord *fd = p->u.c_function_data_record; 6135 if (fd) { 6136 for (i = 0; i < fd->data_len; i++) { 6137 compute_value_size(fd->data[i], hp); 6138 } 6139 s->memory_used_count += 1; 6140 s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); 6141 } 6142 } 6143 break; 6144 case JS_CLASS_REGEXP: /* u.regexp */ 6145 compute_jsstring_size(p->u.regexp.pattern, hp); 6146 compute_jsstring_size(p->u.regexp.bytecode, hp); 6147 break; 6148 6149 case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ 6150 { 6151 JSForInIterator *it = p->u.for_in_iterator; 6152 if (it) { 6153 compute_value_size(it->obj, hp); 6154 s->memory_used_count += 1; 6155 s->memory_used_size += sizeof(*it); 6156 } 6157 } 6158 break; 6159 case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ 6160 case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ 6161 { 6162 JSArrayBuffer *abuf = p->u.array_buffer; 6163 if (abuf) { 6164 s->memory_used_count += 1; 6165 s->memory_used_size += sizeof(*abuf); 6166 if (abuf->data) { 6167 s->memory_used_count += 1; 6168 s->memory_used_size += abuf->byte_length; 6169 } 6170 } 6171 } 6172 break; 6173 case JS_CLASS_GENERATOR: /* u.generator_data */ 6174 case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ 6175 case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ 6176 case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ 6177 case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ 6178 case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ 6179 case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ 6180 case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ 6181 case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ 6182 case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ 6183 case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ 6184 case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ 6185 case JS_CLASS_DATAVIEW: /* u.typed_array */ 6186 #ifdef CONFIG_BIGNUM 6187 case JS_CLASS_FLOAT_ENV: /* u.float_env */ 6188 #endif 6189 case JS_CLASS_MAP: /* u.map_state */ 6190 case JS_CLASS_SET: /* u.map_state */ 6191 case JS_CLASS_WEAKMAP: /* u.map_state */ 6192 case JS_CLASS_WEAKSET: /* u.map_state */ 6193 case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ 6194 case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ 6195 case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ 6196 case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ 6197 case JS_CLASS_PROXY: /* u.proxy_data */ 6198 case JS_CLASS_PROMISE: /* u.promise_data */ 6199 case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ 6200 case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ 6201 case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ 6202 case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ 6203 case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ 6204 case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ 6205 /* TODO */ 6206 default: 6207 /* XXX: class definition should have an opaque block size */ 6208 if (p->u.opaque) { 6209 s->memory_used_count += 1; 6210 } 6211 break; 6212 } 6213 } 6214 s->obj_size += s->obj_count * sizeof(JSObject); 6215 6216 /* hashed shapes */ 6217 s->memory_used_count++; /* rt->shape_hash */ 6218 s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; 6219 for(i = 0; i < rt->shape_hash_size; i++) { 6220 JSShape *sh; 6221 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { 6222 int hash_size = sh->prop_hash_mask + 1; 6223 s->shape_count++; 6224 s->shape_size += get_shape_size(hash_size, sh->prop_size); 6225 } 6226 } 6227 6228 /* atoms */ 6229 s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ 6230 s->atom_count = rt->atom_count; 6231 s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + 6232 sizeof(rt->atom_hash[0]) * rt->atom_hash_size; 6233 for(i = 0; i < rt->atom_size; i++) { 6234 JSAtomStruct *p = rt->atom_array[i]; 6235 if (!atom_is_free(p)) { 6236 s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + 6237 1 - p->is_wide_char); 6238 } 6239 } 6240 s->str_count = round(mem.str_count); 6241 s->str_size = round(mem.str_size); 6242 s->js_func_count = mem.js_func_count; 6243 s->js_func_size = round(mem.js_func_size); 6244 s->js_func_code_size = mem.js_func_code_size; 6245 s->js_func_pc2line_count = mem.js_func_pc2line_count; 6246 s->js_func_pc2line_size = mem.js_func_pc2line_size; 6247 s->memory_used_count += round(mem.memory_used_count) + 6248 s->atom_count + s->str_count + 6249 s->obj_count + s->shape_count + 6250 s->js_func_count + s->js_func_pc2line_count; 6251 s->memory_used_size += s->atom_size + s->str_size + 6252 s->obj_size + s->prop_size + s->shape_size + 6253 s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; 6254 } 6255 6256 void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) 6257 { 6258 fprintf(fp, "QuickJS memory usage -- " 6259 #ifdef CONFIG_BIGNUM 6260 "BigNum " 6261 #endif 6262 CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", 6263 (int)sizeof(void *) * 8, s->malloc_limit); 6264 #if 1 6265 if (rt) { 6266 static const struct { 6267 const char *name; 6268 size_t size; 6269 } object_types[] = { 6270 { "JSRuntime", sizeof(JSRuntime) }, 6271 { "JSContext", sizeof(JSContext) }, 6272 { "JSObject", sizeof(JSObject) }, 6273 { "JSString", sizeof(JSString) }, 6274 { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, 6275 }; 6276 int i, usage_size_ok = 0; 6277 for(i = 0; i < countof(object_types); i++) { 6278 unsigned int size = object_types[i].size; 6279 void *p = js_malloc_rt(rt, size); 6280 if (p) { 6281 unsigned int size1 = js_malloc_usable_size_rt(rt, p); 6282 if (size1 >= size) { 6283 usage_size_ok = 1; 6284 fprintf(fp, " %3u + %-2u %s\n", 6285 size, size1 - size, object_types[i].name); 6286 } 6287 js_free_rt(rt, p); 6288 } 6289 } 6290 if (!usage_size_ok) { 6291 fprintf(fp, " malloc_usable_size unavailable\n"); 6292 } 6293 { 6294 int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; 6295 int class_id; 6296 struct list_head *el; 6297 list_for_each(el, &rt->gc_obj_list) { 6298 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); 6299 JSObject *p; 6300 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 6301 p = (JSObject *)gp; 6302 obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; 6303 } 6304 } 6305 fprintf(fp, "\n" "JSObject classes\n"); 6306 if (obj_classes[0]) 6307 fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); 6308 for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { 6309 if (obj_classes[class_id] && class_id < rt->class_count) { 6310 char buf[ATOM_GET_STR_BUF_SIZE]; 6311 fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, 6312 JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); 6313 } 6314 } 6315 if (obj_classes[JS_CLASS_INIT_COUNT]) 6316 fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); 6317 } 6318 fprintf(fp, "\n"); 6319 } 6320 #endif 6321 fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); 6322 6323 if (s->malloc_count) { 6324 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", 6325 "memory allocated", s->malloc_count, s->malloc_size, 6326 (double)s->malloc_size / s->malloc_count); 6327 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", 6328 "memory used", s->memory_used_count, s->memory_used_size, 6329 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / 6330 s->memory_used_count)); 6331 } 6332 if (s->atom_count) { 6333 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", 6334 "atoms", s->atom_count, s->atom_size, 6335 (double)s->atom_size / s->atom_count); 6336 } 6337 if (s->str_count) { 6338 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", 6339 "strings", s->str_count, s->str_size, 6340 (double)s->str_size / s->str_count); 6341 } 6342 if (s->obj_count) { 6343 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", 6344 "objects", s->obj_count, s->obj_size, 6345 (double)s->obj_size / s->obj_count); 6346 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", 6347 " properties", s->prop_count, s->prop_size, 6348 (double)s->prop_count / s->obj_count); 6349 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", 6350 " shapes", s->shape_count, s->shape_size, 6351 (double)s->shape_size / s->shape_count); 6352 } 6353 if (s->js_func_count) { 6354 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", 6355 "bytecode functions", s->js_func_count, s->js_func_size); 6356 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", 6357 " bytecode", s->js_func_count, s->js_func_code_size, 6358 (double)s->js_func_code_size / s->js_func_count); 6359 if (s->js_func_pc2line_count) { 6360 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", 6361 " pc2line", s->js_func_pc2line_count, 6362 s->js_func_pc2line_size, 6363 (double)s->js_func_pc2line_size / s->js_func_pc2line_count); 6364 } 6365 } 6366 if (s->c_func_count) { 6367 fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); 6368 } 6369 if (s->array_count) { 6370 fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); 6371 if (s->fast_array_count) { 6372 fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); 6373 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", 6374 " elements", s->fast_array_elements, 6375 s->fast_array_elements * (int)sizeof(JSValue), 6376 (double)s->fast_array_elements / s->fast_array_count); 6377 } 6378 } 6379 if (s->binary_object_count) { 6380 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", 6381 "binary objects", s->binary_object_count, s->binary_object_size); 6382 } 6383 } 6384 6385 JSValue JS_GetGlobalObject(JSContext *ctx) 6386 { 6387 return JS_DupValue(ctx, ctx->global_obj); 6388 } 6389 6390 /* WARNING: obj is freed */ 6391 JSValue JS_Throw(JSContext *ctx, JSValue obj) 6392 { 6393 JSRuntime *rt = ctx->rt; 6394 JS_FreeValue(ctx, rt->current_exception); 6395 rt->current_exception = obj; 6396 return JS_EXCEPTION; 6397 } 6398 6399 /* return the pending exception (cannot be called twice). */ 6400 JSValue JS_GetException(JSContext *ctx) 6401 { 6402 JSValue val; 6403 JSRuntime *rt = ctx->rt; 6404 val = rt->current_exception; 6405 rt->current_exception = JS_UNINITIALIZED; 6406 return val; 6407 } 6408 6409 JS_BOOL JS_HasException(JSContext *ctx) 6410 { 6411 return !JS_IsUninitialized(ctx->rt->current_exception); 6412 } 6413 6414 static void dbuf_put_leb128(DynBuf *s, uint32_t v) 6415 { 6416 uint32_t a; 6417 for(;;) { 6418 a = v & 0x7f; 6419 v >>= 7; 6420 if (v != 0) { 6421 dbuf_putc(s, a | 0x80); 6422 } else { 6423 dbuf_putc(s, a); 6424 break; 6425 } 6426 } 6427 } 6428 6429 static void dbuf_put_sleb128(DynBuf *s, int32_t v1) 6430 { 6431 uint32_t v = v1; 6432 dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); 6433 } 6434 6435 static int get_leb128(uint32_t *pval, const uint8_t *buf, 6436 const uint8_t *buf_end) 6437 { 6438 const uint8_t *ptr = buf; 6439 uint32_t v, a, i; 6440 v = 0; 6441 for(i = 0; i < 5; i++) { 6442 if (unlikely(ptr >= buf_end)) 6443 break; 6444 a = *ptr++; 6445 v |= (a & 0x7f) << (i * 7); 6446 if (!(a & 0x80)) { 6447 *pval = v; 6448 return ptr - buf; 6449 } 6450 } 6451 *pval = 0; 6452 return -1; 6453 } 6454 6455 static int get_sleb128(int32_t *pval, const uint8_t *buf, 6456 const uint8_t *buf_end) 6457 { 6458 int ret; 6459 uint32_t val; 6460 ret = get_leb128(&val, buf, buf_end); 6461 if (ret < 0) { 6462 *pval = 0; 6463 return -1; 6464 } 6465 *pval = (val >> 1) ^ -(val & 1); 6466 return ret; 6467 } 6468 6469 static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, 6470 uint32_t pc_value) 6471 { 6472 const uint8_t *p_end, *p; 6473 int new_line_num, line_num, pc, v, ret; 6474 unsigned int op; 6475 6476 if (!b->has_debug || !b->debug.pc2line_buf) { 6477 /* function was stripped */ 6478 return -1; 6479 } 6480 6481 p = b->debug.pc2line_buf; 6482 p_end = p + b->debug.pc2line_len; 6483 pc = 0; 6484 line_num = b->debug.line_num; 6485 while (p < p_end) { 6486 op = *p++; 6487 if (op == 0) { 6488 uint32_t val; 6489 ret = get_leb128(&val, p, p_end); 6490 if (ret < 0) 6491 goto fail; 6492 pc += val; 6493 p += ret; 6494 ret = get_sleb128(&v, p, p_end); 6495 if (ret < 0) { 6496 fail: 6497 /* should never happen */ 6498 return b->debug.line_num; 6499 } 6500 p += ret; 6501 new_line_num = line_num + v; 6502 } else { 6503 op -= PC2LINE_OP_FIRST; 6504 pc += (op / PC2LINE_RANGE); 6505 new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; 6506 } 6507 if (pc_value < pc) 6508 return line_num; 6509 line_num = new_line_num; 6510 } 6511 return line_num; 6512 } 6513 6514 /* in order to avoid executing arbitrary code during the stack trace 6515 generation, we only look at simple 'name' properties containing a 6516 string. */ 6517 static const char *get_func_name(JSContext *ctx, JSValueConst func) 6518 { 6519 JSProperty *pr; 6520 JSShapeProperty *prs; 6521 JSValueConst val; 6522 6523 if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) 6524 return NULL; 6525 prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); 6526 if (!prs) 6527 return NULL; 6528 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 6529 return NULL; 6530 val = pr->u.value; 6531 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 6532 return NULL; 6533 return JS_ToCString(ctx, val); 6534 } 6535 6536 #define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) 6537 /* only taken into account if filename is provided */ 6538 #define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) 6539 6540 /* if filename != NULL, an additional level is added with the filename 6541 and line number information (used for parse error). */ 6542 static void build_backtrace(JSContext *ctx, JSValueConst error_obj, 6543 const char *filename, int line_num, 6544 int backtrace_flags) 6545 { 6546 JSStackFrame *sf; 6547 JSValue str; 6548 DynBuf dbuf; 6549 const char *func_name_str; 6550 const char *str1; 6551 JSObject *p; 6552 BOOL backtrace_barrier; 6553 6554 js_dbuf_init(ctx, &dbuf); 6555 if (filename) { 6556 dbuf_printf(&dbuf, " at %s", filename); 6557 if (line_num != -1) 6558 dbuf_printf(&dbuf, ":%d", line_num); 6559 dbuf_putc(&dbuf, '\n'); 6560 str = JS_NewString(ctx, filename); 6561 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str, 6562 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 6563 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num), 6564 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 6565 if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL) 6566 goto done; 6567 } 6568 for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) { 6569 if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { 6570 backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; 6571 continue; 6572 } 6573 func_name_str = get_func_name(ctx, sf->cur_func); 6574 if (!func_name_str || func_name_str[0] == '\0') 6575 str1 = "<anonymous>"; 6576 else 6577 str1 = func_name_str; 6578 dbuf_printf(&dbuf, " at %s", str1); 6579 JS_FreeCString(ctx, func_name_str); 6580 6581 p = JS_VALUE_GET_OBJ(sf->cur_func); 6582 backtrace_barrier = FALSE; 6583 if (js_class_has_bytecode(p->class_id)) { 6584 JSFunctionBytecode *b; 6585 const char *atom_str; 6586 int line_num1; 6587 6588 b = p->u.func.function_bytecode; 6589 backtrace_barrier = b->backtrace_barrier; 6590 if (b->has_debug) { 6591 line_num1 = find_line_num(ctx, b, 6592 sf->cur_pc - b->byte_code_buf - 1); 6593 atom_str = JS_AtomToCString(ctx, b->debug.filename); 6594 dbuf_printf(&dbuf, " (%s", 6595 atom_str ? atom_str : "<null>"); 6596 JS_FreeCString(ctx, atom_str); 6597 if (line_num1 != -1) 6598 dbuf_printf(&dbuf, ":%d", line_num1); 6599 dbuf_putc(&dbuf, ')'); 6600 } 6601 } else { 6602 dbuf_printf(&dbuf, " (native)"); 6603 } 6604 dbuf_putc(&dbuf, '\n'); 6605 /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ 6606 if (backtrace_barrier) 6607 break; 6608 } 6609 done: 6610 dbuf_putc(&dbuf, '\0'); 6611 if (dbuf_error(&dbuf)) 6612 str = JS_NULL; 6613 else 6614 str = JS_NewString(ctx, (char *)dbuf.buf); 6615 dbuf_free(&dbuf); 6616 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, 6617 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 6618 } 6619 6620 /* Note: it is important that no exception is returned by this function */ 6621 static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj) 6622 { 6623 JSObject *p; 6624 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 6625 return FALSE; 6626 p = JS_VALUE_GET_OBJ(obj); 6627 if (p->class_id != JS_CLASS_ERROR) 6628 return FALSE; 6629 if (find_own_property1(p, JS_ATOM_stack)) 6630 return FALSE; 6631 return TRUE; 6632 } 6633 6634 JSValue JS_NewError(JSContext *ctx) 6635 { 6636 return JS_NewObjectClass(ctx, JS_CLASS_ERROR); 6637 } 6638 6639 static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, 6640 const char *fmt, va_list ap, BOOL add_backtrace) 6641 { 6642 char buf[256]; 6643 JSValue obj, ret; 6644 6645 vsnprintf(buf, sizeof(buf), fmt, ap); 6646 obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], 6647 JS_CLASS_ERROR); 6648 if (unlikely(JS_IsException(obj))) { 6649 /* out of memory: throw JS_NULL to avoid recursing */ 6650 obj = JS_NULL; 6651 } else { 6652 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, 6653 JS_NewString(ctx, buf), 6654 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 6655 } 6656 if (add_backtrace) { 6657 build_backtrace(ctx, obj, NULL, 0, 0); 6658 } 6659 ret = JS_Throw(ctx, obj); 6660 return ret; 6661 } 6662 6663 static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, 6664 const char *fmt, va_list ap) 6665 { 6666 JSRuntime *rt = ctx->rt; 6667 JSStackFrame *sf; 6668 BOOL add_backtrace; 6669 6670 /* the backtrace is added later if called from a bytecode function */ 6671 sf = rt->current_stack_frame; 6672 add_backtrace = !rt->in_out_of_memory && 6673 (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); 6674 return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); 6675 } 6676 6677 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) 6678 { 6679 JSValue val; 6680 va_list ap; 6681 6682 va_start(ap, fmt); 6683 val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); 6684 va_end(ap); 6685 return val; 6686 } 6687 6688 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) 6689 { 6690 JSValue val; 6691 va_list ap; 6692 6693 va_start(ap, fmt); 6694 val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); 6695 va_end(ap); 6696 return val; 6697 } 6698 6699 static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) 6700 { 6701 va_list ap; 6702 6703 if ((flags & JS_PROP_THROW) || 6704 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 6705 va_start(ap, fmt); 6706 JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); 6707 va_end(ap); 6708 return -1; 6709 } else { 6710 return FALSE; 6711 } 6712 } 6713 6714 /* never use it directly */ 6715 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) 6716 { 6717 char buf[ATOM_GET_STR_BUF_SIZE]; 6718 return JS_ThrowTypeError(ctx, fmt, 6719 JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); 6720 } 6721 6722 /* never use it directly */ 6723 static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) 6724 { 6725 char buf[ATOM_GET_STR_BUF_SIZE]; 6726 return JS_ThrowSyntaxError(ctx, fmt, 6727 JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); 6728 } 6729 6730 /* %s is replaced by 'atom'. The macro is used so that gcc can check 6731 the format string. */ 6732 #define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") 6733 #define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") 6734 6735 static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) 6736 { 6737 if ((flags & JS_PROP_THROW) || 6738 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 6739 JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); 6740 return -1; 6741 } else { 6742 return FALSE; 6743 } 6744 } 6745 6746 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) 6747 { 6748 JSValue val; 6749 va_list ap; 6750 6751 va_start(ap, fmt); 6752 val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); 6753 va_end(ap); 6754 return val; 6755 } 6756 6757 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) 6758 { 6759 JSValue val; 6760 va_list ap; 6761 6762 va_start(ap, fmt); 6763 val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); 6764 va_end(ap); 6765 return val; 6766 } 6767 6768 JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) 6769 { 6770 JSValue val; 6771 va_list ap; 6772 6773 va_start(ap, fmt); 6774 val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); 6775 va_end(ap); 6776 return val; 6777 } 6778 6779 JSValue JS_ThrowOutOfMemory(JSContext *ctx) 6780 { 6781 JSRuntime *rt = ctx->rt; 6782 if (!rt->in_out_of_memory) { 6783 rt->in_out_of_memory = TRUE; 6784 JS_ThrowInternalError(ctx, "out of memory"); 6785 rt->in_out_of_memory = FALSE; 6786 } 6787 return JS_EXCEPTION; 6788 } 6789 6790 static JSValue JS_ThrowStackOverflow(JSContext *ctx) 6791 { 6792 return JS_ThrowInternalError(ctx, "stack overflow"); 6793 } 6794 6795 static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) 6796 { 6797 return JS_ThrowTypeError(ctx, "not an object"); 6798 } 6799 6800 static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) 6801 { 6802 return JS_ThrowTypeError(ctx, "not a symbol"); 6803 } 6804 6805 static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) 6806 { 6807 char buf[ATOM_GET_STR_BUF_SIZE]; 6808 return JS_ThrowReferenceError(ctx, "'%s' is not defined", 6809 JS_AtomGetStr(ctx, buf, sizeof(buf), name)); 6810 } 6811 6812 static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) 6813 { 6814 char buf[ATOM_GET_STR_BUF_SIZE]; 6815 return JS_ThrowReferenceError(ctx, "%s is not initialized", 6816 name == JS_ATOM_NULL ? "lexical variable" : 6817 JS_AtomGetStr(ctx, buf, sizeof(buf), name)); 6818 } 6819 6820 static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, 6821 JSFunctionBytecode *b, 6822 int idx, BOOL is_ref) 6823 { 6824 JSAtom atom = JS_ATOM_NULL; 6825 if (is_ref) { 6826 atom = b->closure_var[idx].var_name; 6827 } else { 6828 /* not present if the function is stripped and contains no eval() */ 6829 if (b->vardefs) 6830 atom = b->vardefs[b->arg_count + idx].var_name; 6831 } 6832 return JS_ThrowReferenceErrorUninitialized(ctx, atom); 6833 } 6834 6835 static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) 6836 { 6837 JSRuntime *rt = ctx->rt; 6838 JSAtom name; 6839 name = rt->class_array[class_id].class_name; 6840 return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); 6841 } 6842 6843 static no_inline __exception int __js_poll_interrupts(JSContext *ctx) 6844 { 6845 JSRuntime *rt = ctx->rt; 6846 ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; 6847 if (rt->interrupt_handler) { 6848 if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { 6849 /* XXX: should set a specific flag to avoid catching */ 6850 JS_ThrowInternalError(ctx, "interrupted"); 6851 JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); 6852 return -1; 6853 } 6854 } 6855 return 0; 6856 } 6857 6858 static inline __exception int js_poll_interrupts(JSContext *ctx) 6859 { 6860 if (unlikely(--ctx->interrupt_counter <= 0)) { 6861 return __js_poll_interrupts(ctx); 6862 } else { 6863 return 0; 6864 } 6865 } 6866 6867 /* return -1 (exception) or TRUE/FALSE */ 6868 static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, 6869 JSValueConst proto_val, 6870 BOOL throw_flag) 6871 { 6872 JSObject *proto, *p, *p1; 6873 JSShape *sh; 6874 6875 if (throw_flag) { 6876 if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || 6877 JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) 6878 goto not_obj; 6879 } else { 6880 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 6881 goto not_obj; 6882 } 6883 p = JS_VALUE_GET_OBJ(obj); 6884 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { 6885 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { 6886 not_obj: 6887 JS_ThrowTypeErrorNotAnObject(ctx); 6888 return -1; 6889 } 6890 proto = NULL; 6891 } else { 6892 proto = JS_VALUE_GET_OBJ(proto_val); 6893 } 6894 6895 if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 6896 return TRUE; 6897 6898 if (unlikely(p->class_id == JS_CLASS_PROXY)) 6899 return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); 6900 sh = p->shape; 6901 if (sh->proto == proto) 6902 return TRUE; 6903 if (!p->extensible) { 6904 if (throw_flag) { 6905 JS_ThrowTypeError(ctx, "object is not extensible"); 6906 return -1; 6907 } else { 6908 return FALSE; 6909 } 6910 } 6911 if (proto) { 6912 /* check if there is a cycle */ 6913 p1 = proto; 6914 do { 6915 if (p1 == p) { 6916 if (throw_flag) { 6917 JS_ThrowTypeError(ctx, "circular prototype chain"); 6918 return -1; 6919 } else { 6920 return FALSE; 6921 } 6922 } 6923 /* Note: for Proxy objects, proto is NULL */ 6924 p1 = p1->shape->proto; 6925 } while (p1 != NULL); 6926 JS_DupValue(ctx, proto_val); 6927 } 6928 6929 if (js_shape_prepare_update(ctx, p, NULL)) 6930 return -1; 6931 sh = p->shape; 6932 if (sh->proto) 6933 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); 6934 sh->proto = proto; 6935 return TRUE; 6936 } 6937 6938 /* return -1 (exception) or TRUE/FALSE */ 6939 int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) 6940 { 6941 return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); 6942 } 6943 6944 /* Only works for primitive types, otherwise return JS_NULL. */ 6945 static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val) 6946 { 6947 switch(JS_VALUE_GET_NORM_TAG(val)) { 6948 case JS_TAG_BIG_INT: 6949 val = ctx->class_proto[JS_CLASS_BIG_INT]; 6950 break; 6951 #ifdef CONFIG_BIGNUM 6952 case JS_TAG_BIG_FLOAT: 6953 val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; 6954 break; 6955 case JS_TAG_BIG_DECIMAL: 6956 val = ctx->class_proto[JS_CLASS_BIG_DECIMAL]; 6957 break; 6958 #endif 6959 case JS_TAG_INT: 6960 case JS_TAG_FLOAT64: 6961 val = ctx->class_proto[JS_CLASS_NUMBER]; 6962 break; 6963 case JS_TAG_BOOL: 6964 val = ctx->class_proto[JS_CLASS_BOOLEAN]; 6965 break; 6966 case JS_TAG_STRING: 6967 val = ctx->class_proto[JS_CLASS_STRING]; 6968 break; 6969 case JS_TAG_SYMBOL: 6970 val = ctx->class_proto[JS_CLASS_SYMBOL]; 6971 break; 6972 case JS_TAG_OBJECT: 6973 case JS_TAG_NULL: 6974 case JS_TAG_UNDEFINED: 6975 default: 6976 val = JS_NULL; 6977 break; 6978 } 6979 return val; 6980 } 6981 6982 /* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ 6983 JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) 6984 { 6985 JSValue val; 6986 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 6987 JSObject *p; 6988 p = JS_VALUE_GET_OBJ(obj); 6989 if (unlikely(p->class_id == JS_CLASS_PROXY)) { 6990 val = js_proxy_getPrototypeOf(ctx, obj); 6991 } else { 6992 p = p->shape->proto; 6993 if (!p) 6994 val = JS_NULL; 6995 else 6996 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 6997 } 6998 } else { 6999 val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj)); 7000 } 7001 return val; 7002 } 7003 7004 static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) 7005 { 7006 JSValue obj1; 7007 obj1 = JS_GetPrototype(ctx, obj); 7008 JS_FreeValue(ctx, obj); 7009 return obj1; 7010 } 7011 7012 /* return TRUE, FALSE or (-1) in case of exception */ 7013 static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val, 7014 JSValueConst obj) 7015 { 7016 JSValue obj_proto; 7017 JSObject *proto; 7018 const JSObject *p, *proto1; 7019 BOOL ret; 7020 7021 if (!JS_IsFunction(ctx, obj)) 7022 return FALSE; 7023 p = JS_VALUE_GET_OBJ(obj); 7024 if (p->class_id == JS_CLASS_BOUND_FUNCTION) { 7025 JSBoundFunction *s = p->u.bound_function; 7026 return JS_IsInstanceOf(ctx, val, s->func_obj); 7027 } 7028 7029 /* Only explicitly boxed values are instances of constructors */ 7030 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 7031 return FALSE; 7032 obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); 7033 if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { 7034 if (!JS_IsException(obj_proto)) 7035 JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); 7036 ret = -1; 7037 goto done; 7038 } 7039 proto = JS_VALUE_GET_OBJ(obj_proto); 7040 p = JS_VALUE_GET_OBJ(val); 7041 for(;;) { 7042 proto1 = p->shape->proto; 7043 if (!proto1) { 7044 /* slow case if proxy in the prototype chain */ 7045 if (unlikely(p->class_id == JS_CLASS_PROXY)) { 7046 JSValue obj1; 7047 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); 7048 for(;;) { 7049 obj1 = JS_GetPrototypeFree(ctx, obj1); 7050 if (JS_IsException(obj1)) { 7051 ret = -1; 7052 break; 7053 } 7054 if (JS_IsNull(obj1)) { 7055 ret = FALSE; 7056 break; 7057 } 7058 if (proto == JS_VALUE_GET_OBJ(obj1)) { 7059 JS_FreeValue(ctx, obj1); 7060 ret = TRUE; 7061 break; 7062 } 7063 /* must check for timeout to avoid infinite loop */ 7064 if (js_poll_interrupts(ctx)) { 7065 JS_FreeValue(ctx, obj1); 7066 ret = -1; 7067 break; 7068 } 7069 } 7070 } else { 7071 ret = FALSE; 7072 } 7073 break; 7074 } 7075 p = proto1; 7076 if (proto == p) { 7077 ret = TRUE; 7078 break; 7079 } 7080 } 7081 done: 7082 JS_FreeValue(ctx, obj_proto); 7083 return ret; 7084 } 7085 7086 /* return TRUE, FALSE or (-1) in case of exception */ 7087 int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj) 7088 { 7089 JSValue method; 7090 7091 if (!JS_IsObject(obj)) 7092 goto fail; 7093 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); 7094 if (JS_IsException(method)) 7095 return -1; 7096 if (!JS_IsNull(method) && !JS_IsUndefined(method)) { 7097 JSValue ret; 7098 ret = JS_CallFree(ctx, method, obj, 1, &val); 7099 return JS_ToBoolFree(ctx, ret); 7100 } 7101 7102 /* legacy case */ 7103 if (!JS_IsFunction(ctx, obj)) { 7104 fail: 7105 JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); 7106 return -1; 7107 } 7108 return JS_OrdinaryIsInstanceOf(ctx, val, obj); 7109 } 7110 7111 /* return the value associated to the autoinit property or an exception */ 7112 typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); 7113 7114 static JSAutoInitFunc *js_autoinit_func_table[] = { 7115 js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ 7116 js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ 7117 JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ 7118 }; 7119 7120 /* warning: 'prs' is reallocated after it */ 7121 static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, 7122 JSProperty *pr, JSShapeProperty *prs) 7123 { 7124 JSValue val; 7125 JSContext *realm; 7126 JSAutoInitFunc *func; 7127 7128 if (js_shape_prepare_update(ctx, p, &prs)) 7129 return -1; 7130 7131 realm = js_autoinit_get_realm(pr); 7132 func = js_autoinit_func_table[js_autoinit_get_id(pr)]; 7133 /* 'func' shall not modify the object properties 'pr' */ 7134 val = func(realm, p, prop, pr->u.init.opaque); 7135 js_autoinit_free(ctx->rt, pr); 7136 prs->flags &= ~JS_PROP_TMASK; 7137 pr->u.value = JS_UNDEFINED; 7138 if (JS_IsException(val)) 7139 return -1; 7140 pr->u.value = val; 7141 return 0; 7142 } 7143 7144 JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, 7145 JSAtom prop, JSValueConst this_obj, 7146 BOOL throw_ref_error) 7147 { 7148 JSObject *p; 7149 JSProperty *pr; 7150 JSShapeProperty *prs; 7151 uint32_t tag; 7152 7153 tag = JS_VALUE_GET_TAG(obj); 7154 if (unlikely(tag != JS_TAG_OBJECT)) { 7155 switch(tag) { 7156 case JS_TAG_NULL: 7157 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); 7158 case JS_TAG_UNDEFINED: 7159 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); 7160 case JS_TAG_EXCEPTION: 7161 return JS_EXCEPTION; 7162 case JS_TAG_STRING: 7163 { 7164 JSString *p1 = JS_VALUE_GET_STRING(obj); 7165 if (__JS_AtomIsTaggedInt(prop)) { 7166 uint32_t idx, ch; 7167 idx = __JS_AtomToUInt32(prop); 7168 if (idx < p1->len) { 7169 if (p1->is_wide_char) 7170 ch = p1->u.str16[idx]; 7171 else 7172 ch = p1->u.str8[idx]; 7173 return js_new_string_char(ctx, ch); 7174 } 7175 } else if (prop == JS_ATOM_length) { 7176 return JS_NewInt32(ctx, p1->len); 7177 } 7178 } 7179 break; 7180 default: 7181 break; 7182 } 7183 /* cannot raise an exception */ 7184 p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); 7185 if (!p) 7186 return JS_UNDEFINED; 7187 } else { 7188 p = JS_VALUE_GET_OBJ(obj); 7189 } 7190 7191 for(;;) { 7192 prs = find_own_property(&pr, p, prop); 7193 if (prs) { 7194 /* found */ 7195 if (unlikely(prs->flags & JS_PROP_TMASK)) { 7196 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 7197 if (unlikely(!pr->u.getset.getter)) { 7198 return JS_UNDEFINED; 7199 } else { 7200 JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); 7201 /* Note: the field could be removed in the getter */ 7202 func = JS_DupValue(ctx, func); 7203 return JS_CallFree(ctx, func, this_obj, 0, NULL); 7204 } 7205 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 7206 JSValue val = *pr->u.var_ref->pvalue; 7207 if (unlikely(JS_IsUninitialized(val))) 7208 return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 7209 return JS_DupValue(ctx, val); 7210 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 7211 /* Instantiate property and retry */ 7212 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 7213 return JS_EXCEPTION; 7214 continue; 7215 } 7216 } else { 7217 return JS_DupValue(ctx, pr->u.value); 7218 } 7219 } 7220 if (unlikely(p->is_exotic)) { 7221 /* exotic behaviors */ 7222 if (p->fast_array) { 7223 if (__JS_AtomIsTaggedInt(prop)) { 7224 uint32_t idx = __JS_AtomToUInt32(prop); 7225 if (idx < p->u.array.count) { 7226 /* we avoid duplicating the code */ 7227 return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); 7228 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 7229 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 7230 return JS_UNDEFINED; 7231 } 7232 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 7233 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 7234 int ret; 7235 ret = JS_AtomIsNumericIndex(ctx, prop); 7236 if (ret != 0) { 7237 if (ret < 0) 7238 return JS_EXCEPTION; 7239 return JS_UNDEFINED; 7240 } 7241 } 7242 } else { 7243 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 7244 if (em) { 7245 if (em->get_property) { 7246 JSValue obj1, retval; 7247 /* XXX: should pass throw_ref_error */ 7248 /* Note: if 'p' is a prototype, it can be 7249 freed in the called function */ 7250 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7251 retval = em->get_property(ctx, obj1, prop, this_obj); 7252 JS_FreeValue(ctx, obj1); 7253 return retval; 7254 } 7255 if (em->get_own_property) { 7256 JSPropertyDescriptor desc; 7257 int ret; 7258 JSValue obj1; 7259 7260 /* Note: if 'p' is a prototype, it can be 7261 freed in the called function */ 7262 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7263 ret = em->get_own_property(ctx, &desc, obj1, prop); 7264 JS_FreeValue(ctx, obj1); 7265 if (ret < 0) 7266 return JS_EXCEPTION; 7267 if (ret) { 7268 if (desc.flags & JS_PROP_GETSET) { 7269 JS_FreeValue(ctx, desc.setter); 7270 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); 7271 } else { 7272 return desc.value; 7273 } 7274 } 7275 } 7276 } 7277 } 7278 } 7279 p = p->shape->proto; 7280 if (!p) 7281 break; 7282 } 7283 if (unlikely(throw_ref_error)) { 7284 return JS_ThrowReferenceErrorNotDefined(ctx, prop); 7285 } else { 7286 return JS_UNDEFINED; 7287 } 7288 } 7289 7290 static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) 7291 { 7292 return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", 7293 atom); 7294 } 7295 7296 /* Private fields can be added even on non extensible objects or 7297 Proxies */ 7298 static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, 7299 JSValueConst name, JSValue val) 7300 { 7301 JSObject *p; 7302 JSShapeProperty *prs; 7303 JSProperty *pr; 7304 JSAtom prop; 7305 7306 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 7307 JS_ThrowTypeErrorNotAnObject(ctx); 7308 goto fail; 7309 } 7310 /* safety check */ 7311 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { 7312 JS_ThrowTypeErrorNotASymbol(ctx); 7313 goto fail; 7314 } 7315 prop = js_symbol_to_atom(ctx, (JSValue)name); 7316 p = JS_VALUE_GET_OBJ(obj); 7317 prs = find_own_property(&pr, p, prop); 7318 if (prs) { 7319 JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", 7320 prop); 7321 goto fail; 7322 } 7323 pr = add_property(ctx, p, prop, JS_PROP_C_W_E); 7324 if (unlikely(!pr)) { 7325 fail: 7326 JS_FreeValue(ctx, val); 7327 return -1; 7328 } 7329 pr->u.value = val; 7330 return 0; 7331 } 7332 7333 static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, 7334 JSValueConst name) 7335 { 7336 JSObject *p; 7337 JSShapeProperty *prs; 7338 JSProperty *pr; 7339 JSAtom prop; 7340 7341 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 7342 return JS_ThrowTypeErrorNotAnObject(ctx); 7343 /* safety check */ 7344 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) 7345 return JS_ThrowTypeErrorNotASymbol(ctx); 7346 prop = js_symbol_to_atom(ctx, (JSValue)name); 7347 p = JS_VALUE_GET_OBJ(obj); 7348 prs = find_own_property(&pr, p, prop); 7349 if (!prs) { 7350 JS_ThrowTypeErrorPrivateNotFound(ctx, prop); 7351 return JS_EXCEPTION; 7352 } 7353 return JS_DupValue(ctx, pr->u.value); 7354 } 7355 7356 static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, 7357 JSValueConst name, JSValue val) 7358 { 7359 JSObject *p; 7360 JSShapeProperty *prs; 7361 JSProperty *pr; 7362 JSAtom prop; 7363 7364 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 7365 JS_ThrowTypeErrorNotAnObject(ctx); 7366 goto fail; 7367 } 7368 /* safety check */ 7369 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { 7370 JS_ThrowTypeErrorNotASymbol(ctx); 7371 goto fail; 7372 } 7373 prop = js_symbol_to_atom(ctx, (JSValue)name); 7374 p = JS_VALUE_GET_OBJ(obj); 7375 prs = find_own_property(&pr, p, prop); 7376 if (!prs) { 7377 JS_ThrowTypeErrorPrivateNotFound(ctx, prop); 7378 fail: 7379 JS_FreeValue(ctx, val); 7380 return -1; 7381 } 7382 set_value(ctx, &pr->u.value, val); 7383 return 0; 7384 } 7385 7386 /* add a private brand field to 'home_obj' if not already present and 7387 if obj is != null add a private brand to it */ 7388 static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) 7389 { 7390 JSObject *p, *p1; 7391 JSShapeProperty *prs; 7392 JSProperty *pr; 7393 JSValue brand; 7394 JSAtom brand_atom; 7395 7396 if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { 7397 JS_ThrowTypeErrorNotAnObject(ctx); 7398 return -1; 7399 } 7400 p = JS_VALUE_GET_OBJ(home_obj); 7401 prs = find_own_property(&pr, p, JS_ATOM_Private_brand); 7402 if (!prs) { 7403 /* if the brand is not present, add it */ 7404 brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); 7405 if (JS_IsException(brand)) 7406 return -1; 7407 pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); 7408 if (!pr) { 7409 JS_FreeValue(ctx, brand); 7410 return -1; 7411 } 7412 pr->u.value = JS_DupValue(ctx, brand); 7413 } else { 7414 brand = JS_DupValue(ctx, pr->u.value); 7415 } 7416 brand_atom = js_symbol_to_atom(ctx, brand); 7417 7418 if (JS_IsObject(obj)) { 7419 p1 = JS_VALUE_GET_OBJ(obj); 7420 prs = find_own_property(&pr, p1, brand_atom); 7421 if (unlikely(prs)) { 7422 JS_FreeAtom(ctx, brand_atom); 7423 JS_ThrowTypeError(ctx, "private method is already present"); 7424 return -1; 7425 } 7426 pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); 7427 JS_FreeAtom(ctx, brand_atom); 7428 if (!pr) 7429 return -1; 7430 pr->u.value = JS_UNDEFINED; 7431 } else { 7432 JS_FreeAtom(ctx, brand_atom); 7433 } 7434 return 0; 7435 } 7436 7437 /* return a boolean telling if the brand of the home object of 'func' 7438 is present on 'obj' or -1 in case of exception */ 7439 static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) 7440 { 7441 JSObject *p, *p1, *home_obj; 7442 JSShapeProperty *prs; 7443 JSProperty *pr; 7444 JSValueConst brand; 7445 7446 /* get the home object of 'func' */ 7447 if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) 7448 goto not_obj; 7449 p1 = JS_VALUE_GET_OBJ(func); 7450 if (!js_class_has_bytecode(p1->class_id)) 7451 goto not_obj; 7452 home_obj = p1->u.func.home_object; 7453 if (!home_obj) 7454 goto not_obj; 7455 prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); 7456 if (!prs) { 7457 JS_ThrowTypeError(ctx, "expecting <brand> private field"); 7458 return -1; 7459 } 7460 brand = pr->u.value; 7461 /* safety check */ 7462 if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) 7463 goto not_obj; 7464 7465 /* get the brand array of 'obj' */ 7466 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { 7467 not_obj: 7468 JS_ThrowTypeErrorNotAnObject(ctx); 7469 return -1; 7470 } 7471 p = JS_VALUE_GET_OBJ(obj); 7472 prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); 7473 return (prs != NULL); 7474 } 7475 7476 static uint32_t js_string_obj_get_length(JSContext *ctx, 7477 JSValueConst obj) 7478 { 7479 JSObject *p; 7480 JSString *p1; 7481 uint32_t len = 0; 7482 7483 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ 7484 p = JS_VALUE_GET_OBJ(obj); 7485 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { 7486 p1 = JS_VALUE_GET_STRING(p->u.object_data); 7487 len = p1->len; 7488 } 7489 return len; 7490 } 7491 7492 static int num_keys_cmp(const void *p1, const void *p2, void *opaque) 7493 { 7494 JSContext *ctx = opaque; 7495 JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; 7496 JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; 7497 uint32_t v1, v2; 7498 BOOL atom1_is_integer, atom2_is_integer; 7499 7500 atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); 7501 atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); 7502 assert(atom1_is_integer && atom2_is_integer); 7503 if (v1 < v2) 7504 return -1; 7505 else if (v1 == v2) 7506 return 0; 7507 else 7508 return 1; 7509 } 7510 7511 static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) 7512 { 7513 uint32_t i; 7514 if (tab) { 7515 for(i = 0; i < len; i++) 7516 JS_FreeAtom(ctx, tab[i].atom); 7517 js_free(ctx, tab); 7518 } 7519 } 7520 7521 /* return < 0 in case if exception, 0 if OK. ptab and its atoms must 7522 be freed by the user. */ 7523 static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, 7524 JSPropertyEnum **ptab, 7525 uint32_t *plen, 7526 JSObject *p, int flags) 7527 { 7528 int i, j; 7529 JSShape *sh; 7530 JSShapeProperty *prs; 7531 JSPropertyEnum *tab_atom, *tab_exotic; 7532 JSAtom atom; 7533 uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; 7534 uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; 7535 BOOL is_enumerable, num_sorted; 7536 uint32_t num_key; 7537 JSAtomKindEnum kind; 7538 7539 /* clear pointer for consistency in case of failure */ 7540 *ptab = NULL; 7541 *plen = 0; 7542 7543 /* compute the number of returned properties */ 7544 num_keys_count = 0; 7545 str_keys_count = 0; 7546 sym_keys_count = 0; 7547 exotic_keys_count = 0; 7548 exotic_count = 0; 7549 tab_exotic = NULL; 7550 sh = p->shape; 7551 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 7552 atom = prs->atom; 7553 if (atom != JS_ATOM_NULL) { 7554 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); 7555 kind = JS_AtomGetKind(ctx, atom); 7556 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 7557 ((flags >> kind) & 1) != 0) { 7558 /* need to raise an exception in case of the module 7559 name space (implicit GetOwnProperty) */ 7560 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && 7561 (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { 7562 JSVarRef *var_ref = p->prop[i].u.var_ref; 7563 if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { 7564 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 7565 return -1; 7566 } 7567 } 7568 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { 7569 num_keys_count++; 7570 } else if (kind == JS_ATOM_KIND_STRING) { 7571 str_keys_count++; 7572 } else { 7573 sym_keys_count++; 7574 } 7575 } 7576 } 7577 } 7578 7579 if (p->is_exotic) { 7580 if (p->fast_array) { 7581 if (flags & JS_GPN_STRING_MASK) { 7582 num_keys_count += p->u.array.count; 7583 } 7584 } else if (p->class_id == JS_CLASS_STRING) { 7585 if (flags & JS_GPN_STRING_MASK) { 7586 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7587 } 7588 } else { 7589 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 7590 if (em && em->get_own_property_names) { 7591 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, 7592 JS_MKPTR(JS_TAG_OBJECT, p))) 7593 return -1; 7594 for(i = 0; i < exotic_count; i++) { 7595 atom = tab_exotic[i].atom; 7596 kind = JS_AtomGetKind(ctx, atom); 7597 if (((flags >> kind) & 1) != 0) { 7598 is_enumerable = FALSE; 7599 if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { 7600 JSPropertyDescriptor desc; 7601 int res; 7602 /* set the "is_enumerable" field if necessary */ 7603 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 7604 if (res < 0) { 7605 js_free_prop_enum(ctx, tab_exotic, exotic_count); 7606 return -1; 7607 } 7608 if (res) { 7609 is_enumerable = 7610 ((desc.flags & JS_PROP_ENUMERABLE) != 0); 7611 js_free_desc(ctx, &desc); 7612 } 7613 tab_exotic[i].is_enumerable = is_enumerable; 7614 } 7615 if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { 7616 exotic_keys_count++; 7617 } 7618 } 7619 } 7620 } 7621 } 7622 } 7623 7624 /* fill them */ 7625 7626 atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; 7627 /* avoid allocating 0 bytes */ 7628 tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); 7629 if (!tab_atom) { 7630 js_free_prop_enum(ctx, tab_exotic, exotic_count); 7631 return -1; 7632 } 7633 7634 num_index = 0; 7635 str_index = num_keys_count; 7636 sym_index = str_index + str_keys_count; 7637 7638 num_sorted = TRUE; 7639 sh = p->shape; 7640 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 7641 atom = prs->atom; 7642 if (atom != JS_ATOM_NULL) { 7643 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); 7644 kind = JS_AtomGetKind(ctx, atom); 7645 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 7646 ((flags >> kind) & 1) != 0) { 7647 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { 7648 j = num_index++; 7649 num_sorted = FALSE; 7650 } else if (kind == JS_ATOM_KIND_STRING) { 7651 j = str_index++; 7652 } else { 7653 j = sym_index++; 7654 } 7655 tab_atom[j].atom = JS_DupAtom(ctx, atom); 7656 tab_atom[j].is_enumerable = is_enumerable; 7657 } 7658 } 7659 } 7660 7661 if (p->is_exotic) { 7662 int len; 7663 if (p->fast_array) { 7664 if (flags & JS_GPN_STRING_MASK) { 7665 len = p->u.array.count; 7666 goto add_array_keys; 7667 } 7668 } else if (p->class_id == JS_CLASS_STRING) { 7669 if (flags & JS_GPN_STRING_MASK) { 7670 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7671 add_array_keys: 7672 for(i = 0; i < len; i++) { 7673 tab_atom[num_index].atom = __JS_AtomFromUInt32(i); 7674 if (tab_atom[num_index].atom == JS_ATOM_NULL) { 7675 js_free_prop_enum(ctx, tab_atom, num_index); 7676 return -1; 7677 } 7678 tab_atom[num_index].is_enumerable = TRUE; 7679 num_index++; 7680 } 7681 } 7682 } else { 7683 /* Note: exotic keys are not reordered and comes after the object own properties. */ 7684 for(i = 0; i < exotic_count; i++) { 7685 atom = tab_exotic[i].atom; 7686 is_enumerable = tab_exotic[i].is_enumerable; 7687 kind = JS_AtomGetKind(ctx, atom); 7688 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && 7689 ((flags >> kind) & 1) != 0) { 7690 tab_atom[sym_index].atom = atom; 7691 tab_atom[sym_index].is_enumerable = is_enumerable; 7692 sym_index++; 7693 } else { 7694 JS_FreeAtom(ctx, atom); 7695 } 7696 } 7697 js_free(ctx, tab_exotic); 7698 } 7699 } 7700 7701 assert(num_index == num_keys_count); 7702 assert(str_index == num_keys_count + str_keys_count); 7703 assert(sym_index == atom_count); 7704 7705 if (num_keys_count != 0 && !num_sorted) { 7706 rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, 7707 ctx); 7708 } 7709 *ptab = tab_atom; 7710 *plen = atom_count; 7711 return 0; 7712 } 7713 7714 int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, 7715 uint32_t *plen, JSValueConst obj, int flags) 7716 { 7717 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 7718 JS_ThrowTypeErrorNotAnObject(ctx); 7719 return -1; 7720 } 7721 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, 7722 JS_VALUE_GET_OBJ(obj), flags); 7723 } 7724 7725 /* Return -1 if exception, 7726 FALSE if the property does not exist, TRUE if it exists. If TRUE is 7727 returned, the property descriptor 'desc' is filled present. */ 7728 static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, 7729 JSObject *p, JSAtom prop) 7730 { 7731 JSShapeProperty *prs; 7732 JSProperty *pr; 7733 7734 retry: 7735 prs = find_own_property(&pr, p, prop); 7736 if (prs) { 7737 if (desc) { 7738 desc->flags = prs->flags & JS_PROP_C_W_E; 7739 desc->getter = JS_UNDEFINED; 7740 desc->setter = JS_UNDEFINED; 7741 desc->value = JS_UNDEFINED; 7742 if (unlikely(prs->flags & JS_PROP_TMASK)) { 7743 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 7744 desc->flags |= JS_PROP_GETSET; 7745 if (pr->u.getset.getter) 7746 desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 7747 if (pr->u.getset.setter) 7748 desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 7749 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 7750 JSValue val = *pr->u.var_ref->pvalue; 7751 if (unlikely(JS_IsUninitialized(val))) { 7752 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 7753 return -1; 7754 } 7755 desc->value = JS_DupValue(ctx, val); 7756 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 7757 /* Instantiate property and retry */ 7758 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 7759 return -1; 7760 goto retry; 7761 } 7762 } else { 7763 desc->value = JS_DupValue(ctx, pr->u.value); 7764 } 7765 } else { 7766 /* for consistency, send the exception even if desc is NULL */ 7767 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { 7768 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { 7769 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 7770 return -1; 7771 } 7772 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 7773 /* nothing to do: delay instantiation until actual value and/or attributes are read */ 7774 } 7775 } 7776 return TRUE; 7777 } 7778 if (p->is_exotic) { 7779 if (p->fast_array) { 7780 /* specific case for fast arrays */ 7781 if (__JS_AtomIsTaggedInt(prop)) { 7782 uint32_t idx; 7783 idx = __JS_AtomToUInt32(prop); 7784 if (idx < p->u.array.count) { 7785 if (desc) { 7786 desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | 7787 JS_PROP_CONFIGURABLE; 7788 desc->getter = JS_UNDEFINED; 7789 desc->setter = JS_UNDEFINED; 7790 desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); 7791 } 7792 return TRUE; 7793 } 7794 } 7795 } else { 7796 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 7797 if (em && em->get_own_property) { 7798 return em->get_own_property(ctx, desc, 7799 JS_MKPTR(JS_TAG_OBJECT, p), prop); 7800 } 7801 } 7802 } 7803 return FALSE; 7804 } 7805 7806 int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, 7807 JSValueConst obj, JSAtom prop) 7808 { 7809 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 7810 JS_ThrowTypeErrorNotAnObject(ctx); 7811 return -1; 7812 } 7813 return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); 7814 } 7815 7816 /* return -1 if exception (Proxy object only) or TRUE/FALSE */ 7817 int JS_IsExtensible(JSContext *ctx, JSValueConst obj) 7818 { 7819 JSObject *p; 7820 7821 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 7822 return FALSE; 7823 p = JS_VALUE_GET_OBJ(obj); 7824 if (unlikely(p->class_id == JS_CLASS_PROXY)) 7825 return js_proxy_isExtensible(ctx, obj); 7826 else 7827 return p->extensible; 7828 } 7829 7830 /* return -1 if exception (Proxy object only) or TRUE/FALSE */ 7831 int JS_PreventExtensions(JSContext *ctx, JSValueConst obj) 7832 { 7833 JSObject *p; 7834 7835 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 7836 return FALSE; 7837 p = JS_VALUE_GET_OBJ(obj); 7838 if (unlikely(p->class_id == JS_CLASS_PROXY)) 7839 return js_proxy_preventExtensions(ctx, obj); 7840 p->extensible = FALSE; 7841 return TRUE; 7842 } 7843 7844 /* return -1 if exception otherwise TRUE or FALSE */ 7845 int JS_HasPropertyStr(JSContext *ctx, JSValueConst obj, const char *propname) 7846 { 7847 JSAtom atom; 7848 int ret; 7849 atom = JS_NewAtom(ctx, propname); 7850 ret = JS_HasProperty(ctx, obj, atom); 7851 JS_FreeAtom(ctx, atom); 7852 return ret; 7853 } 7854 7855 /* return -1 if exception otherwise TRUE or FALSE */ 7856 int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) 7857 { 7858 JSObject *p; 7859 int ret; 7860 JSValue obj1; 7861 7862 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) 7863 return FALSE; 7864 p = JS_VALUE_GET_OBJ(obj); 7865 for(;;) { 7866 if (p->is_exotic) { 7867 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 7868 if (em && em->has_property) { 7869 /* has_property can free the prototype */ 7870 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7871 ret = em->has_property(ctx, obj1, prop); 7872 JS_FreeValue(ctx, obj1); 7873 return ret; 7874 } 7875 } 7876 /* JS_GetOwnPropertyInternal can free the prototype */ 7877 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7878 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); 7879 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 7880 if (ret != 0) 7881 return ret; 7882 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 7883 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 7884 ret = JS_AtomIsNumericIndex(ctx, prop); 7885 if (ret != 0) { 7886 if (ret < 0) 7887 return -1; 7888 return FALSE; 7889 } 7890 } 7891 p = p->shape->proto; 7892 if (!p) 7893 break; 7894 } 7895 return FALSE; 7896 } 7897 7898 /* val must be a symbol */ 7899 static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) 7900 { 7901 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 7902 return js_get_atom_index(ctx->rt, p); 7903 } 7904 7905 /* return JS_ATOM_NULL in case of exception */ 7906 JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) 7907 { 7908 JSAtom atom; 7909 uint32_t tag; 7910 tag = JS_VALUE_GET_TAG(val); 7911 if (tag == JS_TAG_INT && 7912 (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { 7913 /* fast path for integer values */ 7914 atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); 7915 } else if (tag == JS_TAG_SYMBOL) { 7916 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 7917 atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); 7918 } else { 7919 JSValue str; 7920 str = JS_ToPropertyKey(ctx, val); 7921 if (JS_IsException(str)) 7922 return JS_ATOM_NULL; 7923 if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { 7924 atom = js_symbol_to_atom(ctx, str); 7925 } else { 7926 atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); 7927 } 7928 } 7929 return atom; 7930 } 7931 7932 static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, 7933 JSValue prop) 7934 { 7935 JSAtom atom; 7936 JSValue ret; 7937 7938 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && 7939 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { 7940 JSObject *p; 7941 uint32_t idx; 7942 /* fast path for array access */ 7943 p = JS_VALUE_GET_OBJ(this_obj); 7944 idx = JS_VALUE_GET_INT(prop); 7945 switch(p->class_id) { 7946 case JS_CLASS_ARRAY: 7947 case JS_CLASS_ARGUMENTS: 7948 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7949 return JS_DupValue(ctx, p->u.array.u.values[idx]); 7950 case JS_CLASS_INT8_ARRAY: 7951 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7952 return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); 7953 case JS_CLASS_UINT8C_ARRAY: 7954 case JS_CLASS_UINT8_ARRAY: 7955 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7956 return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); 7957 case JS_CLASS_INT16_ARRAY: 7958 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7959 return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); 7960 case JS_CLASS_UINT16_ARRAY: 7961 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7962 return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); 7963 case JS_CLASS_INT32_ARRAY: 7964 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7965 return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); 7966 case JS_CLASS_UINT32_ARRAY: 7967 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7968 return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); 7969 case JS_CLASS_BIG_INT64_ARRAY: 7970 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7971 return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); 7972 case JS_CLASS_BIG_UINT64_ARRAY: 7973 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7974 return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); 7975 case JS_CLASS_FLOAT32_ARRAY: 7976 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7977 return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); 7978 case JS_CLASS_FLOAT64_ARRAY: 7979 if (unlikely(idx >= p->u.array.count)) goto slow_path; 7980 return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); 7981 default: 7982 goto slow_path; 7983 } 7984 } else { 7985 slow_path: 7986 atom = JS_ValueToAtom(ctx, prop); 7987 JS_FreeValue(ctx, prop); 7988 if (unlikely(atom == JS_ATOM_NULL)) 7989 return JS_EXCEPTION; 7990 ret = JS_GetProperty(ctx, this_obj, atom); 7991 JS_FreeAtom(ctx, atom); 7992 return ret; 7993 } 7994 } 7995 7996 JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 7997 uint32_t idx) 7998 { 7999 return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx)); 8000 } 8001 8002 /* Check if an object has a generalized numeric property. Return value: 8003 -1 for exception, 8004 TRUE if property exists, stored into *pval, 8005 FALSE if proprty does not exist. 8006 */ 8007 static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval) 8008 { 8009 JSValue val = JS_UNDEFINED; 8010 JSAtom prop; 8011 int present; 8012 8013 if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { 8014 /* fast path */ 8015 present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); 8016 if (present > 0) { 8017 val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); 8018 if (unlikely(JS_IsException(val))) 8019 present = -1; 8020 } 8021 } else { 8022 prop = JS_NewAtomInt64(ctx, idx); 8023 present = -1; 8024 if (likely(prop != JS_ATOM_NULL)) { 8025 present = JS_HasProperty(ctx, obj, prop); 8026 if (present > 0) { 8027 val = JS_GetProperty(ctx, obj, prop); 8028 if (unlikely(JS_IsException(val))) 8029 present = -1; 8030 } 8031 JS_FreeAtom(ctx, prop); 8032 } 8033 } 8034 *pval = val; 8035 return present; 8036 } 8037 8038 static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx) 8039 { 8040 JSAtom prop; 8041 JSValue val; 8042 8043 if ((uint64_t)idx <= INT32_MAX) { 8044 /* fast path for fast arrays */ 8045 return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); 8046 } 8047 prop = JS_NewAtomInt64(ctx, idx); 8048 if (prop == JS_ATOM_NULL) 8049 return JS_EXCEPTION; 8050 8051 val = JS_GetProperty(ctx, obj, prop); 8052 JS_FreeAtom(ctx, prop); 8053 return val; 8054 } 8055 8056 JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, 8057 const char *prop) 8058 { 8059 JSAtom atom; 8060 JSValue ret; 8061 atom = JS_NewAtom(ctx, prop); 8062 ret = JS_GetProperty(ctx, this_obj, atom); 8063 JS_FreeAtom(ctx, atom); 8064 return ret; 8065 } 8066 8067 /* Note: the property value is not initialized. Return NULL if memory 8068 error. */ 8069 static JSProperty *add_property(JSContext *ctx, 8070 JSObject *p, JSAtom prop, int prop_flags) 8071 { 8072 JSShape *sh, *new_sh; 8073 8074 sh = p->shape; 8075 if (sh->is_hashed) { 8076 /* try to find an existing shape */ 8077 new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); 8078 if (new_sh) { 8079 /* matching shape found: use it */ 8080 /* the property array may need to be resized */ 8081 if (new_sh->prop_size != sh->prop_size) { 8082 JSProperty *new_prop; 8083 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * 8084 new_sh->prop_size); 8085 if (!new_prop) 8086 return NULL; 8087 p->prop = new_prop; 8088 } 8089 p->shape = js_dup_shape(new_sh); 8090 js_free_shape(ctx->rt, sh); 8091 return &p->prop[new_sh->prop_count - 1]; 8092 } else if (sh->header.ref_count != 1) { 8093 /* if the shape is shared, clone it */ 8094 new_sh = js_clone_shape(ctx, sh); 8095 if (!new_sh) 8096 return NULL; 8097 /* hash the cloned shape */ 8098 new_sh->is_hashed = TRUE; 8099 js_shape_hash_link(ctx->rt, new_sh); 8100 js_free_shape(ctx->rt, p->shape); 8101 p->shape = new_sh; 8102 } 8103 } 8104 assert(p->shape->header.ref_count == 1); 8105 if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) 8106 return NULL; 8107 return &p->prop[p->shape->prop_count - 1]; 8108 } 8109 8110 /* can be called on Array or Arguments objects. return < 0 if 8111 memory alloc error. */ 8112 static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, 8113 JSObject *p) 8114 { 8115 JSProperty *pr; 8116 JSShape *sh; 8117 JSValue *tab; 8118 uint32_t i, len, new_count; 8119 8120 if (js_shape_prepare_update(ctx, p, NULL)) 8121 return -1; 8122 len = p->u.array.count; 8123 /* resize the properties once to simplify the error handling */ 8124 sh = p->shape; 8125 new_count = sh->prop_count + len; 8126 if (new_count > sh->prop_size) { 8127 if (resize_properties(ctx, &p->shape, p, new_count)) 8128 return -1; 8129 } 8130 8131 tab = p->u.array.u.values; 8132 for(i = 0; i < len; i++) { 8133 /* add_property cannot fail here but 8134 __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ 8135 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); 8136 pr->u.value = *tab++; 8137 } 8138 js_free(ctx, p->u.array.u.values); 8139 p->u.array.count = 0; 8140 p->u.array.u.values = NULL; /* fail safe */ 8141 p->u.array.u1.size = 0; 8142 p->fast_array = 0; 8143 return 0; 8144 } 8145 8146 static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) 8147 { 8148 JSShape *sh; 8149 JSShapeProperty *pr, *lpr, *prop; 8150 JSProperty *pr1; 8151 uint32_t lpr_idx; 8152 intptr_t h, h1; 8153 8154 redo: 8155 sh = p->shape; 8156 h1 = atom & sh->prop_hash_mask; 8157 h = prop_hash_end(sh)[-h1 - 1]; 8158 prop = get_shape_prop(sh); 8159 lpr = NULL; 8160 lpr_idx = 0; /* prevent warning */ 8161 while (h != 0) { 8162 pr = &prop[h - 1]; 8163 if (likely(pr->atom == atom)) { 8164 /* found ! */ 8165 if (!(pr->flags & JS_PROP_CONFIGURABLE)) 8166 return FALSE; 8167 /* realloc the shape if needed */ 8168 if (lpr) 8169 lpr_idx = lpr - get_shape_prop(sh); 8170 if (js_shape_prepare_update(ctx, p, &pr)) 8171 return -1; 8172 sh = p->shape; 8173 /* remove property */ 8174 if (lpr) { 8175 lpr = get_shape_prop(sh) + lpr_idx; 8176 lpr->hash_next = pr->hash_next; 8177 } else { 8178 prop_hash_end(sh)[-h1 - 1] = pr->hash_next; 8179 } 8180 sh->deleted_prop_count++; 8181 /* free the entry */ 8182 pr1 = &p->prop[h - 1]; 8183 free_property(ctx->rt, pr1, pr->flags); 8184 JS_FreeAtom(ctx, pr->atom); 8185 /* put default values */ 8186 pr->flags = 0; 8187 pr->atom = JS_ATOM_NULL; 8188 pr1->u.value = JS_UNDEFINED; 8189 8190 /* compact the properties if too many deleted properties */ 8191 if (sh->deleted_prop_count >= 8 && 8192 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { 8193 compact_properties(ctx, p); 8194 } 8195 return TRUE; 8196 } 8197 lpr = pr; 8198 h = pr->hash_next; 8199 } 8200 8201 if (p->is_exotic) { 8202 if (p->fast_array) { 8203 uint32_t idx; 8204 if (JS_AtomIsArrayIndex(ctx, &idx, atom) && 8205 idx < p->u.array.count) { 8206 if (p->class_id == JS_CLASS_ARRAY || 8207 p->class_id == JS_CLASS_ARGUMENTS) { 8208 /* Special case deleting the last element of a fast Array */ 8209 if (idx == p->u.array.count - 1) { 8210 JS_FreeValue(ctx, p->u.array.u.values[idx]); 8211 p->u.array.count = idx; 8212 return TRUE; 8213 } 8214 if (convert_fast_array_to_array(ctx, p)) 8215 return -1; 8216 goto redo; 8217 } else { 8218 return FALSE; 8219 } 8220 } 8221 } else { 8222 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8223 if (em && em->delete_property) { 8224 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); 8225 } 8226 } 8227 } 8228 /* not found */ 8229 return TRUE; 8230 } 8231 8232 static int call_setter(JSContext *ctx, JSObject *setter, 8233 JSValueConst this_obj, JSValue val, int flags) 8234 { 8235 JSValue ret, func; 8236 if (likely(setter)) { 8237 func = JS_MKPTR(JS_TAG_OBJECT, setter); 8238 /* Note: the field could be removed in the setter */ 8239 func = JS_DupValue(ctx, func); 8240 ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); 8241 JS_FreeValue(ctx, val); 8242 if (JS_IsException(ret)) 8243 return -1; 8244 JS_FreeValue(ctx, ret); 8245 return TRUE; 8246 } else { 8247 JS_FreeValue(ctx, val); 8248 if ((flags & JS_PROP_THROW) || 8249 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 8250 JS_ThrowTypeError(ctx, "no setter for property"); 8251 return -1; 8252 } 8253 return FALSE; 8254 } 8255 } 8256 8257 /* set the array length and remove the array elements if necessary. */ 8258 static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, 8259 int flags) 8260 { 8261 uint32_t len, idx, cur_len; 8262 int i, ret; 8263 8264 /* Note: this call can reallocate the properties of 'p' */ 8265 ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); 8266 if (ret) 8267 return -1; 8268 /* JS_ToArrayLengthFree() must be done before the read-only test */ 8269 if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) 8270 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 8271 8272 if (likely(p->fast_array)) { 8273 uint32_t old_len = p->u.array.count; 8274 if (len < old_len) { 8275 for(i = len; i < old_len; i++) { 8276 JS_FreeValue(ctx, p->u.array.u.values[i]); 8277 } 8278 p->u.array.count = len; 8279 } 8280 p->prop[0].u.value = JS_NewUint32(ctx, len); 8281 } else { 8282 /* Note: length is always a uint32 because the object is an 8283 array */ 8284 JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); 8285 if (len < cur_len) { 8286 uint32_t d; 8287 JSShape *sh; 8288 JSShapeProperty *pr; 8289 8290 d = cur_len - len; 8291 sh = p->shape; 8292 if (d <= sh->prop_count) { 8293 JSAtom atom; 8294 8295 /* faster to iterate */ 8296 while (cur_len > len) { 8297 atom = JS_NewAtomUInt32(ctx, cur_len - 1); 8298 ret = delete_property(ctx, p, atom); 8299 JS_FreeAtom(ctx, atom); 8300 if (unlikely(!ret)) { 8301 /* unlikely case: property is not 8302 configurable */ 8303 break; 8304 } 8305 cur_len--; 8306 } 8307 } else { 8308 /* faster to iterate thru all the properties. Need two 8309 passes in case one of the property is not 8310 configurable */ 8311 cur_len = len; 8312 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; 8313 i++, pr++) { 8314 if (pr->atom != JS_ATOM_NULL && 8315 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { 8316 if (idx >= cur_len && 8317 !(pr->flags & JS_PROP_CONFIGURABLE)) { 8318 cur_len = idx + 1; 8319 } 8320 } 8321 } 8322 8323 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; 8324 i++, pr++) { 8325 if (pr->atom != JS_ATOM_NULL && 8326 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { 8327 if (idx >= cur_len) { 8328 /* remove the property */ 8329 delete_property(ctx, p, pr->atom); 8330 /* WARNING: the shape may have been modified */ 8331 sh = p->shape; 8332 pr = get_shape_prop(sh) + i; 8333 } 8334 } 8335 } 8336 } 8337 } else { 8338 cur_len = len; 8339 } 8340 set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len)); 8341 if (unlikely(cur_len > len)) { 8342 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); 8343 } 8344 } 8345 return TRUE; 8346 } 8347 8348 /* return -1 if exception */ 8349 static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) 8350 { 8351 uint32_t new_size; 8352 size_t slack; 8353 JSValue *new_array_prop; 8354 /* XXX: potential arithmetic overflow */ 8355 new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); 8356 new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); 8357 if (!new_array_prop) 8358 return -1; 8359 new_size += slack / sizeof(*new_array_prop); 8360 p->u.array.u.values = new_array_prop; 8361 p->u.array.u1.size = new_size; 8362 return 0; 8363 } 8364 8365 /* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = 8366 TRUE and p->extensible = TRUE */ 8367 static int add_fast_array_element(JSContext *ctx, JSObject *p, 8368 JSValue val, int flags) 8369 { 8370 uint32_t new_len, array_len; 8371 /* extend the array by one */ 8372 /* XXX: convert to slow array if new_len > 2^31-1 elements */ 8373 new_len = p->u.array.count + 1; 8374 /* update the length if necessary. We assume that if the length is 8375 not an integer, then if it >= 2^31. */ 8376 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { 8377 array_len = JS_VALUE_GET_INT(p->prop[0].u.value); 8378 if (new_len > array_len) { 8379 if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { 8380 JS_FreeValue(ctx, val); 8381 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 8382 } 8383 p->prop[0].u.value = JS_NewInt32(ctx, new_len); 8384 } 8385 } 8386 if (unlikely(new_len > p->u.array.u1.size)) { 8387 if (expand_fast_array(ctx, p, new_len)) { 8388 JS_FreeValue(ctx, val); 8389 return -1; 8390 } 8391 } 8392 p->u.array.u.values[new_len - 1] = val; 8393 p->u.array.count = new_len; 8394 return TRUE; 8395 } 8396 8397 /* Allocate a new fast array. Its 'length' property is set to zero. It 8398 maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit 8399 integer. WARNING: the content of the array is not initialized. */ 8400 static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) 8401 { 8402 JSValue arr; 8403 JSObject *p; 8404 8405 if (len > INT32_MAX) 8406 return JS_ThrowRangeError(ctx, "invalid array length"); 8407 arr = JS_NewArray(ctx); 8408 if (JS_IsException(arr)) 8409 return arr; 8410 if (len > 0) { 8411 p = JS_VALUE_GET_OBJ(arr); 8412 if (expand_fast_array(ctx, p, len) < 0) { 8413 JS_FreeValue(ctx, arr); 8414 return JS_EXCEPTION; 8415 } 8416 p->u.array.count = len; 8417 } 8418 return arr; 8419 } 8420 8421 static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) 8422 { 8423 JS_FreeValue(ctx, desc->getter); 8424 JS_FreeValue(ctx, desc->setter); 8425 JS_FreeValue(ctx, desc->value); 8426 } 8427 8428 /* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is 8429 freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, 8430 JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, 8431 the new property is not added and an error is raised. 'this_obj' is 8432 the receiver. If obj != this_obj, then obj must be an object 8433 (Reflect.set case). */ 8434 int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, 8435 JSAtom prop, JSValue val, JSValueConst this_obj, int flags) 8436 { 8437 JSObject *p, *p1; 8438 JSShapeProperty *prs; 8439 JSProperty *pr; 8440 uint32_t tag; 8441 JSPropertyDescriptor desc; 8442 int ret; 8443 #if 0 8444 printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n"); 8445 #endif 8446 tag = JS_VALUE_GET_TAG(this_obj); 8447 if (unlikely(tag != JS_TAG_OBJECT)) { 8448 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 8449 p = NULL; 8450 p1 = JS_VALUE_GET_OBJ(obj); 8451 goto prototype_lookup; 8452 } else { 8453 switch(tag) { 8454 case JS_TAG_NULL: 8455 JS_FreeValue(ctx, val); 8456 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); 8457 return -1; 8458 case JS_TAG_UNDEFINED: 8459 JS_FreeValue(ctx, val); 8460 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); 8461 return -1; 8462 default: 8463 /* even on a primitive type we can have setters on the prototype */ 8464 p = NULL; 8465 p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); 8466 goto prototype_lookup; 8467 } 8468 } 8469 } else { 8470 p = JS_VALUE_GET_OBJ(this_obj); 8471 p1 = JS_VALUE_GET_OBJ(obj); 8472 if (unlikely(p != p1)) 8473 goto retry2; 8474 } 8475 8476 /* fast path if obj == this_obj */ 8477 retry: 8478 prs = find_own_property(&pr, p1, prop); 8479 if (prs) { 8480 if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | 8481 JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { 8482 /* fast case */ 8483 set_value(ctx, &pr->u.value, val); 8484 return TRUE; 8485 } else if (prs->flags & JS_PROP_LENGTH) { 8486 assert(p->class_id == JS_CLASS_ARRAY); 8487 assert(prop == JS_ATOM_length); 8488 return set_array_length(ctx, p, val, flags); 8489 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 8490 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); 8491 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 8492 /* JS_PROP_WRITABLE is always true for variable 8493 references, but they are write protected in module name 8494 spaces. */ 8495 if (p->class_id == JS_CLASS_MODULE_NS) 8496 goto read_only_prop; 8497 set_value(ctx, pr->u.var_ref->pvalue, val); 8498 return TRUE; 8499 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 8500 /* Instantiate property and retry (potentially useless) */ 8501 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) { 8502 JS_FreeValue(ctx, val); 8503 return -1; 8504 } 8505 goto retry; 8506 } else { 8507 goto read_only_prop; 8508 } 8509 } 8510 8511 for(;;) { 8512 if (p1->is_exotic) { 8513 if (p1->fast_array) { 8514 if (__JS_AtomIsTaggedInt(prop)) { 8515 uint32_t idx = __JS_AtomToUInt32(prop); 8516 if (idx < p1->u.array.count) { 8517 if (unlikely(p == p1)) 8518 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); 8519 else 8520 break; 8521 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && 8522 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8523 goto typed_array_oob; 8524 } 8525 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && 8526 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8527 ret = JS_AtomIsNumericIndex(ctx, prop); 8528 if (ret != 0) { 8529 if (ret < 0) { 8530 JS_FreeValue(ctx, val); 8531 return -1; 8532 } 8533 typed_array_oob: 8534 /* must convert the argument even if out of bound access */ 8535 if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || 8536 p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { 8537 int64_t v; 8538 if (JS_ToBigInt64Free(ctx, &v, val)) 8539 return -1; 8540 } else { 8541 val = JS_ToNumberFree(ctx, val); 8542 JS_FreeValue(ctx, val); 8543 if (JS_IsException(val)) 8544 return -1; 8545 } 8546 return TRUE; 8547 } 8548 } 8549 } else { 8550 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; 8551 if (em) { 8552 JSValue obj1; 8553 if (em->set_property) { 8554 /* set_property can free the prototype */ 8555 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 8556 ret = em->set_property(ctx, obj1, prop, 8557 val, this_obj, flags); 8558 JS_FreeValue(ctx, obj1); 8559 JS_FreeValue(ctx, val); 8560 return ret; 8561 } 8562 if (em->get_own_property) { 8563 /* get_own_property can free the prototype */ 8564 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 8565 ret = em->get_own_property(ctx, &desc, 8566 obj1, prop); 8567 JS_FreeValue(ctx, obj1); 8568 if (ret < 0) { 8569 JS_FreeValue(ctx, val); 8570 return ret; 8571 } 8572 if (ret) { 8573 if (desc.flags & JS_PROP_GETSET) { 8574 JSObject *setter; 8575 if (JS_IsUndefined(desc.setter)) 8576 setter = NULL; 8577 else 8578 setter = JS_VALUE_GET_OBJ(desc.setter); 8579 ret = call_setter(ctx, setter, this_obj, val, flags); 8580 JS_FreeValue(ctx, desc.getter); 8581 JS_FreeValue(ctx, desc.setter); 8582 return ret; 8583 } else { 8584 JS_FreeValue(ctx, desc.value); 8585 if (!(desc.flags & JS_PROP_WRITABLE)) 8586 goto read_only_prop; 8587 if (likely(p == p1)) { 8588 ret = JS_DefineProperty(ctx, this_obj, prop, val, 8589 JS_UNDEFINED, JS_UNDEFINED, 8590 JS_PROP_HAS_VALUE); 8591 JS_FreeValue(ctx, val); 8592 return ret; 8593 } else { 8594 break; 8595 } 8596 } 8597 } 8598 } 8599 } 8600 } 8601 } 8602 p1 = p1->shape->proto; 8603 prototype_lookup: 8604 if (!p1) 8605 break; 8606 8607 retry2: 8608 prs = find_own_property(&pr, p1, prop); 8609 if (prs) { 8610 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 8611 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); 8612 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 8613 /* Instantiate property and retry (potentially useless) */ 8614 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) 8615 return -1; 8616 goto retry2; 8617 } else if (!(prs->flags & JS_PROP_WRITABLE)) { 8618 goto read_only_prop; 8619 } 8620 } 8621 } 8622 8623 if (unlikely(flags & JS_PROP_NO_ADD)) { 8624 JS_FreeValue(ctx, val); 8625 JS_ThrowReferenceErrorNotDefined(ctx, prop); 8626 return -1; 8627 } 8628 8629 if (unlikely(!p)) { 8630 JS_FreeValue(ctx, val); 8631 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); 8632 } 8633 8634 if (unlikely(!p->extensible)) { 8635 JS_FreeValue(ctx, val); 8636 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); 8637 } 8638 8639 if (likely(p == JS_VALUE_GET_OBJ(obj))) { 8640 if (p->is_exotic) { 8641 if (p->class_id == JS_CLASS_ARRAY && p->fast_array && 8642 __JS_AtomIsTaggedInt(prop)) { 8643 uint32_t idx = __JS_AtomToUInt32(prop); 8644 if (idx == p->u.array.count) { 8645 /* fast case */ 8646 return add_fast_array_element(ctx, p, val, flags); 8647 } else { 8648 goto generic_create_prop; 8649 } 8650 } else { 8651 goto generic_create_prop; 8652 } 8653 } else { 8654 pr = add_property(ctx, p, prop, JS_PROP_C_W_E); 8655 if (unlikely(!pr)) { 8656 JS_FreeValue(ctx, val); 8657 return -1; 8658 } 8659 pr->u.value = val; 8660 return TRUE; 8661 } 8662 } else { 8663 /* generic case: modify the property in this_obj if it already exists */ 8664 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 8665 if (ret < 0) { 8666 JS_FreeValue(ctx, val); 8667 return ret; 8668 } 8669 if (ret) { 8670 if (desc.flags & JS_PROP_GETSET) { 8671 JS_FreeValue(ctx, desc.getter); 8672 JS_FreeValue(ctx, desc.setter); 8673 JS_FreeValue(ctx, val); 8674 return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); 8675 } else { 8676 JS_FreeValue(ctx, desc.value); 8677 if (!(desc.flags & JS_PROP_WRITABLE) || 8678 p->class_id == JS_CLASS_MODULE_NS) { 8679 read_only_prop: 8680 JS_FreeValue(ctx, val); 8681 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); 8682 } 8683 } 8684 ret = JS_DefineProperty(ctx, this_obj, prop, val, 8685 JS_UNDEFINED, JS_UNDEFINED, 8686 JS_PROP_HAS_VALUE); 8687 JS_FreeValue(ctx, val); 8688 return ret; 8689 } else { 8690 generic_create_prop: 8691 ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, 8692 flags | 8693 JS_PROP_HAS_VALUE | 8694 JS_PROP_HAS_ENUMERABLE | 8695 JS_PROP_HAS_WRITABLE | 8696 JS_PROP_HAS_CONFIGURABLE | 8697 JS_PROP_C_W_E); 8698 JS_FreeValue(ctx, val); 8699 return ret; 8700 } 8701 } 8702 } 8703 8704 /* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ 8705 static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 8706 JSValue prop, JSValue val, int flags) 8707 { 8708 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && 8709 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { 8710 JSObject *p; 8711 uint32_t idx; 8712 double d; 8713 int32_t v; 8714 8715 /* fast path for array access */ 8716 p = JS_VALUE_GET_OBJ(this_obj); 8717 idx = JS_VALUE_GET_INT(prop); 8718 switch(p->class_id) { 8719 case JS_CLASS_ARRAY: 8720 if (unlikely(idx >= (uint32_t)p->u.array.count)) { 8721 JSObject *p1; 8722 JSShape *sh1; 8723 8724 /* fast path to add an element to the array */ 8725 if (idx != (uint32_t)p->u.array.count || 8726 !p->fast_array || !p->extensible) 8727 goto slow_path; 8728 /* check if prototype chain has a numeric property */ 8729 p1 = p->shape->proto; 8730 while (p1 != NULL) { 8731 sh1 = p1->shape; 8732 if (p1->class_id == JS_CLASS_ARRAY) { 8733 if (unlikely(!p1->fast_array)) 8734 goto slow_path; 8735 } else if (p1->class_id == JS_CLASS_OBJECT) { 8736 if (unlikely(sh1->has_small_array_index)) 8737 goto slow_path; 8738 } else { 8739 goto slow_path; 8740 } 8741 p1 = sh1->proto; 8742 } 8743 /* add element */ 8744 return add_fast_array_element(ctx, p, val, flags); 8745 } 8746 set_value(ctx, &p->u.array.u.values[idx], val); 8747 break; 8748 case JS_CLASS_ARGUMENTS: 8749 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8750 goto slow_path; 8751 set_value(ctx, &p->u.array.u.values[idx], val); 8752 break; 8753 case JS_CLASS_UINT8C_ARRAY: 8754 if (JS_ToUint8ClampFree(ctx, &v, val)) 8755 return -1; 8756 /* Note: the conversion can detach the typed array, so the 8757 array bound check must be done after */ 8758 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8759 goto ta_out_of_bound; 8760 p->u.array.u.uint8_ptr[idx] = v; 8761 break; 8762 case JS_CLASS_INT8_ARRAY: 8763 case JS_CLASS_UINT8_ARRAY: 8764 if (JS_ToInt32Free(ctx, &v, val)) 8765 return -1; 8766 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8767 goto ta_out_of_bound; 8768 p->u.array.u.uint8_ptr[idx] = v; 8769 break; 8770 case JS_CLASS_INT16_ARRAY: 8771 case JS_CLASS_UINT16_ARRAY: 8772 if (JS_ToInt32Free(ctx, &v, val)) 8773 return -1; 8774 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8775 goto ta_out_of_bound; 8776 p->u.array.u.uint16_ptr[idx] = v; 8777 break; 8778 case JS_CLASS_INT32_ARRAY: 8779 case JS_CLASS_UINT32_ARRAY: 8780 if (JS_ToInt32Free(ctx, &v, val)) 8781 return -1; 8782 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8783 goto ta_out_of_bound; 8784 p->u.array.u.uint32_ptr[idx] = v; 8785 break; 8786 case JS_CLASS_BIG_INT64_ARRAY: 8787 case JS_CLASS_BIG_UINT64_ARRAY: 8788 /* XXX: need specific conversion function */ 8789 { 8790 int64_t v; 8791 if (JS_ToBigInt64Free(ctx, &v, val)) 8792 return -1; 8793 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8794 goto ta_out_of_bound; 8795 p->u.array.u.uint64_ptr[idx] = v; 8796 } 8797 break; 8798 case JS_CLASS_FLOAT32_ARRAY: 8799 if (JS_ToFloat64Free(ctx, &d, val)) 8800 return -1; 8801 if (unlikely(idx >= (uint32_t)p->u.array.count)) 8802 goto ta_out_of_bound; 8803 p->u.array.u.float_ptr[idx] = d; 8804 break; 8805 case JS_CLASS_FLOAT64_ARRAY: 8806 if (JS_ToFloat64Free(ctx, &d, val)) 8807 return -1; 8808 if (unlikely(idx >= (uint32_t)p->u.array.count)) { 8809 ta_out_of_bound: 8810 return TRUE; 8811 } 8812 p->u.array.u.double_ptr[idx] = d; 8813 break; 8814 default: 8815 goto slow_path; 8816 } 8817 return TRUE; 8818 } else { 8819 JSAtom atom; 8820 int ret; 8821 slow_path: 8822 atom = JS_ValueToAtom(ctx, prop); 8823 JS_FreeValue(ctx, prop); 8824 if (unlikely(atom == JS_ATOM_NULL)) { 8825 JS_FreeValue(ctx, val); 8826 return -1; 8827 } 8828 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags); 8829 JS_FreeAtom(ctx, atom); 8830 return ret; 8831 } 8832 } 8833 8834 int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, 8835 uint32_t idx, JSValue val) 8836 { 8837 return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val, 8838 JS_PROP_THROW); 8839 } 8840 8841 int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj, 8842 int64_t idx, JSValue val) 8843 { 8844 JSAtom prop; 8845 int res; 8846 8847 if ((uint64_t)idx <= INT32_MAX) { 8848 /* fast path for fast arrays */ 8849 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, 8850 JS_PROP_THROW); 8851 } 8852 prop = JS_NewAtomInt64(ctx, idx); 8853 if (prop == JS_ATOM_NULL) { 8854 JS_FreeValue(ctx, val); 8855 return -1; 8856 } 8857 res = JS_SetProperty(ctx, this_obj, prop, val); 8858 JS_FreeAtom(ctx, prop); 8859 return res; 8860 } 8861 8862 int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, 8863 const char *prop, JSValue val) 8864 { 8865 JSAtom atom; 8866 int ret; 8867 atom = JS_NewAtom(ctx, prop); 8868 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW); 8869 JS_FreeAtom(ctx, atom); 8870 return ret; 8871 } 8872 8873 /* compute the property flags. For each flag: (JS_PROP_HAS_x forces 8874 it, otherwise def_flags is used) 8875 Note: makes assumption about the bit pattern of the flags 8876 */ 8877 static int get_prop_flags(int flags, int def_flags) 8878 { 8879 int mask; 8880 mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; 8881 return (flags & mask) | (def_flags & ~mask); 8882 } 8883 8884 static int JS_CreateProperty(JSContext *ctx, JSObject *p, 8885 JSAtom prop, JSValueConst val, 8886 JSValueConst getter, JSValueConst setter, 8887 int flags) 8888 { 8889 JSProperty *pr; 8890 int ret, prop_flags; 8891 8892 /* add a new property or modify an existing exotic one */ 8893 if (p->is_exotic) { 8894 if (p->class_id == JS_CLASS_ARRAY) { 8895 uint32_t idx, len; 8896 8897 if (p->fast_array) { 8898 if (__JS_AtomIsTaggedInt(prop)) { 8899 idx = __JS_AtomToUInt32(prop); 8900 if (idx == p->u.array.count) { 8901 if (!p->extensible) 8902 goto not_extensible; 8903 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) 8904 goto convert_to_array; 8905 prop_flags = get_prop_flags(flags, 0); 8906 if (prop_flags != JS_PROP_C_W_E) 8907 goto convert_to_array; 8908 return add_fast_array_element(ctx, p, 8909 JS_DupValue(ctx, val), flags); 8910 } else { 8911 goto convert_to_array; 8912 } 8913 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { 8914 /* convert the fast array to normal array */ 8915 convert_to_array: 8916 if (convert_fast_array_to_array(ctx, p)) 8917 return -1; 8918 goto generic_array; 8919 } 8920 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { 8921 JSProperty *plen; 8922 JSShapeProperty *pslen; 8923 generic_array: 8924 /* update the length field */ 8925 plen = &p->prop[0]; 8926 JS_ToUint32(ctx, &len, plen->u.value); 8927 if ((idx + 1) > len) { 8928 pslen = get_shape_prop(p->shape); 8929 if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) 8930 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); 8931 /* XXX: should update the length after defining 8932 the property */ 8933 len = idx + 1; 8934 set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len)); 8935 } 8936 } 8937 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 8938 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 8939 ret = JS_AtomIsNumericIndex(ctx, prop); 8940 if (ret != 0) { 8941 if (ret < 0) 8942 return -1; 8943 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); 8944 } 8945 } else if (!(flags & JS_PROP_NO_EXOTIC)) { 8946 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 8947 if (em) { 8948 if (em->define_own_property) { 8949 return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), 8950 prop, val, getter, setter, flags); 8951 } 8952 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 8953 if (ret < 0) 8954 return -1; 8955 if (!ret) 8956 goto not_extensible; 8957 } 8958 } 8959 } 8960 8961 if (!p->extensible) { 8962 not_extensible: 8963 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); 8964 } 8965 8966 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 8967 prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | 8968 JS_PROP_GETSET; 8969 } else { 8970 prop_flags = flags & JS_PROP_C_W_E; 8971 } 8972 pr = add_property(ctx, p, prop, prop_flags); 8973 if (unlikely(!pr)) 8974 return -1; 8975 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 8976 pr->u.getset.getter = NULL; 8977 if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { 8978 pr->u.getset.getter = 8979 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter)); 8980 } 8981 pr->u.getset.setter = NULL; 8982 if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { 8983 pr->u.getset.setter = 8984 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter)); 8985 } 8986 } else { 8987 if (flags & JS_PROP_HAS_VALUE) { 8988 pr->u.value = JS_DupValue(ctx, val); 8989 } else { 8990 pr->u.value = JS_UNDEFINED; 8991 } 8992 } 8993 return TRUE; 8994 } 8995 8996 /* return FALSE if not OK */ 8997 static BOOL check_define_prop_flags(int prop_flags, int flags) 8998 { 8999 BOOL has_accessor, is_getset; 9000 9001 if (!(prop_flags & JS_PROP_CONFIGURABLE)) { 9002 if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == 9003 (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { 9004 return FALSE; 9005 } 9006 if ((flags & JS_PROP_HAS_ENUMERABLE) && 9007 (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) 9008 return FALSE; 9009 } 9010 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | 9011 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 9012 if (!(prop_flags & JS_PROP_CONFIGURABLE)) { 9013 has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); 9014 is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); 9015 if (has_accessor != is_getset) 9016 return FALSE; 9017 if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { 9018 /* not writable: cannot set the writable bit */ 9019 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == 9020 (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) 9021 return FALSE; 9022 } 9023 } 9024 } 9025 return TRUE; 9026 } 9027 9028 /* ensure that the shape can be safely modified */ 9029 static int js_shape_prepare_update(JSContext *ctx, JSObject *p, 9030 JSShapeProperty **pprs) 9031 { 9032 JSShape *sh; 9033 uint32_t idx = 0; /* prevent warning */ 9034 9035 sh = p->shape; 9036 if (sh->is_hashed) { 9037 if (sh->header.ref_count != 1) { 9038 if (pprs) 9039 idx = *pprs - get_shape_prop(sh); 9040 /* clone the shape (the resulting one is no longer hashed) */ 9041 sh = js_clone_shape(ctx, sh); 9042 if (!sh) 9043 return -1; 9044 js_free_shape(ctx->rt, p->shape); 9045 p->shape = sh; 9046 if (pprs) 9047 *pprs = get_shape_prop(sh) + idx; 9048 } else { 9049 js_shape_hash_unlink(ctx->rt, sh); 9050 sh->is_hashed = FALSE; 9051 } 9052 } 9053 return 0; 9054 } 9055 9056 static int js_update_property_flags(JSContext *ctx, JSObject *p, 9057 JSShapeProperty **pprs, int flags) 9058 { 9059 if (flags != (*pprs)->flags) { 9060 if (js_shape_prepare_update(ctx, p, pprs)) 9061 return -1; 9062 (*pprs)->flags = flags; 9063 } 9064 return 0; 9065 } 9066 9067 /* allowed flags: 9068 JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE 9069 JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, 9070 JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, 9071 JS_PROP_THROW, JS_PROP_NO_EXOTIC. 9072 If JS_PROP_THROW is set, return an exception instead of FALSE. 9073 if JS_PROP_NO_EXOTIC is set, do not call the exotic 9074 define_own_property callback. 9075 return -1 (exception), FALSE or TRUE. 9076 */ 9077 int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, 9078 JSAtom prop, JSValueConst val, 9079 JSValueConst getter, JSValueConst setter, int flags) 9080 { 9081 JSObject *p; 9082 JSShapeProperty *prs; 9083 JSProperty *pr; 9084 int mask, res; 9085 9086 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { 9087 JS_ThrowTypeErrorNotAnObject(ctx); 9088 return -1; 9089 } 9090 p = JS_VALUE_GET_OBJ(this_obj); 9091 9092 redo_prop_update: 9093 prs = find_own_property(&pr, p, prop); 9094 if (prs) { 9095 /* the range of the Array length property is always tested before */ 9096 if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { 9097 uint32_t array_length; 9098 if (JS_ToArrayLengthFree(ctx, &array_length, 9099 JS_DupValue(ctx, val), FALSE)) { 9100 return -1; 9101 } 9102 /* this code relies on the fact that Uint32 are never allocated */ 9103 val = (JSValueConst)JS_NewUint32(ctx, array_length); 9104 /* prs may have been modified */ 9105 prs = find_own_property(&pr, p, prop); 9106 assert(prs != NULL); 9107 } 9108 /* property already exists */ 9109 if (!check_define_prop_flags(prs->flags, flags)) { 9110 not_configurable: 9111 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); 9112 } 9113 9114 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 9115 /* Instantiate property and retry */ 9116 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) 9117 return -1; 9118 goto redo_prop_update; 9119 } 9120 9121 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | 9122 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 9123 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 9124 JSObject *new_getter, *new_setter; 9125 9126 if (JS_IsFunction(ctx, getter)) { 9127 new_getter = JS_VALUE_GET_OBJ(getter); 9128 } else { 9129 new_getter = NULL; 9130 } 9131 if (JS_IsFunction(ctx, setter)) { 9132 new_setter = JS_VALUE_GET_OBJ(setter); 9133 } else { 9134 new_setter = NULL; 9135 } 9136 9137 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { 9138 if (js_shape_prepare_update(ctx, p, &prs)) 9139 return -1; 9140 /* convert to getset */ 9141 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 9142 free_var_ref(ctx->rt, pr->u.var_ref); 9143 } else { 9144 JS_FreeValue(ctx, pr->u.value); 9145 } 9146 prs->flags = (prs->flags & 9147 (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | 9148 JS_PROP_GETSET; 9149 pr->u.getset.getter = NULL; 9150 pr->u.getset.setter = NULL; 9151 } else { 9152 if (!(prs->flags & JS_PROP_CONFIGURABLE)) { 9153 if ((flags & JS_PROP_HAS_GET) && 9154 new_getter != pr->u.getset.getter) { 9155 goto not_configurable; 9156 } 9157 if ((flags & JS_PROP_HAS_SET) && 9158 new_setter != pr->u.getset.setter) { 9159 goto not_configurable; 9160 } 9161 } 9162 } 9163 if (flags & JS_PROP_HAS_GET) { 9164 if (pr->u.getset.getter) 9165 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 9166 if (new_getter) 9167 JS_DupValue(ctx, getter); 9168 pr->u.getset.getter = new_getter; 9169 } 9170 if (flags & JS_PROP_HAS_SET) { 9171 if (pr->u.getset.setter) 9172 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 9173 if (new_setter) 9174 JS_DupValue(ctx, setter); 9175 pr->u.getset.setter = new_setter; 9176 } 9177 } else { 9178 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 9179 /* convert to data descriptor */ 9180 if (js_shape_prepare_update(ctx, p, &prs)) 9181 return -1; 9182 if (pr->u.getset.getter) 9183 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); 9184 if (pr->u.getset.setter) 9185 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); 9186 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); 9187 pr->u.value = JS_UNDEFINED; 9188 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 9189 /* Note: JS_PROP_VARREF is always writable */ 9190 } else { 9191 if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && 9192 (flags & JS_PROP_HAS_VALUE)) { 9193 if (!js_same_value(ctx, val, pr->u.value)) { 9194 goto not_configurable; 9195 } else { 9196 return TRUE; 9197 } 9198 } 9199 } 9200 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 9201 if (flags & JS_PROP_HAS_VALUE) { 9202 if (p->class_id == JS_CLASS_MODULE_NS) { 9203 /* JS_PROP_WRITABLE is always true for variable 9204 references, but they are write protected in module name 9205 spaces. */ 9206 if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) 9207 goto not_configurable; 9208 } else { 9209 /* update the reference */ 9210 set_value(ctx, pr->u.var_ref->pvalue, 9211 JS_DupValue(ctx, val)); 9212 } 9213 } 9214 /* if writable is set to false, no longer a 9215 reference (for mapped arguments) */ 9216 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { 9217 JSValue val1; 9218 if (p->class_id == JS_CLASS_MODULE_NS) { 9219 return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false"); 9220 } 9221 if (js_shape_prepare_update(ctx, p, &prs)) 9222 return -1; 9223 val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); 9224 free_var_ref(ctx->rt, pr->u.var_ref); 9225 pr->u.value = val1; 9226 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); 9227 } 9228 } else if (prs->flags & JS_PROP_LENGTH) { 9229 if (flags & JS_PROP_HAS_VALUE) { 9230 /* Note: no JS code is executable because 9231 'val' is guaranted to be a Uint32 */ 9232 res = set_array_length(ctx, p, JS_DupValue(ctx, val), 9233 flags); 9234 } else { 9235 res = TRUE; 9236 } 9237 /* still need to reset the writable flag if 9238 needed. The JS_PROP_LENGTH is kept because the 9239 Uint32 test is still done if the length 9240 property is read-only. */ 9241 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == 9242 JS_PROP_HAS_WRITABLE) { 9243 prs = get_shape_prop(p->shape); 9244 if (js_update_property_flags(ctx, p, &prs, 9245 prs->flags & ~JS_PROP_WRITABLE)) 9246 return -1; 9247 } 9248 return res; 9249 } else { 9250 if (flags & JS_PROP_HAS_VALUE) { 9251 JS_FreeValue(ctx, pr->u.value); 9252 pr->u.value = JS_DupValue(ctx, val); 9253 } 9254 if (flags & JS_PROP_HAS_WRITABLE) { 9255 if (js_update_property_flags(ctx, p, &prs, 9256 (prs->flags & ~JS_PROP_WRITABLE) | 9257 (flags & JS_PROP_WRITABLE))) 9258 return -1; 9259 } 9260 } 9261 } 9262 } 9263 mask = 0; 9264 if (flags & JS_PROP_HAS_CONFIGURABLE) 9265 mask |= JS_PROP_CONFIGURABLE; 9266 if (flags & JS_PROP_HAS_ENUMERABLE) 9267 mask |= JS_PROP_ENUMERABLE; 9268 if (js_update_property_flags(ctx, p, &prs, 9269 (prs->flags & ~mask) | (flags & mask))) 9270 return -1; 9271 return TRUE; 9272 } 9273 9274 /* handle modification of fast array elements */ 9275 if (p->fast_array) { 9276 uint32_t idx; 9277 uint32_t prop_flags; 9278 if (p->class_id == JS_CLASS_ARRAY) { 9279 if (__JS_AtomIsTaggedInt(prop)) { 9280 idx = __JS_AtomToUInt32(prop); 9281 if (idx < p->u.array.count) { 9282 prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); 9283 if (prop_flags != JS_PROP_C_W_E) 9284 goto convert_to_slow_array; 9285 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 9286 convert_to_slow_array: 9287 if (convert_fast_array_to_array(ctx, p)) 9288 return -1; 9289 else 9290 goto redo_prop_update; 9291 } 9292 if (flags & JS_PROP_HAS_VALUE) { 9293 set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val)); 9294 } 9295 return TRUE; 9296 } 9297 } 9298 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 9299 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 9300 JSValue num; 9301 int ret; 9302 9303 if (!__JS_AtomIsTaggedInt(prop)) { 9304 /* slow path with to handle all numeric indexes */ 9305 num = JS_AtomIsNumericIndex1(ctx, prop); 9306 if (JS_IsUndefined(num)) 9307 goto typed_array_done; 9308 if (JS_IsException(num)) 9309 return -1; 9310 ret = JS_NumberIsInteger(ctx, num); 9311 if (ret < 0) { 9312 JS_FreeValue(ctx, num); 9313 return -1; 9314 } 9315 if (!ret) { 9316 JS_FreeValue(ctx, num); 9317 return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); 9318 } 9319 ret = JS_NumberIsNegativeOrMinusZero(ctx, num); 9320 JS_FreeValue(ctx, num); 9321 if (ret) { 9322 return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); 9323 } 9324 if (!__JS_AtomIsTaggedInt(prop)) 9325 goto typed_array_oob; 9326 } 9327 idx = __JS_AtomToUInt32(prop); 9328 /* if the typed array is detached, p->u.array.count = 0 */ 9329 if (idx >= p->u.array.count) { 9330 typed_array_oob: 9331 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); 9332 } 9333 prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 9334 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || 9335 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { 9336 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); 9337 } 9338 if (flags & JS_PROP_HAS_VALUE) { 9339 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags); 9340 } 9341 return TRUE; 9342 typed_array_done: ; 9343 } 9344 } 9345 9346 return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); 9347 } 9348 9349 static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj, 9350 JSAtom prop, JSAutoInitIDEnum id, 9351 void *opaque, int flags) 9352 { 9353 JSObject *p; 9354 JSProperty *pr; 9355 9356 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) 9357 return FALSE; 9358 9359 p = JS_VALUE_GET_OBJ(this_obj); 9360 9361 if (find_own_property(&pr, p, prop)) { 9362 /* property already exists */ 9363 abort(); 9364 return FALSE; 9365 } 9366 9367 /* Specialized CreateProperty */ 9368 pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); 9369 if (unlikely(!pr)) 9370 return -1; 9371 pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); 9372 assert((pr->u.init.realm_and_id & 3) == 0); 9373 assert(id <= 3); 9374 pr->u.init.realm_and_id |= id; 9375 pr->u.init.opaque = opaque; 9376 return TRUE; 9377 } 9378 9379 /* shortcut to add or redefine a new property value */ 9380 int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj, 9381 JSAtom prop, JSValue val, int flags) 9382 { 9383 int ret; 9384 ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, 9385 flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); 9386 JS_FreeValue(ctx, val); 9387 return ret; 9388 } 9389 9390 int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj, 9391 JSValue prop, JSValue val, int flags) 9392 { 9393 JSAtom atom; 9394 int ret; 9395 atom = JS_ValueToAtom(ctx, prop); 9396 JS_FreeValue(ctx, prop); 9397 if (unlikely(atom == JS_ATOM_NULL)) { 9398 JS_FreeValue(ctx, val); 9399 return -1; 9400 } 9401 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); 9402 JS_FreeAtom(ctx, atom); 9403 return ret; 9404 } 9405 9406 int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj, 9407 uint32_t idx, JSValue val, int flags) 9408 { 9409 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx), 9410 val, flags); 9411 } 9412 9413 int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj, 9414 int64_t idx, JSValue val, int flags) 9415 { 9416 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), 9417 val, flags); 9418 } 9419 9420 int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, 9421 const char *prop, JSValue val, int flags) 9422 { 9423 JSAtom atom; 9424 int ret; 9425 atom = JS_NewAtom(ctx, prop); 9426 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); 9427 JS_FreeAtom(ctx, atom); 9428 return ret; 9429 } 9430 9431 /* shortcut to add getter & setter */ 9432 int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj, 9433 JSAtom prop, JSValue getter, JSValue setter, 9434 int flags) 9435 { 9436 int ret; 9437 ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, 9438 flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | 9439 JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); 9440 JS_FreeValue(ctx, getter); 9441 JS_FreeValue(ctx, setter); 9442 return ret; 9443 } 9444 9445 static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj, 9446 int64_t idx, JSValue val, int flags) 9447 { 9448 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), 9449 val, flags | JS_PROP_CONFIGURABLE | 9450 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); 9451 } 9452 9453 9454 /* return TRUE if 'obj' has a non empty 'name' string */ 9455 static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj) 9456 { 9457 JSProperty *pr; 9458 JSShapeProperty *prs; 9459 JSValueConst val; 9460 JSString *p; 9461 9462 prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); 9463 if (!prs) 9464 return FALSE; 9465 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) 9466 return TRUE; 9467 val = pr->u.value; 9468 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 9469 return TRUE; 9470 p = JS_VALUE_GET_STRING(val); 9471 return (p->len != 0); 9472 } 9473 9474 static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj, 9475 JSAtom name, int flags) 9476 { 9477 if (name != JS_ATOM_NULL 9478 && JS_IsObject(obj) 9479 && !js_object_has_name(ctx, obj) 9480 && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { 9481 return -1; 9482 } 9483 return 0; 9484 } 9485 9486 static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj, 9487 JSValueConst str, int flags) 9488 { 9489 if (JS_IsObject(obj) && 9490 !js_object_has_name(ctx, obj)) { 9491 JSAtom prop; 9492 JSValue name_str; 9493 prop = JS_ValueToAtom(ctx, str); 9494 if (prop == JS_ATOM_NULL) 9495 return -1; 9496 name_str = js_get_function_name(ctx, prop); 9497 JS_FreeAtom(ctx, prop); 9498 if (JS_IsException(name_str)) 9499 return -1; 9500 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) 9501 return -1; 9502 } 9503 return 0; 9504 } 9505 9506 #define DEFINE_GLOBAL_LEX_VAR (1 << 7) 9507 #define DEFINE_GLOBAL_FUNC_VAR (1 << 6) 9508 9509 static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) 9510 { 9511 return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); 9512 } 9513 9514 /* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ 9515 /* XXX: could support exotic global object. */ 9516 static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) 9517 { 9518 JSObject *p; 9519 JSShapeProperty *prs; 9520 9521 p = JS_VALUE_GET_OBJ(ctx->global_obj); 9522 prs = find_own_property1(p, prop); 9523 /* XXX: should handle JS_PROP_AUTOINIT */ 9524 if (flags & DEFINE_GLOBAL_LEX_VAR) { 9525 if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) 9526 goto fail_redeclaration; 9527 } else { 9528 if (!prs && !p->extensible) 9529 goto define_error; 9530 if (flags & DEFINE_GLOBAL_FUNC_VAR) { 9531 if (prs) { 9532 if (!(prs->flags & JS_PROP_CONFIGURABLE) && 9533 ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || 9534 ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != 9535 (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { 9536 define_error: 9537 JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", 9538 prop); 9539 return -1; 9540 } 9541 } 9542 } 9543 } 9544 /* check if there already is a lexical declaration */ 9545 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9546 prs = find_own_property1(p, prop); 9547 if (prs) { 9548 fail_redeclaration: 9549 JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); 9550 return -1; 9551 } 9552 return 0; 9553 } 9554 9555 /* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | 9556 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ 9557 /* XXX: could support exotic global object. */ 9558 static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) 9559 { 9560 JSObject *p; 9561 JSShapeProperty *prs; 9562 JSProperty *pr; 9563 JSValue val; 9564 int flags; 9565 9566 if (def_flags & DEFINE_GLOBAL_LEX_VAR) { 9567 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9568 flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | 9569 JS_PROP_CONFIGURABLE; 9570 val = JS_UNINITIALIZED; 9571 } else { 9572 p = JS_VALUE_GET_OBJ(ctx->global_obj); 9573 flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | 9574 (def_flags & JS_PROP_CONFIGURABLE); 9575 val = JS_UNDEFINED; 9576 } 9577 prs = find_own_property1(p, prop); 9578 if (prs) 9579 return 0; 9580 if (!p->extensible) 9581 return 0; 9582 pr = add_property(ctx, p, prop, flags); 9583 if (unlikely(!pr)) 9584 return -1; 9585 pr->u.value = val; 9586 return 0; 9587 } 9588 9589 /* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ 9590 /* XXX: could support exotic global object. */ 9591 static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, 9592 JSValueConst func, int def_flags) 9593 { 9594 9595 JSObject *p; 9596 JSShapeProperty *prs; 9597 int flags; 9598 9599 p = JS_VALUE_GET_OBJ(ctx->global_obj); 9600 prs = find_own_property1(p, prop); 9601 flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; 9602 if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { 9603 flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | 9604 JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; 9605 } 9606 if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, 9607 JS_UNDEFINED, JS_UNDEFINED, flags) < 0) 9608 return -1; 9609 return 0; 9610 } 9611 9612 static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, 9613 BOOL throw_ref_error) 9614 { 9615 JSObject *p; 9616 JSShapeProperty *prs; 9617 JSProperty *pr; 9618 9619 /* no exotic behavior is possible in global_var_obj */ 9620 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9621 prs = find_own_property(&pr, p, prop); 9622 if (prs) { 9623 /* XXX: should handle JS_PROP_TMASK properties */ 9624 if (unlikely(JS_IsUninitialized(pr->u.value))) 9625 return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 9626 return JS_DupValue(ctx, pr->u.value); 9627 } 9628 return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, 9629 ctx->global_obj, throw_ref_error); 9630 } 9631 9632 /* construct a reference to a global variable */ 9633 static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) 9634 { 9635 JSObject *p; 9636 JSShapeProperty *prs; 9637 JSProperty *pr; 9638 9639 /* no exotic behavior is possible in global_var_obj */ 9640 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9641 prs = find_own_property(&pr, p, prop); 9642 if (prs) { 9643 /* XXX: should handle JS_PROP_AUTOINIT properties? */ 9644 /* XXX: conformance: do these tests in 9645 OP_put_var_ref/OP_get_var_ref ? */ 9646 if (unlikely(JS_IsUninitialized(pr->u.value))) { 9647 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 9648 return -1; 9649 } 9650 if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { 9651 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); 9652 } 9653 sp[0] = JS_DupValue(ctx, ctx->global_var_obj); 9654 } else { 9655 int ret; 9656 ret = JS_HasProperty(ctx, ctx->global_obj, prop); 9657 if (ret < 0) 9658 return -1; 9659 if (ret) { 9660 sp[0] = JS_DupValue(ctx, ctx->global_obj); 9661 } else { 9662 sp[0] = JS_UNDEFINED; 9663 } 9664 } 9665 sp[1] = JS_AtomToValue(ctx, prop); 9666 return 0; 9667 } 9668 9669 /* use for strict variable access: test if the variable exists */ 9670 static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) 9671 { 9672 JSObject *p; 9673 JSShapeProperty *prs; 9674 int ret; 9675 9676 /* no exotic behavior is possible in global_var_obj */ 9677 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9678 prs = find_own_property1(p, prop); 9679 if (prs) { 9680 ret = TRUE; 9681 } else { 9682 ret = JS_HasProperty(ctx, ctx->global_obj, prop); 9683 if (ret < 0) 9684 return -1; 9685 } 9686 return ret; 9687 } 9688 9689 /* flag = 0: normal variable write 9690 flag = 1: initialize lexical variable 9691 flag = 2: normal variable write, strict check was done before 9692 */ 9693 static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, 9694 int flag) 9695 { 9696 JSObject *p; 9697 JSShapeProperty *prs; 9698 JSProperty *pr; 9699 int flags; 9700 9701 /* no exotic behavior is possible in global_var_obj */ 9702 p = JS_VALUE_GET_OBJ(ctx->global_var_obj); 9703 prs = find_own_property(&pr, p, prop); 9704 if (prs) { 9705 /* XXX: should handle JS_PROP_AUTOINIT properties? */ 9706 if (flag != 1) { 9707 if (unlikely(JS_IsUninitialized(pr->u.value))) { 9708 JS_FreeValue(ctx, val); 9709 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); 9710 return -1; 9711 } 9712 if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { 9713 JS_FreeValue(ctx, val); 9714 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); 9715 } 9716 } 9717 set_value(ctx, &pr->u.value, val); 9718 return 0; 9719 } 9720 flags = JS_PROP_THROW_STRICT; 9721 if (is_strict_mode(ctx)) 9722 flags |= JS_PROP_NO_ADD; 9723 return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags); 9724 } 9725 9726 /* return -1, FALSE or TRUE. return FALSE if not configurable or 9727 invalid object. return -1 in case of exception. 9728 flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ 9729 int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags) 9730 { 9731 JSValue obj1; 9732 JSObject *p; 9733 int res; 9734 9735 obj1 = JS_ToObject(ctx, obj); 9736 if (JS_IsException(obj1)) 9737 return -1; 9738 p = JS_VALUE_GET_OBJ(obj1); 9739 res = delete_property(ctx, p, prop); 9740 JS_FreeValue(ctx, obj1); 9741 if (res != FALSE) 9742 return res; 9743 if ((flags & JS_PROP_THROW) || 9744 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 9745 JS_ThrowTypeError(ctx, "could not delete property"); 9746 return -1; 9747 } 9748 return FALSE; 9749 } 9750 9751 int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags) 9752 { 9753 JSAtom prop; 9754 int res; 9755 9756 if ((uint64_t)idx <= JS_ATOM_MAX_INT) { 9757 /* fast path for fast arrays */ 9758 return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); 9759 } 9760 prop = JS_NewAtomInt64(ctx, idx); 9761 if (prop == JS_ATOM_NULL) 9762 return -1; 9763 res = JS_DeleteProperty(ctx, obj, prop, flags); 9764 JS_FreeAtom(ctx, prop); 9765 return res; 9766 } 9767 9768 BOOL JS_IsFunction(JSContext *ctx, JSValueConst val) 9769 { 9770 JSObject *p; 9771 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9772 return FALSE; 9773 p = JS_VALUE_GET_OBJ(val); 9774 switch(p->class_id) { 9775 case JS_CLASS_BYTECODE_FUNCTION: 9776 return TRUE; 9777 case JS_CLASS_PROXY: 9778 return p->u.proxy_data->is_func; 9779 default: 9780 return (ctx->rt->class_array[p->class_id].call != NULL); 9781 } 9782 } 9783 9784 BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic) 9785 { 9786 JSObject *p; 9787 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9788 return FALSE; 9789 p = JS_VALUE_GET_OBJ(val); 9790 if (p->class_id == JS_CLASS_C_FUNCTION) 9791 return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); 9792 else 9793 return FALSE; 9794 } 9795 9796 BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val) 9797 { 9798 JSObject *p; 9799 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9800 return FALSE; 9801 p = JS_VALUE_GET_OBJ(val); 9802 return p->is_constructor; 9803 } 9804 9805 BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val) 9806 { 9807 JSObject *p; 9808 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 9809 return FALSE; 9810 p = JS_VALUE_GET_OBJ(func_obj); 9811 p->is_constructor = val; 9812 return TRUE; 9813 } 9814 9815 BOOL JS_IsError(JSContext *ctx, JSValueConst val) 9816 { 9817 JSObject *p; 9818 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9819 return FALSE; 9820 p = JS_VALUE_GET_OBJ(val); 9821 return (p->class_id == JS_CLASS_ERROR); 9822 } 9823 9824 /* used to avoid catching interrupt exceptions */ 9825 BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val) 9826 { 9827 JSObject *p; 9828 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9829 return FALSE; 9830 p = JS_VALUE_GET_OBJ(val); 9831 return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; 9832 } 9833 9834 void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag) 9835 { 9836 JSObject *p; 9837 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9838 return; 9839 p = JS_VALUE_GET_OBJ(val); 9840 if (p->class_id == JS_CLASS_ERROR) 9841 p->is_uncatchable_error = flag; 9842 } 9843 9844 void JS_ResetUncatchableError(JSContext *ctx) 9845 { 9846 JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); 9847 } 9848 9849 void JS_SetOpaque(JSValue obj, void *opaque) 9850 { 9851 JSObject *p; 9852 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 9853 p = JS_VALUE_GET_OBJ(obj); 9854 p->u.opaque = opaque; 9855 } 9856 } 9857 9858 /* return NULL if not an object of class class_id */ 9859 void *JS_GetOpaque(JSValueConst obj, JSClassID class_id) 9860 { 9861 JSObject *p; 9862 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 9863 return NULL; 9864 p = JS_VALUE_GET_OBJ(obj); 9865 if (p->class_id != class_id) 9866 return NULL; 9867 return p->u.opaque; 9868 } 9869 9870 void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id) 9871 { 9872 void *p = JS_GetOpaque(obj, class_id); 9873 if (unlikely(!p)) { 9874 JS_ThrowTypeErrorInvalidClass(ctx, class_id); 9875 } 9876 return p; 9877 } 9878 9879 static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) 9880 { 9881 int i; 9882 BOOL force_ordinary; 9883 9884 JSAtom method_name; 9885 JSValue method, ret; 9886 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) 9887 return val; 9888 force_ordinary = hint & HINT_FORCE_ORDINARY; 9889 hint &= ~HINT_FORCE_ORDINARY; 9890 if (!force_ordinary) { 9891 method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); 9892 if (JS_IsException(method)) 9893 goto exception; 9894 /* ECMA says *If exoticToPrim is not undefined* but tests in 9895 test262 use null as a non callable converter */ 9896 if (!JS_IsUndefined(method) && !JS_IsNull(method)) { 9897 JSAtom atom; 9898 JSValue arg; 9899 switch(hint) { 9900 case HINT_STRING: 9901 atom = JS_ATOM_string; 9902 break; 9903 case HINT_NUMBER: 9904 atom = JS_ATOM_number; 9905 break; 9906 default: 9907 case HINT_NONE: 9908 atom = JS_ATOM_default; 9909 break; 9910 } 9911 arg = JS_AtomToString(ctx, atom); 9912 ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); 9913 JS_FreeValue(ctx, arg); 9914 if (JS_IsException(ret)) 9915 goto exception; 9916 JS_FreeValue(ctx, val); 9917 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) 9918 return ret; 9919 JS_FreeValue(ctx, ret); 9920 return JS_ThrowTypeError(ctx, "toPrimitive"); 9921 } 9922 } 9923 if (hint != HINT_STRING) 9924 hint = HINT_NUMBER; 9925 for(i = 0; i < 2; i++) { 9926 if ((i ^ hint) == 0) { 9927 method_name = JS_ATOM_toString; 9928 } else { 9929 method_name = JS_ATOM_valueOf; 9930 } 9931 method = JS_GetProperty(ctx, val, method_name); 9932 if (JS_IsException(method)) 9933 goto exception; 9934 if (JS_IsFunction(ctx, method)) { 9935 ret = JS_CallFree(ctx, method, val, 0, NULL); 9936 if (JS_IsException(ret)) 9937 goto exception; 9938 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 9939 JS_FreeValue(ctx, val); 9940 return ret; 9941 } 9942 JS_FreeValue(ctx, ret); 9943 } else { 9944 JS_FreeValue(ctx, method); 9945 } 9946 } 9947 JS_ThrowTypeError(ctx, "toPrimitive"); 9948 exception: 9949 JS_FreeValue(ctx, val); 9950 return JS_EXCEPTION; 9951 } 9952 9953 static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint) 9954 { 9955 return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint); 9956 } 9957 9958 void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj) 9959 { 9960 JSObject *p; 9961 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 9962 return; 9963 p = JS_VALUE_GET_OBJ(obj); 9964 p->is_HTMLDDA = TRUE; 9965 } 9966 9967 static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj) 9968 { 9969 JSObject *p; 9970 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 9971 return FALSE; 9972 p = JS_VALUE_GET_OBJ(obj); 9973 return p->is_HTMLDDA; 9974 } 9975 9976 static int JS_ToBoolFree(JSContext *ctx, JSValue val) 9977 { 9978 uint32_t tag = JS_VALUE_GET_TAG(val); 9979 switch(tag) { 9980 case JS_TAG_INT: 9981 return JS_VALUE_GET_INT(val) != 0; 9982 case JS_TAG_BOOL: 9983 case JS_TAG_NULL: 9984 case JS_TAG_UNDEFINED: 9985 return JS_VALUE_GET_INT(val); 9986 case JS_TAG_EXCEPTION: 9987 return -1; 9988 case JS_TAG_STRING: 9989 { 9990 BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; 9991 JS_FreeValue(ctx, val); 9992 return ret; 9993 } 9994 case JS_TAG_BIG_INT: 9995 #ifdef CONFIG_BIGNUM 9996 case JS_TAG_BIG_FLOAT: 9997 #endif 9998 { 9999 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10000 BOOL ret; 10001 ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; 10002 JS_FreeValue(ctx, val); 10003 return ret; 10004 } 10005 #ifdef CONFIG_BIGNUM 10006 case JS_TAG_BIG_DECIMAL: 10007 { 10008 JSBigDecimal *p = JS_VALUE_GET_PTR(val); 10009 BOOL ret; 10010 ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; 10011 JS_FreeValue(ctx, val); 10012 return ret; 10013 } 10014 #endif 10015 case JS_TAG_OBJECT: 10016 { 10017 JSObject *p = JS_VALUE_GET_OBJ(val); 10018 BOOL ret; 10019 ret = !p->is_HTMLDDA; 10020 JS_FreeValue(ctx, val); 10021 return ret; 10022 } 10023 break; 10024 default: 10025 if (JS_TAG_IS_FLOAT64(tag)) { 10026 double d = JS_VALUE_GET_FLOAT64(val); 10027 return !isnan(d) && d != 0; 10028 } else { 10029 JS_FreeValue(ctx, val); 10030 return TRUE; 10031 } 10032 } 10033 } 10034 10035 int JS_ToBool(JSContext *ctx, JSValueConst val) 10036 { 10037 return JS_ToBoolFree(ctx, JS_DupValue(ctx, val)); 10038 } 10039 10040 static int skip_spaces(const char *pc) 10041 { 10042 const uint8_t *p, *p_next, *p_start; 10043 uint32_t c; 10044 10045 p = p_start = (const uint8_t *)pc; 10046 for (;;) { 10047 c = *p; 10048 if (c < 128) { 10049 if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) 10050 break; 10051 p++; 10052 } else { 10053 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 10054 if (!lre_is_space(c)) 10055 break; 10056 p = p_next; 10057 } 10058 } 10059 return p - p_start; 10060 } 10061 10062 static inline int to_digit(int c) 10063 { 10064 if (c >= '0' && c <= '9') 10065 return c - '0'; 10066 else if (c >= 'A' && c <= 'Z') 10067 return c - 'A' + 10; 10068 else if (c >= 'a' && c <= 'z') 10069 return c - 'a' + 10; 10070 else 10071 return 36; 10072 } 10073 10074 /* XXX: remove */ 10075 static double js_strtod(const char *str, int radix, BOOL is_float) 10076 { 10077 double d; 10078 int c; 10079 10080 if (!is_float || radix != 10) { 10081 const char *p = str; 10082 uint64_t n_max, n; 10083 int int_exp, is_neg; 10084 10085 is_neg = 0; 10086 if (*p == '-') { 10087 is_neg = 1; 10088 p++; 10089 } 10090 10091 /* skip leading zeros */ 10092 while (*p == '0') 10093 p++; 10094 n = 0; 10095 if (radix == 10) 10096 n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ 10097 else 10098 n_max = ((uint64_t)-1 - (radix - 1)) / radix; 10099 /* XXX: could be more precise */ 10100 int_exp = 0; 10101 while (*p != '\0') { 10102 c = to_digit((uint8_t)*p); 10103 if (c >= radix) 10104 break; 10105 if (n <= n_max) { 10106 n = n * radix + c; 10107 } else { 10108 if (radix == 10) 10109 goto strtod_case; 10110 int_exp++; 10111 } 10112 p++; 10113 } 10114 d = n; 10115 if (int_exp != 0) { 10116 d *= pow(radix, int_exp); 10117 } 10118 if (is_neg) 10119 d = -d; 10120 } else { 10121 strtod_case: 10122 d = strtod(str, NULL); 10123 } 10124 return d; 10125 } 10126 10127 #define ATOD_INT_ONLY (1 << 0) 10128 /* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */ 10129 #define ATOD_ACCEPT_BIN_OCT (1 << 2) 10130 /* accept O prefix as octal if radix == 0 and properly formed (Annex B) */ 10131 #define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4) 10132 /* accept _ between digits as a digit separator */ 10133 #define ATOD_ACCEPT_UNDERSCORES (1 << 5) 10134 /* allow a suffix to override the type */ 10135 #define ATOD_ACCEPT_SUFFIX (1 << 6) 10136 /* default type */ 10137 #define ATOD_TYPE_MASK (3 << 7) 10138 #define ATOD_TYPE_FLOAT64 (0 << 7) 10139 #define ATOD_TYPE_BIG_INT (1 << 7) 10140 #ifdef CONFIG_BIGNUM 10141 #define ATOD_TYPE_BIG_FLOAT (2 << 7) 10142 #define ATOD_TYPE_BIG_DECIMAL (3 << 7) 10143 /* assume bigint mode: floats are parsed as integers if no decimal 10144 point nor exponent */ 10145 #define ATOD_MODE_BIGINT (1 << 9) 10146 #endif 10147 /* accept -0x1 */ 10148 #define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10) 10149 10150 static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, 10151 int radix, int flags, slimb_t *pexponent) 10152 { 10153 bf_t a_s, *a = &a_s; 10154 int ret; 10155 JSValue val; 10156 val = JS_NewBigInt(ctx); 10157 if (JS_IsException(val)) 10158 return val; 10159 a = JS_GetBigInt(val); 10160 ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); 10161 if (ret & BF_ST_MEM_ERROR) { 10162 JS_FreeValue(ctx, val); 10163 return JS_ThrowOutOfMemory(ctx); 10164 } 10165 #ifdef CONFIG_BIGNUM 10166 val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); 10167 #else 10168 val = JS_CompactBigInt1(ctx, val, FALSE); 10169 #endif 10170 return val; 10171 } 10172 10173 #ifdef CONFIG_BIGNUM 10174 static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, 10175 int radix, int flags, slimb_t *pexponent) 10176 { 10177 bf_t *a; 10178 int ret; 10179 JSValue val; 10180 10181 val = JS_NewBigFloat(ctx); 10182 if (JS_IsException(val)) 10183 return val; 10184 a = JS_GetBigFloat(val); 10185 if (flags & ATOD_ACCEPT_SUFFIX) { 10186 /* return the exponent to get infinite precision */ 10187 ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF, 10188 BF_RNDZ | BF_ATOF_EXPONENT); 10189 } else { 10190 ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec, 10191 ctx->fp_env.flags); 10192 } 10193 if (ret & BF_ST_MEM_ERROR) { 10194 JS_FreeValue(ctx, val); 10195 return JS_ThrowOutOfMemory(ctx); 10196 } 10197 return val; 10198 } 10199 10200 static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, 10201 int radix, int flags, slimb_t *pexponent) 10202 { 10203 bfdec_t *a; 10204 int ret; 10205 JSValue val; 10206 10207 val = JS_NewBigDecimal(ctx); 10208 if (JS_IsException(val)) 10209 return val; 10210 a = JS_GetBigDecimal(val); 10211 ret = bfdec_atof(a, buf, NULL, BF_PREC_INF, 10212 BF_RNDZ | BF_ATOF_NO_NAN_INF); 10213 if (ret & BF_ST_MEM_ERROR) { 10214 JS_FreeValue(ctx, val); 10215 return JS_ThrowOutOfMemory(ctx); 10216 } 10217 return val; 10218 } 10219 #endif 10220 10221 /* return an exception in case of memory error. Return JS_NAN if 10222 invalid syntax */ 10223 #ifdef CONFIG_BIGNUM 10224 static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp, 10225 int radix, int flags, slimb_t *pexponent) 10226 #else 10227 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, 10228 int radix, int flags) 10229 #endif 10230 { 10231 const char *p, *p_start; 10232 int sep, is_neg; 10233 BOOL is_float, has_legacy_octal; 10234 int atod_type = flags & ATOD_TYPE_MASK; 10235 char buf1[64], *buf; 10236 int i, j, len; 10237 BOOL buf_allocated = FALSE; 10238 JSValue val; 10239 10240 /* optional separator between digits */ 10241 sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; 10242 has_legacy_octal = FALSE; 10243 10244 p = str; 10245 p_start = p; 10246 is_neg = 0; 10247 if (p[0] == '+') { 10248 p++; 10249 p_start++; 10250 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) 10251 goto no_radix_prefix; 10252 } else if (p[0] == '-') { 10253 p++; 10254 p_start++; 10255 is_neg = 1; 10256 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN)) 10257 goto no_radix_prefix; 10258 } 10259 if (p[0] == '0') { 10260 if ((p[1] == 'x' || p[1] == 'X') && 10261 (radix == 0 || radix == 16)) { 10262 p += 2; 10263 radix = 16; 10264 } else if ((p[1] == 'o' || p[1] == 'O') && 10265 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { 10266 p += 2; 10267 radix = 8; 10268 } else if ((p[1] == 'b' || p[1] == 'B') && 10269 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) { 10270 p += 2; 10271 radix = 2; 10272 } else if ((p[1] >= '0' && p[1] <= '9') && 10273 radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) { 10274 int i; 10275 has_legacy_octal = TRUE; 10276 sep = 256; 10277 for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++) 10278 continue; 10279 if (p[i] == '8' || p[i] == '9') 10280 goto no_prefix; 10281 p += 1; 10282 radix = 8; 10283 } else { 10284 goto no_prefix; 10285 } 10286 /* there must be a digit after the prefix */ 10287 if (to_digit((uint8_t)*p) >= radix) 10288 goto fail; 10289 no_prefix: ; 10290 } else { 10291 no_radix_prefix: 10292 if (!(flags & ATOD_INT_ONLY) && 10293 (atod_type == ATOD_TYPE_FLOAT64 10294 #ifdef CONFIG_BIGNUM 10295 || atod_type == ATOD_TYPE_BIG_FLOAT 10296 #endif 10297 ) && 10298 strstart(p, "Infinity", &p)) { 10299 #ifdef CONFIG_BIGNUM 10300 if (atod_type == ATOD_TYPE_BIG_FLOAT) { 10301 bf_t *a; 10302 val = JS_NewBigFloat(ctx); 10303 if (JS_IsException(val)) 10304 goto done; 10305 a = JS_GetBigFloat(val); 10306 bf_set_inf(a, is_neg); 10307 } else 10308 #endif 10309 { 10310 double d = 1.0 / 0.0; 10311 if (is_neg) 10312 d = -d; 10313 val = JS_NewFloat64(ctx, d); 10314 } 10315 goto done; 10316 } 10317 } 10318 if (radix == 0) 10319 radix = 10; 10320 is_float = FALSE; 10321 p_start = p; 10322 while (to_digit((uint8_t)*p) < radix 10323 || (*p == sep && (radix != 10 || 10324 p != p_start + 1 || p[-1] != '0') && 10325 to_digit((uint8_t)p[1]) < radix)) { 10326 p++; 10327 } 10328 if (!(flags & ATOD_INT_ONLY)) { 10329 if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) { 10330 is_float = TRUE; 10331 p++; 10332 if (*p == sep) 10333 goto fail; 10334 while (to_digit((uint8_t)*p) < radix || 10335 (*p == sep && to_digit((uint8_t)p[1]) < radix)) 10336 p++; 10337 } 10338 if (p > p_start && 10339 (((*p == 'e' || *p == 'E') && radix == 10) || 10340 ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { 10341 const char *p1 = p + 1; 10342 is_float = TRUE; 10343 if (*p1 == '+') { 10344 p1++; 10345 } else if (*p1 == '-') { 10346 p1++; 10347 } 10348 if (is_digit((uint8_t)*p1)) { 10349 p = p1 + 1; 10350 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) 10351 p++; 10352 } 10353 } 10354 } 10355 if (p == p_start) 10356 goto fail; 10357 10358 buf = buf1; 10359 buf_allocated = FALSE; 10360 len = p - p_start; 10361 if (unlikely((len + 2) > sizeof(buf1))) { 10362 buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ 10363 if (!buf) 10364 goto mem_error; 10365 buf_allocated = TRUE; 10366 } 10367 /* remove the separators and the radix prefixes */ 10368 j = 0; 10369 if (is_neg) 10370 buf[j++] = '-'; 10371 for (i = 0; i < len; i++) { 10372 if (p_start[i] != '_') 10373 buf[j++] = p_start[i]; 10374 } 10375 buf[j] = '\0'; 10376 10377 if (flags & ATOD_ACCEPT_SUFFIX) { 10378 if (*p == 'n') { 10379 p++; 10380 atod_type = ATOD_TYPE_BIG_INT; 10381 } else 10382 #ifdef CONFIG_BIGNUM 10383 if (*p == 'l') { 10384 p++; 10385 atod_type = ATOD_TYPE_BIG_FLOAT; 10386 } else if (*p == 'm') { 10387 p++; 10388 atod_type = ATOD_TYPE_BIG_DECIMAL; 10389 } else if (flags & ATOD_MODE_BIGINT) { 10390 if (!is_float) 10391 atod_type = ATOD_TYPE_BIG_INT; 10392 if (has_legacy_octal) 10393 goto fail; 10394 } else 10395 #endif 10396 { 10397 if (is_float && radix != 10) 10398 goto fail; 10399 } 10400 } else { 10401 if (atod_type == ATOD_TYPE_FLOAT64) { 10402 #ifdef CONFIG_BIGNUM 10403 if (flags & ATOD_MODE_BIGINT) { 10404 if (!is_float) 10405 atod_type = ATOD_TYPE_BIG_INT; 10406 if (has_legacy_octal) 10407 goto fail; 10408 } else 10409 #endif 10410 { 10411 if (is_float && radix != 10) 10412 goto fail; 10413 } 10414 } 10415 } 10416 10417 switch(atod_type) { 10418 case ATOD_TYPE_FLOAT64: 10419 { 10420 double d; 10421 d = js_strtod(buf, radix, is_float); 10422 /* return int or float64 */ 10423 val = JS_NewFloat64(ctx, d); 10424 } 10425 break; 10426 case ATOD_TYPE_BIG_INT: 10427 if (has_legacy_octal || is_float) 10428 goto fail; 10429 val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); 10430 break; 10431 #ifdef CONFIG_BIGNUM 10432 case ATOD_TYPE_BIG_FLOAT: 10433 if (has_legacy_octal) 10434 goto fail; 10435 val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags, 10436 pexponent); 10437 break; 10438 case ATOD_TYPE_BIG_DECIMAL: 10439 if (radix != 10) 10440 goto fail; 10441 val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); 10442 break; 10443 #endif 10444 default: 10445 abort(); 10446 } 10447 10448 done: 10449 if (buf_allocated) 10450 js_free_rt(ctx->rt, buf); 10451 if (pp) 10452 *pp = p; 10453 return val; 10454 fail: 10455 val = JS_NAN; 10456 goto done; 10457 mem_error: 10458 val = JS_ThrowOutOfMemory(ctx); 10459 goto done; 10460 } 10461 10462 #ifdef CONFIG_BIGNUM 10463 static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, 10464 int radix, int flags) 10465 { 10466 return js_atof2(ctx, str, pp, radix, flags, NULL); 10467 } 10468 #endif 10469 10470 typedef enum JSToNumberHintEnum { 10471 TON_FLAG_NUMBER, 10472 TON_FLAG_NUMERIC, 10473 } JSToNumberHintEnum; 10474 10475 static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, 10476 JSToNumberHintEnum flag) 10477 { 10478 uint32_t tag; 10479 JSValue ret; 10480 10481 redo: 10482 tag = JS_VALUE_GET_NORM_TAG(val); 10483 switch(tag) { 10484 case JS_TAG_BIG_INT: 10485 if (flag != TON_FLAG_NUMERIC) { 10486 JS_FreeValue(ctx, val); 10487 return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); 10488 } 10489 ret = val; 10490 break; 10491 #ifdef CONFIG_BIGNUM 10492 case JS_TAG_BIG_DECIMAL: 10493 if (flag != TON_FLAG_NUMERIC) { 10494 JS_FreeValue(ctx, val); 10495 return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number"); 10496 } 10497 ret = val; 10498 break; 10499 case JS_TAG_BIG_FLOAT: 10500 if (flag != TON_FLAG_NUMERIC) { 10501 JS_FreeValue(ctx, val); 10502 return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number"); 10503 } 10504 ret = val; 10505 break; 10506 #endif 10507 case JS_TAG_FLOAT64: 10508 case JS_TAG_INT: 10509 case JS_TAG_EXCEPTION: 10510 ret = val; 10511 break; 10512 case JS_TAG_BOOL: 10513 case JS_TAG_NULL: 10514 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); 10515 break; 10516 case JS_TAG_UNDEFINED: 10517 ret = JS_NAN; 10518 break; 10519 case JS_TAG_OBJECT: 10520 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 10521 if (JS_IsException(val)) 10522 return JS_EXCEPTION; 10523 goto redo; 10524 case JS_TAG_STRING: 10525 { 10526 const char *str; 10527 const char *p; 10528 size_t len; 10529 10530 str = JS_ToCStringLen(ctx, &len, val); 10531 JS_FreeValue(ctx, val); 10532 if (!str) 10533 return JS_EXCEPTION; 10534 p = str; 10535 p += skip_spaces(p); 10536 if ((p - str) == len) { 10537 ret = JS_NewInt32(ctx, 0); 10538 } else { 10539 int flags = ATOD_ACCEPT_BIN_OCT; 10540 ret = js_atof(ctx, p, &p, 0, flags); 10541 if (!JS_IsException(ret)) { 10542 p += skip_spaces(p); 10543 if ((p - str) != len) { 10544 JS_FreeValue(ctx, ret); 10545 ret = JS_NAN; 10546 } 10547 } 10548 } 10549 JS_FreeCString(ctx, str); 10550 } 10551 break; 10552 case JS_TAG_SYMBOL: 10553 JS_FreeValue(ctx, val); 10554 return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); 10555 default: 10556 JS_FreeValue(ctx, val); 10557 ret = JS_NAN; 10558 break; 10559 } 10560 return ret; 10561 } 10562 10563 static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) 10564 { 10565 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); 10566 } 10567 10568 static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) 10569 { 10570 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); 10571 } 10572 10573 static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) 10574 { 10575 return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); 10576 } 10577 10578 static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, 10579 JSValue val) 10580 { 10581 double d; 10582 uint32_t tag; 10583 10584 val = JS_ToNumberFree(ctx, val); 10585 if (JS_IsException(val)) { 10586 *pres = JS_FLOAT64_NAN; 10587 return -1; 10588 } 10589 tag = JS_VALUE_GET_NORM_TAG(val); 10590 switch(tag) { 10591 case JS_TAG_INT: 10592 d = JS_VALUE_GET_INT(val); 10593 break; 10594 case JS_TAG_FLOAT64: 10595 d = JS_VALUE_GET_FLOAT64(val); 10596 break; 10597 case JS_TAG_BIG_INT: 10598 #ifdef CONFIG_BIGNUM 10599 case JS_TAG_BIG_FLOAT: 10600 #endif 10601 { 10602 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10603 /* XXX: there can be a double rounding issue with some 10604 primitives (such as JS_ToUint8ClampFree()), but it is 10605 not critical to fix it. */ 10606 bf_get_float64(&p->num, &d, BF_RNDN); 10607 JS_FreeValue(ctx, val); 10608 } 10609 break; 10610 default: 10611 abort(); 10612 } 10613 *pres = d; 10614 return 0; 10615 } 10616 10617 static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) 10618 { 10619 uint32_t tag; 10620 10621 tag = JS_VALUE_GET_TAG(val); 10622 if (tag <= JS_TAG_NULL) { 10623 *pres = JS_VALUE_GET_INT(val); 10624 return 0; 10625 } else if (JS_TAG_IS_FLOAT64(tag)) { 10626 *pres = JS_VALUE_GET_FLOAT64(val); 10627 return 0; 10628 } else { 10629 return __JS_ToFloat64Free(ctx, pres, val); 10630 } 10631 } 10632 10633 int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val) 10634 { 10635 return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val)); 10636 } 10637 10638 static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) 10639 { 10640 return JS_ToNumberFree(ctx, JS_DupValue(ctx, val)); 10641 } 10642 10643 /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ 10644 static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) 10645 { 10646 uint32_t tag; 10647 JSValue ret; 10648 10649 redo: 10650 tag = JS_VALUE_GET_NORM_TAG(val); 10651 switch(tag) { 10652 case JS_TAG_INT: 10653 case JS_TAG_BOOL: 10654 case JS_TAG_NULL: 10655 case JS_TAG_UNDEFINED: 10656 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val)); 10657 break; 10658 case JS_TAG_FLOAT64: 10659 { 10660 double d = JS_VALUE_GET_FLOAT64(val); 10661 if (isnan(d)) { 10662 ret = JS_NewInt32(ctx, 0); 10663 } else { 10664 /* convert -0 to +0 */ 10665 d = trunc(d) + 0.0; 10666 ret = JS_NewFloat64(ctx, d); 10667 } 10668 } 10669 break; 10670 #ifdef CONFIG_BIGNUM 10671 case JS_TAG_BIG_FLOAT: 10672 { 10673 bf_t a_s, *a, r_s, *r = &r_s; 10674 BOOL is_nan; 10675 10676 a = JS_ToBigFloat(ctx, &a_s, val); 10677 if (!a) { 10678 JS_FreeValue(ctx, val); 10679 return JS_EXCEPTION; 10680 } 10681 if (!bf_is_finite(a)) { 10682 is_nan = bf_is_nan(a); 10683 if (is_nan) 10684 ret = JS_NewInt32(ctx, 0); 10685 else 10686 ret = JS_DupValue(ctx, val); 10687 } else { 10688 ret = JS_NewBigInt(ctx); 10689 if (!JS_IsException(ret)) { 10690 r = JS_GetBigInt(ret); 10691 bf_set(r, a); 10692 bf_rint(r, BF_RNDZ); 10693 ret = JS_CompactBigInt(ctx, ret); 10694 } 10695 } 10696 if (a == &a_s) 10697 bf_delete(a); 10698 JS_FreeValue(ctx, val); 10699 } 10700 break; 10701 #endif 10702 default: 10703 val = JS_ToNumberFree(ctx, val); 10704 if (JS_IsException(val)) 10705 return val; 10706 goto redo; 10707 } 10708 return ret; 10709 } 10710 10711 /* Note: the integer value is satured to 32 bits */ 10712 static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) 10713 { 10714 uint32_t tag; 10715 int ret; 10716 10717 redo: 10718 tag = JS_VALUE_GET_NORM_TAG(val); 10719 switch(tag) { 10720 case JS_TAG_INT: 10721 case JS_TAG_BOOL: 10722 case JS_TAG_NULL: 10723 case JS_TAG_UNDEFINED: 10724 ret = JS_VALUE_GET_INT(val); 10725 break; 10726 case JS_TAG_EXCEPTION: 10727 *pres = 0; 10728 return -1; 10729 case JS_TAG_FLOAT64: 10730 { 10731 double d = JS_VALUE_GET_FLOAT64(val); 10732 if (isnan(d)) { 10733 ret = 0; 10734 } else { 10735 if (d < INT32_MIN) 10736 ret = INT32_MIN; 10737 else if (d > INT32_MAX) 10738 ret = INT32_MAX; 10739 else 10740 ret = (int)d; 10741 } 10742 } 10743 break; 10744 #ifdef CONFIG_BIGNUM 10745 case JS_TAG_BIG_FLOAT: 10746 { 10747 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10748 bf_get_int32(&ret, &p->num, 0); 10749 JS_FreeValue(ctx, val); 10750 } 10751 break; 10752 #endif 10753 default: 10754 val = JS_ToNumberFree(ctx, val); 10755 if (JS_IsException(val)) { 10756 *pres = 0; 10757 return -1; 10758 } 10759 goto redo; 10760 } 10761 *pres = ret; 10762 return 0; 10763 } 10764 10765 int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val) 10766 { 10767 return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); 10768 } 10769 10770 int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val, 10771 int min, int max, int min_offset) 10772 { 10773 int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val)); 10774 if (res == 0) { 10775 if (*pres < min) { 10776 *pres += min_offset; 10777 if (*pres < min) 10778 *pres = min; 10779 } else { 10780 if (*pres > max) 10781 *pres = max; 10782 } 10783 } 10784 return res; 10785 } 10786 10787 static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) 10788 { 10789 uint32_t tag; 10790 10791 redo: 10792 tag = JS_VALUE_GET_NORM_TAG(val); 10793 switch(tag) { 10794 case JS_TAG_INT: 10795 case JS_TAG_BOOL: 10796 case JS_TAG_NULL: 10797 case JS_TAG_UNDEFINED: 10798 *pres = JS_VALUE_GET_INT(val); 10799 return 0; 10800 case JS_TAG_EXCEPTION: 10801 *pres = 0; 10802 return -1; 10803 case JS_TAG_FLOAT64: 10804 { 10805 double d = JS_VALUE_GET_FLOAT64(val); 10806 if (isnan(d)) { 10807 *pres = 0; 10808 } else { 10809 if (d < INT64_MIN) 10810 *pres = INT64_MIN; 10811 else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ 10812 *pres = INT64_MAX; 10813 else 10814 *pres = (int64_t)d; 10815 } 10816 } 10817 return 0; 10818 #ifdef CONFIG_BIGNUM 10819 case JS_TAG_BIG_FLOAT: 10820 { 10821 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10822 bf_get_int64(pres, &p->num, 0); 10823 JS_FreeValue(ctx, val); 10824 } 10825 return 0; 10826 #endif 10827 default: 10828 val = JS_ToNumberFree(ctx, val); 10829 if (JS_IsException(val)) { 10830 *pres = 0; 10831 return -1; 10832 } 10833 goto redo; 10834 } 10835 } 10836 10837 int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val) 10838 { 10839 return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); 10840 } 10841 10842 int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val, 10843 int64_t min, int64_t max, int64_t neg_offset) 10844 { 10845 int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val)); 10846 if (res == 0) { 10847 if (*pres < 0) 10848 *pres += neg_offset; 10849 if (*pres < min) 10850 *pres = min; 10851 else if (*pres > max) 10852 *pres = max; 10853 } 10854 return res; 10855 } 10856 10857 /* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) 10858 in case of exception */ 10859 static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 10860 { 10861 uint32_t tag; 10862 int64_t ret; 10863 10864 redo: 10865 tag = JS_VALUE_GET_NORM_TAG(val); 10866 switch(tag) { 10867 case JS_TAG_INT: 10868 case JS_TAG_BOOL: 10869 case JS_TAG_NULL: 10870 case JS_TAG_UNDEFINED: 10871 ret = JS_VALUE_GET_INT(val); 10872 break; 10873 case JS_TAG_FLOAT64: 10874 { 10875 JSFloat64Union u; 10876 double d; 10877 int e; 10878 d = JS_VALUE_GET_FLOAT64(val); 10879 u.d = d; 10880 /* we avoid doing fmod(x, 2^64) */ 10881 e = (u.u64 >> 52) & 0x7ff; 10882 if (likely(e <= (1023 + 62))) { 10883 /* fast case */ 10884 ret = (int64_t)d; 10885 } else if (e <= (1023 + 62 + 53)) { 10886 uint64_t v; 10887 /* remainder modulo 2^64 */ 10888 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); 10889 ret = v << ((e - 1023) - 52); 10890 /* take the sign into account */ 10891 if (u.u64 >> 63) 10892 ret = -ret; 10893 } else { 10894 ret = 0; /* also handles NaN and +inf */ 10895 } 10896 } 10897 break; 10898 #ifdef CONFIG_BIGNUM 10899 case JS_TAG_BIG_FLOAT: 10900 { 10901 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10902 bf_get_int64(&ret, &p->num, BF_GET_INT_MOD); 10903 JS_FreeValue(ctx, val); 10904 } 10905 break; 10906 #endif 10907 default: 10908 val = JS_ToNumberFree(ctx, val); 10909 if (JS_IsException(val)) { 10910 *pres = 0; 10911 return -1; 10912 } 10913 goto redo; 10914 } 10915 *pres = ret; 10916 return 0; 10917 } 10918 10919 int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val) 10920 { 10921 return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val)); 10922 } 10923 10924 int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val) 10925 { 10926 if (JS_IsBigInt(ctx, val)) 10927 return JS_ToBigInt64(ctx, pres, val); 10928 else 10929 return JS_ToInt64(ctx, pres, val); 10930 } 10931 10932 /* return (<0, 0) in case of exception */ 10933 static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) 10934 { 10935 uint32_t tag; 10936 int32_t ret; 10937 10938 redo: 10939 tag = JS_VALUE_GET_NORM_TAG(val); 10940 switch(tag) { 10941 case JS_TAG_INT: 10942 case JS_TAG_BOOL: 10943 case JS_TAG_NULL: 10944 case JS_TAG_UNDEFINED: 10945 ret = JS_VALUE_GET_INT(val); 10946 break; 10947 case JS_TAG_FLOAT64: 10948 { 10949 JSFloat64Union u; 10950 double d; 10951 int e; 10952 d = JS_VALUE_GET_FLOAT64(val); 10953 u.d = d; 10954 /* we avoid doing fmod(x, 2^32) */ 10955 e = (u.u64 >> 52) & 0x7ff; 10956 if (likely(e <= (1023 + 30))) { 10957 /* fast case */ 10958 ret = (int32_t)d; 10959 } else if (e <= (1023 + 30 + 53)) { 10960 uint64_t v; 10961 /* remainder modulo 2^32 */ 10962 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); 10963 v = v << ((e - 1023) - 52 + 32); 10964 ret = v >> 32; 10965 /* take the sign into account */ 10966 if (u.u64 >> 63) 10967 ret = -ret; 10968 } else { 10969 ret = 0; /* also handles NaN and +inf */ 10970 } 10971 } 10972 break; 10973 #ifdef CONFIG_BIGNUM 10974 case JS_TAG_BIG_FLOAT: 10975 { 10976 JSBigFloat *p = JS_VALUE_GET_PTR(val); 10977 bf_get_int32(&ret, &p->num, BF_GET_INT_MOD); 10978 JS_FreeValue(ctx, val); 10979 } 10980 break; 10981 #endif 10982 default: 10983 val = JS_ToNumberFree(ctx, val); 10984 if (JS_IsException(val)) { 10985 *pres = 0; 10986 return -1; 10987 } 10988 goto redo; 10989 } 10990 *pres = ret; 10991 return 0; 10992 } 10993 10994 int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val) 10995 { 10996 return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val)); 10997 } 10998 10999 static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) 11000 { 11001 return JS_ToInt32Free(ctx, (int32_t *)pres, val); 11002 } 11003 11004 static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) 11005 { 11006 uint32_t tag; 11007 int res; 11008 11009 redo: 11010 tag = JS_VALUE_GET_NORM_TAG(val); 11011 switch(tag) { 11012 case JS_TAG_INT: 11013 case JS_TAG_BOOL: 11014 case JS_TAG_NULL: 11015 case JS_TAG_UNDEFINED: 11016 res = JS_VALUE_GET_INT(val); 11017 #ifdef CONFIG_BIGNUM 11018 int_clamp: 11019 #endif 11020 res = max_int(0, min_int(255, res)); 11021 break; 11022 case JS_TAG_FLOAT64: 11023 { 11024 double d = JS_VALUE_GET_FLOAT64(val); 11025 if (isnan(d)) { 11026 res = 0; 11027 } else { 11028 if (d < 0) 11029 res = 0; 11030 else if (d > 255) 11031 res = 255; 11032 else 11033 res = lrint(d); 11034 } 11035 } 11036 break; 11037 #ifdef CONFIG_BIGNUM 11038 case JS_TAG_BIG_FLOAT: 11039 { 11040 JSBigFloat *p = JS_VALUE_GET_PTR(val); 11041 bf_t r_s, *r = &r_s; 11042 bf_init(ctx->bf_ctx, r); 11043 bf_set(r, &p->num); 11044 bf_rint(r, BF_RNDN); 11045 bf_get_int32(&res, r, 0); 11046 bf_delete(r); 11047 JS_FreeValue(ctx, val); 11048 } 11049 goto int_clamp; 11050 #endif 11051 default: 11052 val = JS_ToNumberFree(ctx, val); 11053 if (JS_IsException(val)) { 11054 *pres = 0; 11055 return -1; 11056 } 11057 goto redo; 11058 } 11059 *pres = res; 11060 return 0; 11061 } 11062 11063 static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, 11064 JSValue val, BOOL is_array_ctor) 11065 { 11066 uint32_t tag, len; 11067 11068 tag = JS_VALUE_GET_TAG(val); 11069 switch(tag) { 11070 case JS_TAG_INT: 11071 case JS_TAG_BOOL: 11072 case JS_TAG_NULL: 11073 { 11074 int v; 11075 v = JS_VALUE_GET_INT(val); 11076 if (v < 0) 11077 goto fail; 11078 len = v; 11079 } 11080 break; 11081 case JS_TAG_BIG_INT: 11082 #ifdef CONFIG_BIGNUM 11083 case JS_TAG_BIG_FLOAT: 11084 #endif 11085 { 11086 JSBigFloat *p = JS_VALUE_GET_PTR(val); 11087 bf_t a; 11088 BOOL res; 11089 bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); 11090 bf_init(ctx->bf_ctx, &a); 11091 bf_set_ui(&a, len); 11092 res = bf_cmp_eq(&a, &p->num); 11093 bf_delete(&a); 11094 JS_FreeValue(ctx, val); 11095 if (!res) 11096 goto fail; 11097 } 11098 break; 11099 default: 11100 if (JS_TAG_IS_FLOAT64(tag)) { 11101 double d; 11102 d = JS_VALUE_GET_FLOAT64(val); 11103 if (!(d >= 0 && d <= UINT32_MAX)) 11104 goto fail; 11105 len = (uint32_t)d; 11106 if (len != d) 11107 goto fail; 11108 } else { 11109 uint32_t len1; 11110 11111 if (is_array_ctor) { 11112 val = JS_ToNumberFree(ctx, val); 11113 if (JS_IsException(val)) 11114 return -1; 11115 /* cannot recurse because val is a number */ 11116 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) 11117 return -1; 11118 } else { 11119 /* legacy behavior: must do the conversion twice and compare */ 11120 if (JS_ToUint32(ctx, &len, val)) { 11121 JS_FreeValue(ctx, val); 11122 return -1; 11123 } 11124 val = JS_ToNumberFree(ctx, val); 11125 if (JS_IsException(val)) 11126 return -1; 11127 /* cannot recurse because val is a number */ 11128 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) 11129 return -1; 11130 if (len1 != len) { 11131 fail: 11132 JS_ThrowRangeError(ctx, "invalid array length"); 11133 return -1; 11134 } 11135 } 11136 } 11137 break; 11138 } 11139 *plen = len; 11140 return 0; 11141 } 11142 11143 #define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) 11144 11145 static BOOL is_safe_integer(double d) 11146 { 11147 return isfinite(d) && floor(d) == d && 11148 fabs(d) <= (double)MAX_SAFE_INTEGER; 11149 } 11150 11151 int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) 11152 { 11153 int64_t v; 11154 if (JS_ToInt64Sat(ctx, &v, val)) 11155 return -1; 11156 if (v < 0 || v > MAX_SAFE_INTEGER) { 11157 JS_ThrowRangeError(ctx, "invalid array index"); 11158 *plen = 0; 11159 return -1; 11160 } 11161 *plen = v; 11162 return 0; 11163 } 11164 11165 /* convert a value to a length between 0 and MAX_SAFE_INTEGER. 11166 return -1 for exception */ 11167 static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, 11168 JSValue val) 11169 { 11170 int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); 11171 JS_FreeValue(ctx, val); 11172 return res; 11173 } 11174 11175 /* Note: can return an exception */ 11176 static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val) 11177 { 11178 double d; 11179 if (!JS_IsNumber(val)) 11180 return FALSE; 11181 if (unlikely(JS_ToFloat64(ctx, &d, val))) 11182 return -1; 11183 return isfinite(d) && floor(d) == d; 11184 } 11185 11186 static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) 11187 { 11188 uint32_t tag; 11189 11190 tag = JS_VALUE_GET_NORM_TAG(val); 11191 switch(tag) { 11192 case JS_TAG_INT: 11193 { 11194 int v; 11195 v = JS_VALUE_GET_INT(val); 11196 return (v < 0); 11197 } 11198 case JS_TAG_FLOAT64: 11199 { 11200 JSFloat64Union u; 11201 u.d = JS_VALUE_GET_FLOAT64(val); 11202 return (u.u64 >> 63); 11203 } 11204 case JS_TAG_BIG_INT: 11205 { 11206 JSBigFloat *p = JS_VALUE_GET_PTR(val); 11207 /* Note: integer zeros are not necessarily positive */ 11208 return p->num.sign && !bf_is_zero(&p->num); 11209 } 11210 #ifdef CONFIG_BIGNUM 11211 case JS_TAG_BIG_FLOAT: 11212 { 11213 JSBigFloat *p = JS_VALUE_GET_PTR(val); 11214 return p->num.sign; 11215 } 11216 break; 11217 case JS_TAG_BIG_DECIMAL: 11218 { 11219 JSBigDecimal *p = JS_VALUE_GET_PTR(val); 11220 return p->num.sign; 11221 } 11222 break; 11223 #endif 11224 default: 11225 return FALSE; 11226 } 11227 } 11228 11229 static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) 11230 { 11231 JSValue ret; 11232 bf_t a_s, *a; 11233 char *str; 11234 int saved_sign; 11235 11236 a = JS_ToBigInt(ctx, &a_s, val); 11237 if (!a) 11238 return JS_EXCEPTION; 11239 saved_sign = a->sign; 11240 if (a->expn == BF_EXP_ZERO) 11241 a->sign = 0; 11242 str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | 11243 BF_FTOA_JS_QUIRKS); 11244 a->sign = saved_sign; 11245 JS_FreeBigInt(ctx, a, &a_s); 11246 if (!str) 11247 return JS_ThrowOutOfMemory(ctx); 11248 ret = JS_NewString(ctx, str); 11249 bf_free(ctx->bf_ctx, str); 11250 return ret; 11251 } 11252 11253 static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) 11254 { 11255 return js_bigint_to_string1(ctx, val, 10); 11256 } 11257 11258 #ifdef CONFIG_BIGNUM 11259 11260 static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, 11261 limb_t prec, bf_flags_t flags) 11262 { 11263 JSValue val, ret; 11264 bf_t a_s, *a; 11265 char *str; 11266 int saved_sign; 11267 11268 val = JS_ToNumeric(ctx, val1); 11269 if (JS_IsException(val)) 11270 return val; 11271 a = JS_ToBigFloat(ctx, &a_s, val); 11272 if (!a) { 11273 JS_FreeValue(ctx, val); 11274 return JS_EXCEPTION; 11275 } 11276 saved_sign = a->sign; 11277 if (a->expn == BF_EXP_ZERO) 11278 a->sign = 0; 11279 flags |= BF_FTOA_JS_QUIRKS; 11280 if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) { 11281 /* Note: for floating point numbers with a radix which is not 11282 a power of two, the current precision is used to compute 11283 the number of digits. */ 11284 if ((radix & (radix - 1)) != 0) { 11285 bf_t r_s, *r = &r_s; 11286 int prec, flags1; 11287 /* must round first */ 11288 if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { 11289 prec = ctx->fp_env.prec; 11290 flags1 = ctx->fp_env.flags & 11291 (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)); 11292 } else { 11293 prec = 53; 11294 flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL; 11295 } 11296 bf_init(ctx->bf_ctx, r); 11297 bf_set(r, a); 11298 bf_round(r, prec, flags1 | BF_RNDN); 11299 str = bf_ftoa(NULL, r, radix, prec, flags1 | flags); 11300 bf_delete(r); 11301 } else { 11302 str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags); 11303 } 11304 } else { 11305 str = bf_ftoa(NULL, a, radix, prec, flags); 11306 } 11307 a->sign = saved_sign; 11308 if (a == &a_s) 11309 bf_delete(a); 11310 JS_FreeValue(ctx, val); 11311 if (!str) 11312 return JS_ThrowOutOfMemory(ctx); 11313 ret = JS_NewString(ctx, str); 11314 bf_free(ctx->bf_ctx, str); 11315 return ret; 11316 } 11317 11318 static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val) 11319 { 11320 return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); 11321 } 11322 11323 static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, 11324 limb_t prec, int flags) 11325 { 11326 JSValue ret; 11327 bfdec_t *a; 11328 char *str; 11329 int saved_sign; 11330 11331 a = JS_ToBigDecimal(ctx, val); 11332 if (!a) 11333 return JS_EXCEPTION; 11334 saved_sign = a->sign; 11335 if (a->expn == BF_EXP_ZERO) 11336 a->sign = 0; 11337 str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS); 11338 a->sign = saved_sign; 11339 if (!str) 11340 return JS_ThrowOutOfMemory(ctx); 11341 ret = JS_NewString(ctx, str); 11342 bf_free(ctx->bf_ctx, str); 11343 return ret; 11344 } 11345 11346 static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) 11347 { 11348 return js_bigdecimal_to_string1(ctx, val, 0, 11349 BF_RNDZ | BF_FTOA_FORMAT_FREE); 11350 } 11351 11352 #endif /* CONFIG_BIGNUM */ 11353 11354 /* 2 <= base <= 36 */ 11355 static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; 11356 11357 static char *i64toa(char *buf_end, int64_t n, unsigned int base) 11358 { 11359 char *q = buf_end; 11360 int digit, is_neg; 11361 11362 is_neg = 0; 11363 if (n < 0) { 11364 is_neg = 1; 11365 n = -n; 11366 } 11367 *--q = '\0'; 11368 if (base == 10) { 11369 /* division by known base uses multiplication */ 11370 do { 11371 digit = (uint64_t)n % 10; 11372 n = (uint64_t)n / 10; 11373 *--q = '0' + digit; 11374 } while (n != 0); 11375 } else { 11376 do { 11377 digit = (uint64_t)n % base; 11378 n = (uint64_t)n / base; 11379 *--q = digits[digit]; 11380 } while (n != 0); 11381 } 11382 if (is_neg) 11383 *--q = '-'; 11384 return q; 11385 } 11386 11387 /* buf1 contains the printf result */ 11388 static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf, 11389 int rounding_mode, char *buf1, int buf1_size) 11390 { 11391 if (rounding_mode != FE_TONEAREST) 11392 fesetround(rounding_mode); 11393 snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d); 11394 if (rounding_mode != FE_TONEAREST) 11395 fesetround(FE_TONEAREST); 11396 *sign = (buf1[0] == '-'); 11397 /* mantissa */ 11398 buf[0] = buf1[1]; 11399 if (n_digits > 1) 11400 memcpy(buf + 1, buf1 + 3, n_digits - 1); 11401 buf[n_digits] = '\0'; 11402 /* exponent */ 11403 *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1; 11404 } 11405 11406 /* maximum buffer size for js_dtoa */ 11407 #define JS_DTOA_BUF_SIZE 128 11408 11409 /* needed because ecvt usually limits the number of digits to 11410 17. Return the number of digits. */ 11411 static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, 11412 BOOL is_fixed) 11413 { 11414 int rounding_mode; 11415 char buf_tmp[JS_DTOA_BUF_SIZE]; 11416 11417 if (!is_fixed) { 11418 unsigned int n_digits_min, n_digits_max; 11419 /* find the minimum amount of digits (XXX: inefficient but simple) */ 11420 n_digits_min = 1; 11421 n_digits_max = 17; 11422 while (n_digits_min < n_digits_max) { 11423 n_digits = (n_digits_min + n_digits_max) / 2; 11424 js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, 11425 buf_tmp, sizeof(buf_tmp)); 11426 if (strtod(buf_tmp, NULL) == d) { 11427 /* no need to keep the trailing zeros */ 11428 while (n_digits >= 2 && buf[n_digits - 1] == '0') 11429 n_digits--; 11430 n_digits_max = n_digits; 11431 } else { 11432 n_digits_min = n_digits + 1; 11433 } 11434 } 11435 n_digits = n_digits_max; 11436 rounding_mode = FE_TONEAREST; 11437 } else { 11438 rounding_mode = FE_TONEAREST; 11439 #ifdef CONFIG_PRINTF_RNDN 11440 { 11441 char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE]; 11442 int decpt1, sign1, decpt2, sign2; 11443 /* The JS rounding is specified as round to nearest ties away 11444 from zero (RNDNA), but in printf the "ties" case is not 11445 specified (for example it is RNDN for glibc, RNDNA for 11446 Windows), so we must round manually. */ 11447 js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST, 11448 buf_tmp, sizeof(buf_tmp)); 11449 /* XXX: could use 2 digits to reduce the average running time */ 11450 if (buf1[n_digits] == '5') { 11451 js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD, 11452 buf_tmp, sizeof(buf_tmp)); 11453 js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD, 11454 buf_tmp, sizeof(buf_tmp)); 11455 if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) { 11456 /* exact result: round away from zero */ 11457 if (sign1) 11458 rounding_mode = FE_DOWNWARD; 11459 else 11460 rounding_mode = FE_UPWARD; 11461 } 11462 } 11463 } 11464 #endif /* CONFIG_PRINTF_RNDN */ 11465 } 11466 js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode, 11467 buf_tmp, sizeof(buf_tmp)); 11468 return n_digits; 11469 } 11470 11471 static int js_fcvt1(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits, 11472 int rounding_mode) 11473 { 11474 int n; 11475 if (rounding_mode != FE_TONEAREST) 11476 fesetround(rounding_mode); 11477 n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d); 11478 if (rounding_mode != FE_TONEAREST) 11479 fesetround(FE_TONEAREST); 11480 assert(n < sizeof(*buf)); 11481 return n; 11482 } 11483 11484 static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits) 11485 { 11486 int rounding_mode; 11487 rounding_mode = FE_TONEAREST; 11488 #ifdef CONFIG_PRINTF_RNDN 11489 { 11490 int n1, n2; 11491 char buf1[JS_DTOA_BUF_SIZE]; 11492 char buf2[JS_DTOA_BUF_SIZE]; 11493 11494 /* The JS rounding is specified as round to nearest ties away from 11495 zero (RNDNA), but in printf the "ties" case is not specified 11496 (for example it is RNDN for glibc, RNDNA for Windows), so we 11497 must round manually. */ 11498 n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_TONEAREST); 11499 rounding_mode = FE_TONEAREST; 11500 /* XXX: could use 2 digits to reduce the average running time */ 11501 if (buf1[n1 - 1] == '5') { 11502 n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD); 11503 n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD); 11504 if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { 11505 /* exact result: round away from zero */ 11506 if (buf1[0] == '-') 11507 rounding_mode = FE_DOWNWARD; 11508 else 11509 rounding_mode = FE_UPWARD; 11510 } 11511 } 11512 } 11513 #endif /* CONFIG_PRINTF_RNDN */ 11514 js_fcvt1(buf, d, n_digits, rounding_mode); 11515 } 11516 11517 /* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ 11518 /* use as many digits as necessary */ 11519 #define JS_DTOA_VAR_FORMAT (0 << 0) 11520 /* use n_digits significant digits (1 <= n_digits <= 101) */ 11521 #define JS_DTOA_FIXED_FORMAT (1 << 0) 11522 /* force fractional format: [-]dd.dd with n_digits fractional digits */ 11523 #define JS_DTOA_FRAC_FORMAT (2 << 0) 11524 /* force exponential notation either in fixed or variable format */ 11525 #define JS_DTOA_FORCE_EXP (1 << 2) 11526 11527 /* XXX: slow and maybe not fully correct. Use libbf when it is fast enough. 11528 XXX: radix != 10 is only supported for small integers 11529 */ 11530 static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d, 11531 int radix, int n_digits, int flags) 11532 { 11533 char *q; 11534 11535 if (!isfinite(d)) { 11536 if (isnan(d)) { 11537 pstrcpy(*buf, sizeof(*buf), "NaN"); 11538 } else if (d < 0) { 11539 pstrcpy(*buf, sizeof(*buf), "-Infinity"); 11540 } else { 11541 pstrcpy(*buf, sizeof(*buf), "Infinity"); 11542 } 11543 } else if (flags == JS_DTOA_VAR_FORMAT) { 11544 int64_t i64; 11545 char buf1[70], *ptr; 11546 if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER) 11547 goto generic_conv; 11548 i64 = (int64_t)d; 11549 if (d != i64) 11550 goto generic_conv; 11551 /* fast path for integers */ 11552 ptr = i64toa(buf1 + sizeof(buf1), i64, radix); 11553 pstrcpy(*buf, sizeof(*buf), ptr); 11554 } else { 11555 if (d == 0.0) 11556 d = 0.0; /* convert -0 to 0 */ 11557 if (flags == JS_DTOA_FRAC_FORMAT) { 11558 js_fcvt(buf, d, n_digits); 11559 } else { 11560 char buf1[JS_DTOA_BUF_SIZE]; 11561 int sign, decpt, k, n, i, p, n_max; 11562 BOOL is_fixed; 11563 generic_conv: 11564 is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT); 11565 if (is_fixed) { 11566 n_max = n_digits; 11567 } else { 11568 n_max = 21; 11569 } 11570 /* the number has k digits (k >= 1) */ 11571 k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed); 11572 n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ 11573 q = *buf; 11574 if (sign) 11575 *q++ = '-'; 11576 if (flags & JS_DTOA_FORCE_EXP) 11577 goto force_exp; 11578 if (n >= 1 && n <= n_max) { 11579 if (k <= n) { 11580 memcpy(q, buf1, k); 11581 q += k; 11582 for(i = 0; i < (n - k); i++) 11583 *q++ = '0'; 11584 *q = '\0'; 11585 } else { 11586 /* k > n */ 11587 memcpy(q, buf1, n); 11588 q += n; 11589 *q++ = '.'; 11590 for(i = 0; i < (k - n); i++) 11591 *q++ = buf1[n + i]; 11592 *q = '\0'; 11593 } 11594 } else if (n >= -5 && n <= 0) { 11595 *q++ = '0'; 11596 *q++ = '.'; 11597 for(i = 0; i < -n; i++) 11598 *q++ = '0'; 11599 memcpy(q, buf1, k); 11600 q += k; 11601 *q = '\0'; 11602 } else { 11603 force_exp: 11604 /* exponential notation */ 11605 *q++ = buf1[0]; 11606 if (k > 1) { 11607 *q++ = '.'; 11608 for(i = 1; i < k; i++) 11609 *q++ = buf1[i]; 11610 } 11611 *q++ = 'e'; 11612 p = n - 1; 11613 if (p >= 0) 11614 *q++ = '+'; 11615 snprintf(q, *buf + sizeof(*buf) - q, "%d", p); 11616 } 11617 } 11618 } 11619 } 11620 11621 static JSValue js_dtoa(JSContext *ctx, 11622 double d, int radix, int n_digits, int flags) 11623 { 11624 char buf[JS_DTOA_BUF_SIZE]; 11625 js_dtoa1(&buf, d, radix, n_digits, flags); 11626 return JS_NewString(ctx, buf); 11627 } 11628 11629 static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) 11630 { 11631 char buf[2200], *ptr, *ptr2; 11632 /* d is finite */ 11633 int sign = d < 0; 11634 int digit; 11635 double frac, d0; 11636 int64_t n0 = 0; 11637 d = fabs(d); 11638 d0 = trunc(d); 11639 frac = d - d0; 11640 ptr = buf + 1100; 11641 *ptr = '\0'; 11642 if (d0 <= MAX_SAFE_INTEGER) { 11643 int64_t n = n0 = (int64_t)d0; 11644 while (n >= radix) { 11645 digit = n % radix; 11646 n = n / radix; 11647 *--ptr = digits[digit]; 11648 } 11649 *--ptr = digits[(int)n]; 11650 } else { 11651 /* no decimals */ 11652 while (d0 >= radix) { 11653 digit = fmod(d0, radix); 11654 d0 = trunc(d0 / radix); 11655 if (d0 >= MAX_SAFE_INTEGER) 11656 digit = 0; 11657 *--ptr = digits[digit]; 11658 } 11659 *--ptr = digits[(int)d0]; 11660 goto done; 11661 } 11662 if (frac != 0) { 11663 double log2_radix = log2(radix); 11664 double prec = 1023 + 51; // handle subnormals 11665 ptr2 = buf + 1100; 11666 *ptr2++ = '.'; 11667 while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { 11668 frac *= radix; 11669 digit = trunc(frac); 11670 frac -= digit; 11671 *ptr2++ = digits[digit]; 11672 n0 = n0 * radix + digit; 11673 prec -= log2_radix; 11674 } 11675 *ptr2 = '\0'; 11676 if (frac * radix >= radix / 2) { 11677 char nine = digits[radix - 1]; 11678 // round to closest 11679 while (ptr2[-1] == nine) 11680 *--ptr2 = '\0'; 11681 if (ptr2[-1] == '.') { 11682 *--ptr2 = '\0'; 11683 while (ptr2[-1] == nine) 11684 *--ptr2 = '0'; 11685 } 11686 if (ptr2 - 1 == ptr) 11687 *--ptr = '1'; 11688 else 11689 ptr2[-1] += 1; 11690 } else { 11691 while (ptr2[-1] == '0') 11692 *--ptr2 = '\0'; 11693 if (ptr2[-1] == '.') 11694 *--ptr2 = '\0'; 11695 } 11696 } 11697 done: 11698 ptr[-1] = '-'; 11699 ptr -= sign; 11700 return JS_NewString(ctx, ptr); 11701 } 11702 11703 JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) 11704 { 11705 uint32_t tag; 11706 const char *str; 11707 char buf[32]; 11708 11709 tag = JS_VALUE_GET_NORM_TAG(val); 11710 switch(tag) { 11711 case JS_TAG_STRING: 11712 return JS_DupValue(ctx, val); 11713 case JS_TAG_INT: 11714 snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val)); 11715 str = buf; 11716 goto new_string; 11717 case JS_TAG_BOOL: 11718 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? 11719 JS_ATOM_true : JS_ATOM_false); 11720 case JS_TAG_NULL: 11721 return JS_AtomToString(ctx, JS_ATOM_null); 11722 case JS_TAG_UNDEFINED: 11723 return JS_AtomToString(ctx, JS_ATOM_undefined); 11724 case JS_TAG_EXCEPTION: 11725 return JS_EXCEPTION; 11726 case JS_TAG_OBJECT: 11727 { 11728 JSValue val1, ret; 11729 val1 = JS_ToPrimitive(ctx, val, HINT_STRING); 11730 if (JS_IsException(val1)) 11731 return val1; 11732 ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); 11733 JS_FreeValue(ctx, val1); 11734 return ret; 11735 } 11736 break; 11737 case JS_TAG_FUNCTION_BYTECODE: 11738 str = "[function bytecode]"; 11739 goto new_string; 11740 case JS_TAG_SYMBOL: 11741 if (is_ToPropertyKey) { 11742 return JS_DupValue(ctx, val); 11743 } else { 11744 return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); 11745 } 11746 case JS_TAG_FLOAT64: 11747 return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0, 11748 JS_DTOA_VAR_FORMAT); 11749 case JS_TAG_BIG_INT: 11750 return ctx->rt->bigint_ops.to_string(ctx, val); 11751 #ifdef CONFIG_BIGNUM 11752 case JS_TAG_BIG_FLOAT: 11753 return ctx->rt->bigfloat_ops.to_string(ctx, val); 11754 case JS_TAG_BIG_DECIMAL: 11755 return ctx->rt->bigdecimal_ops.to_string(ctx, val); 11756 #endif 11757 default: 11758 str = "[unsupported type]"; 11759 new_string: 11760 return JS_NewString(ctx, str); 11761 } 11762 } 11763 11764 JSValue JS_ToString(JSContext *ctx, JSValueConst val) 11765 { 11766 return JS_ToStringInternal(ctx, val, FALSE); 11767 } 11768 11769 static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) 11770 { 11771 JSValue ret; 11772 ret = JS_ToString(ctx, val); 11773 JS_FreeValue(ctx, val); 11774 return ret; 11775 } 11776 11777 static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) 11778 { 11779 if (JS_IsUndefined(val) || JS_IsNull(val)) 11780 return JS_ToStringFree(ctx, val); 11781 return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); 11782 } 11783 11784 JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val) 11785 { 11786 return JS_ToStringInternal(ctx, val, TRUE); 11787 } 11788 11789 static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val) 11790 { 11791 uint32_t tag = JS_VALUE_GET_TAG(val); 11792 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) 11793 return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); 11794 return JS_ToString(ctx, val); 11795 } 11796 11797 static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) 11798 { 11799 JSValue val; 11800 JSString *p; 11801 int i; 11802 uint32_t c; 11803 StringBuffer b_s, *b = &b_s; 11804 char buf[16]; 11805 11806 val = JS_ToStringCheckObject(ctx, val1); 11807 if (JS_IsException(val)) 11808 return val; 11809 p = JS_VALUE_GET_STRING(val); 11810 11811 if (string_buffer_init(ctx, b, p->len + 2)) 11812 goto fail; 11813 11814 if (string_buffer_putc8(b, '\"')) 11815 goto fail; 11816 for(i = 0; i < p->len; ) { 11817 c = string_getc(p, &i); 11818 switch(c) { 11819 case '\t': 11820 c = 't'; 11821 goto quote; 11822 case '\r': 11823 c = 'r'; 11824 goto quote; 11825 case '\n': 11826 c = 'n'; 11827 goto quote; 11828 case '\b': 11829 c = 'b'; 11830 goto quote; 11831 case '\f': 11832 c = 'f'; 11833 goto quote; 11834 case '\"': 11835 case '\\': 11836 quote: 11837 if (string_buffer_putc8(b, '\\')) 11838 goto fail; 11839 if (string_buffer_putc8(b, c)) 11840 goto fail; 11841 break; 11842 default: 11843 if (c < 32 || is_surrogate(c)) { 11844 snprintf(buf, sizeof(buf), "\\u%04x", c); 11845 if (string_buffer_puts8(b, buf)) 11846 goto fail; 11847 } else { 11848 if (string_buffer_putc(b, c)) 11849 goto fail; 11850 } 11851 break; 11852 } 11853 } 11854 if (string_buffer_putc8(b, '\"')) 11855 goto fail; 11856 JS_FreeValue(ctx, val); 11857 return string_buffer_end(b); 11858 fail: 11859 JS_FreeValue(ctx, val); 11860 string_buffer_free(b); 11861 return JS_EXCEPTION; 11862 } 11863 11864 static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) 11865 { 11866 printf("%14s %4s %4s %14s %10s %s\n", 11867 "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); 11868 } 11869 11870 /* for debug only: dump an object without side effect */ 11871 static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) 11872 { 11873 uint32_t i; 11874 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 11875 JSShape *sh; 11876 JSShapeProperty *prs; 11877 JSProperty *pr; 11878 BOOL is_first = TRUE; 11879 11880 /* XXX: should encode atoms with special characters */ 11881 sh = p->shape; /* the shape can be NULL while freeing an object */ 11882 printf("%14p %4d ", 11883 (void *)p, 11884 p->header.ref_count); 11885 if (sh) { 11886 printf("%3d%c %14p ", 11887 sh->header.ref_count, 11888 " *"[sh->is_hashed], 11889 (void *)sh->proto); 11890 } else { 11891 printf("%3s %14s ", "-", "-"); 11892 } 11893 printf("%10s ", 11894 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); 11895 if (p->is_exotic && p->fast_array) { 11896 printf("[ "); 11897 for(i = 0; i < p->u.array.count; i++) { 11898 if (i != 0) 11899 printf(", "); 11900 switch (p->class_id) { 11901 case JS_CLASS_ARRAY: 11902 case JS_CLASS_ARGUMENTS: 11903 JS_DumpValueShort(rt, p->u.array.u.values[i]); 11904 break; 11905 case JS_CLASS_UINT8C_ARRAY: 11906 case JS_CLASS_INT8_ARRAY: 11907 case JS_CLASS_UINT8_ARRAY: 11908 case JS_CLASS_INT16_ARRAY: 11909 case JS_CLASS_UINT16_ARRAY: 11910 case JS_CLASS_INT32_ARRAY: 11911 case JS_CLASS_UINT32_ARRAY: 11912 case JS_CLASS_BIG_INT64_ARRAY: 11913 case JS_CLASS_BIG_UINT64_ARRAY: 11914 case JS_CLASS_FLOAT32_ARRAY: 11915 case JS_CLASS_FLOAT64_ARRAY: 11916 { 11917 int size = 1 << typed_array_size_log2(p->class_id); 11918 const uint8_t *b = p->u.array.u.uint8_ptr + i * size; 11919 while (size-- > 0) 11920 printf("%02X", *b++); 11921 } 11922 break; 11923 } 11924 } 11925 printf(" ] "); 11926 } 11927 11928 if (sh) { 11929 printf("{ "); 11930 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 11931 if (prs->atom != JS_ATOM_NULL) { 11932 pr = &p->prop[i]; 11933 if (!is_first) 11934 printf(", "); 11935 printf("%s: ", 11936 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); 11937 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { 11938 printf("[getset %p %p]", (void *)pr->u.getset.getter, 11939 (void *)pr->u.getset.setter); 11940 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { 11941 printf("[varref %p]", (void *)pr->u.var_ref); 11942 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { 11943 printf("[autoinit %p %d %p]", 11944 (void *)js_autoinit_get_realm(pr), 11945 js_autoinit_get_id(pr), 11946 (void *)pr->u.init.opaque); 11947 } else { 11948 JS_DumpValueShort(rt, pr->u.value); 11949 } 11950 is_first = FALSE; 11951 } 11952 } 11953 printf(" }"); 11954 } 11955 11956 if (js_class_has_bytecode(p->class_id)) { 11957 JSFunctionBytecode *b = p->u.func.function_bytecode; 11958 JSVarRef **var_refs; 11959 if (b->closure_var_count) { 11960 var_refs = p->u.func.var_refs; 11961 printf(" Closure:"); 11962 for(i = 0; i < b->closure_var_count; i++) { 11963 printf(" "); 11964 JS_DumpValueShort(rt, var_refs[i]->value); 11965 } 11966 if (p->u.func.home_object) { 11967 printf(" HomeObject: "); 11968 JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); 11969 } 11970 } 11971 } 11972 printf("\n"); 11973 } 11974 11975 static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) 11976 { 11977 if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { 11978 JS_DumpObject(rt, (JSObject *)p); 11979 } else { 11980 printf("%14p %4d ", 11981 (void *)p, 11982 p->ref_count); 11983 switch(p->gc_obj_type) { 11984 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: 11985 printf("[function bytecode]"); 11986 break; 11987 case JS_GC_OBJ_TYPE_SHAPE: 11988 printf("[shape]"); 11989 break; 11990 case JS_GC_OBJ_TYPE_VAR_REF: 11991 printf("[var_ref]"); 11992 break; 11993 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: 11994 printf("[async_function]"); 11995 break; 11996 case JS_GC_OBJ_TYPE_JS_CONTEXT: 11997 printf("[js_context]"); 11998 break; 11999 default: 12000 printf("[unknown %d]", p->gc_obj_type); 12001 break; 12002 } 12003 printf("\n"); 12004 } 12005 } 12006 12007 static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, 12008 JSValueConst val) 12009 { 12010 uint32_t tag = JS_VALUE_GET_NORM_TAG(val); 12011 const char *str; 12012 12013 switch(tag) { 12014 case JS_TAG_INT: 12015 printf("%d", JS_VALUE_GET_INT(val)); 12016 break; 12017 case JS_TAG_BOOL: 12018 if (JS_VALUE_GET_BOOL(val)) 12019 str = "true"; 12020 else 12021 str = "false"; 12022 goto print_str; 12023 case JS_TAG_NULL: 12024 str = "null"; 12025 goto print_str; 12026 case JS_TAG_EXCEPTION: 12027 str = "exception"; 12028 goto print_str; 12029 case JS_TAG_UNINITIALIZED: 12030 str = "uninitialized"; 12031 goto print_str; 12032 case JS_TAG_UNDEFINED: 12033 str = "undefined"; 12034 print_str: 12035 printf("%s", str); 12036 break; 12037 case JS_TAG_FLOAT64: 12038 printf("%.14g", JS_VALUE_GET_FLOAT64(val)); 12039 break; 12040 case JS_TAG_BIG_INT: 12041 { 12042 JSBigFloat *p = JS_VALUE_GET_PTR(val); 12043 char *str; 12044 str = bf_ftoa(NULL, &p->num, 10, 0, 12045 BF_RNDZ | BF_FTOA_FORMAT_FRAC); 12046 printf("%sn", str); 12047 bf_realloc(&rt->bf_ctx, str, 0); 12048 } 12049 break; 12050 #ifdef CONFIG_BIGNUM 12051 case JS_TAG_BIG_FLOAT: 12052 { 12053 JSBigFloat *p = JS_VALUE_GET_PTR(val); 12054 char *str; 12055 str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF, 12056 BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX); 12057 printf("%sl", str); 12058 bf_free(&rt->bf_ctx, str); 12059 } 12060 break; 12061 case JS_TAG_BIG_DECIMAL: 12062 { 12063 JSBigDecimal *p = JS_VALUE_GET_PTR(val); 12064 char *str; 12065 str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF, 12066 BF_RNDZ | BF_FTOA_FORMAT_FREE); 12067 printf("%sm", str); 12068 bf_free(&rt->bf_ctx, str); 12069 } 12070 break; 12071 #endif 12072 case JS_TAG_STRING: 12073 { 12074 JSString *p; 12075 p = JS_VALUE_GET_STRING(val); 12076 JS_DumpString(rt, p); 12077 } 12078 break; 12079 case JS_TAG_FUNCTION_BYTECODE: 12080 { 12081 JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); 12082 char buf[ATOM_GET_STR_BUF_SIZE]; 12083 printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); 12084 } 12085 break; 12086 case JS_TAG_OBJECT: 12087 { 12088 JSObject *p = JS_VALUE_GET_OBJ(val); 12089 JSAtom atom = rt->class_array[p->class_id].class_name; 12090 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 12091 printf("[%s %p]", 12092 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); 12093 } 12094 break; 12095 case JS_TAG_SYMBOL: 12096 { 12097 JSAtomStruct *p = JS_VALUE_GET_PTR(val); 12098 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 12099 printf("Symbol(%s)", 12100 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); 12101 } 12102 break; 12103 case JS_TAG_MODULE: 12104 printf("[module]"); 12105 break; 12106 default: 12107 printf("[unknown tag %d]", tag); 12108 break; 12109 } 12110 } 12111 12112 static __maybe_unused void JS_DumpValue(JSContext *ctx, 12113 JSValueConst val) 12114 { 12115 JS_DumpValueShort(ctx->rt, val); 12116 } 12117 12118 static __maybe_unused void JS_PrintValue(JSContext *ctx, 12119 const char *str, 12120 JSValueConst val) 12121 { 12122 printf("%s=", str); 12123 JS_DumpValueShort(ctx->rt, val); 12124 printf("\n"); 12125 } 12126 12127 /* return -1 if exception (proxy case) or TRUE/FALSE */ 12128 // TODO: should take flags to make proxy resolution and exceptions optional 12129 int JS_IsArray(JSContext *ctx, JSValueConst val) 12130 { 12131 if (js_resolve_proxy(ctx, &val, TRUE)) 12132 return -1; 12133 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { 12134 JSObject *p = JS_VALUE_GET_OBJ(val); 12135 return p->class_id == JS_CLASS_ARRAY; 12136 } else { 12137 return FALSE; 12138 } 12139 } 12140 12141 static double js_pow(double a, double b) 12142 { 12143 if (unlikely(!isfinite(b)) && fabs(a) == 1) { 12144 /* not compatible with IEEE 754 */ 12145 return JS_FLOAT64_NAN; 12146 } else { 12147 return pow(a, b); 12148 } 12149 } 12150 12151 JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) 12152 { 12153 JSValue val; 12154 bf_t *a; 12155 val = JS_NewBigInt(ctx); 12156 if (JS_IsException(val)) 12157 return val; 12158 a = JS_GetBigInt(val); 12159 if (bf_set_si(a, v)) { 12160 JS_FreeValue(ctx, val); 12161 return JS_ThrowOutOfMemory(ctx); 12162 } 12163 return val; 12164 } 12165 12166 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) 12167 { 12168 if (is_math_mode(ctx) && 12169 v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { 12170 return JS_NewInt64(ctx, v); 12171 } else { 12172 return JS_NewBigInt64_1(ctx, v); 12173 } 12174 } 12175 12176 JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) 12177 { 12178 JSValue val; 12179 if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) { 12180 val = JS_NewInt64(ctx, v); 12181 } else { 12182 bf_t *a; 12183 val = JS_NewBigInt(ctx); 12184 if (JS_IsException(val)) 12185 return val; 12186 a = JS_GetBigInt(val); 12187 if (bf_set_ui(a, v)) { 12188 JS_FreeValue(ctx, val); 12189 return JS_ThrowOutOfMemory(ctx); 12190 } 12191 } 12192 return val; 12193 } 12194 12195 /* return NaN if bad bigint literal */ 12196 static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) 12197 { 12198 const char *str, *p; 12199 size_t len; 12200 int flags; 12201 12202 str = JS_ToCStringLen(ctx, &len, val); 12203 JS_FreeValue(ctx, val); 12204 if (!str) 12205 return JS_EXCEPTION; 12206 p = str; 12207 p += skip_spaces(p); 12208 if ((p - str) == len) { 12209 val = JS_NewBigInt64(ctx, 0); 12210 } else { 12211 flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; 12212 #ifdef CONFIG_BIGNUM 12213 if (is_math_mode(ctx)) 12214 flags |= ATOD_MODE_BIGINT; 12215 #endif 12216 val = js_atof(ctx, p, &p, 0, flags); 12217 p += skip_spaces(p); 12218 if (!JS_IsException(val)) { 12219 if ((p - str) != len) { 12220 JS_FreeValue(ctx, val); 12221 val = JS_NAN; 12222 } 12223 } 12224 } 12225 JS_FreeCString(ctx, str); 12226 return val; 12227 } 12228 12229 static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) 12230 { 12231 val = JS_StringToBigInt(ctx, val); 12232 if (JS_VALUE_IS_NAN(val)) 12233 return JS_ThrowSyntaxError(ctx, "invalid bigint literal"); 12234 return val; 12235 } 12236 12237 /* if the returned bigfloat is allocated it is equal to 12238 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */ 12239 static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) 12240 { 12241 uint32_t tag; 12242 bf_t *r; 12243 JSBigFloat *p; 12244 12245 redo: 12246 tag = JS_VALUE_GET_NORM_TAG(val); 12247 switch(tag) { 12248 case JS_TAG_INT: 12249 case JS_TAG_NULL: 12250 case JS_TAG_UNDEFINED: 12251 if (!is_math_mode(ctx)) 12252 goto fail; 12253 /* fall tru */ 12254 case JS_TAG_BOOL: 12255 r = buf; 12256 bf_init(ctx->bf_ctx, r); 12257 bf_set_si(r, JS_VALUE_GET_INT(val)); 12258 break; 12259 case JS_TAG_FLOAT64: 12260 { 12261 double d = JS_VALUE_GET_FLOAT64(val); 12262 if (!is_math_mode(ctx)) 12263 goto fail; 12264 if (!isfinite(d)) 12265 goto fail; 12266 r = buf; 12267 bf_init(ctx->bf_ctx, r); 12268 d = trunc(d); 12269 bf_set_float64(r, d); 12270 } 12271 break; 12272 case JS_TAG_BIG_INT: 12273 p = JS_VALUE_GET_PTR(val); 12274 r = &p->num; 12275 break; 12276 #ifdef CONFIG_BIGNUM 12277 case JS_TAG_BIG_FLOAT: 12278 if (!is_math_mode(ctx)) 12279 goto fail; 12280 p = JS_VALUE_GET_PTR(val); 12281 if (!bf_is_finite(&p->num)) 12282 goto fail; 12283 r = buf; 12284 bf_init(ctx->bf_ctx, r); 12285 bf_set(r, &p->num); 12286 bf_rint(r, BF_RNDZ); 12287 JS_FreeValue(ctx, val); 12288 break; 12289 #endif 12290 case JS_TAG_STRING: 12291 val = JS_StringToBigIntErr(ctx, val); 12292 if (JS_IsException(val)) 12293 return NULL; 12294 goto redo; 12295 case JS_TAG_OBJECT: 12296 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 12297 if (JS_IsException(val)) 12298 return NULL; 12299 goto redo; 12300 default: 12301 fail: 12302 JS_FreeValue(ctx, val); 12303 JS_ThrowTypeError(ctx, "cannot convert to bigint"); 12304 return NULL; 12305 } 12306 return r; 12307 } 12308 12309 static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val) 12310 { 12311 return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val)); 12312 } 12313 12314 static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) 12315 { 12316 if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { 12317 return val; 12318 } else { 12319 bf_t a_s, *a, *r; 12320 int ret; 12321 JSValue res; 12322 12323 res = JS_NewBigInt(ctx); 12324 if (JS_IsException(res)) 12325 return JS_EXCEPTION; 12326 a = JS_ToBigIntFree(ctx, &a_s, val); 12327 if (!a) { 12328 JS_FreeValue(ctx, res); 12329 return JS_EXCEPTION; 12330 } 12331 r = JS_GetBigInt(res); 12332 ret = bf_set(r, a); 12333 JS_FreeBigInt(ctx, a, &a_s); 12334 if (ret) { 12335 JS_FreeValue(ctx, res); 12336 return JS_ThrowOutOfMemory(ctx); 12337 } 12338 return JS_CompactBigInt(ctx, res); 12339 } 12340 } 12341 12342 /* free the bf_t allocated by JS_ToBigInt */ 12343 static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) 12344 { 12345 if (a == buf) { 12346 bf_delete(a); 12347 } else { 12348 JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - 12349 offsetof(JSBigFloat, num)); 12350 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); 12351 } 12352 } 12353 12354 /* XXX: merge with JS_ToInt64Free with a specific flag */ 12355 static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) 12356 { 12357 bf_t a_s, *a; 12358 12359 a = JS_ToBigIntFree(ctx, &a_s, val); 12360 if (!a) { 12361 *pres = 0; 12362 return -1; 12363 } 12364 bf_get_int64(pres, a, BF_GET_INT_MOD); 12365 JS_FreeBigInt(ctx, a, &a_s); 12366 return 0; 12367 } 12368 12369 int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) 12370 { 12371 return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val)); 12372 } 12373 12374 static JSBigFloat *js_new_bf(JSContext *ctx) 12375 { 12376 JSBigFloat *p; 12377 p = js_malloc(ctx, sizeof(*p)); 12378 if (!p) 12379 return NULL; 12380 p->header.ref_count = 1; 12381 bf_init(ctx->bf_ctx, &p->num); 12382 return p; 12383 } 12384 12385 static JSValue JS_NewBigInt(JSContext *ctx) 12386 { 12387 JSBigFloat *p; 12388 p = js_malloc(ctx, sizeof(*p)); 12389 if (!p) 12390 return JS_EXCEPTION; 12391 p->header.ref_count = 1; 12392 bf_init(ctx->bf_ctx, &p->num); 12393 return JS_MKPTR(JS_TAG_BIG_INT, p); 12394 } 12395 12396 static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val, 12397 BOOL convert_to_safe_integer) 12398 { 12399 int64_t v; 12400 bf_t *a; 12401 12402 if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) 12403 return val; /* fail safe */ 12404 a = JS_GetBigInt(val); 12405 if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 && 12406 v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) { 12407 JS_FreeValue(ctx, val); 12408 return JS_NewInt64(ctx, v); 12409 } else if (a->expn == BF_EXP_ZERO && a->sign) { 12410 JSBigFloat *p = JS_VALUE_GET_PTR(val); 12411 assert(p->header.ref_count == 1); 12412 a->sign = 0; 12413 } 12414 return val; 12415 } 12416 12417 /* Convert the big int to a safe integer if in math mode. normalize 12418 the zero representation. Could also be used to convert the bigint 12419 to a short bigint value. The reference count of the value must be 12420 1. Cannot fail */ 12421 static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) 12422 { 12423 return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); 12424 } 12425 12426 static JSValue throw_bf_exception(JSContext *ctx, int status) 12427 { 12428 const char *str; 12429 if (status & BF_ST_MEM_ERROR) 12430 return JS_ThrowOutOfMemory(ctx); 12431 if (status & BF_ST_DIVIDE_ZERO) { 12432 str = "division by zero"; 12433 } else if (status & BF_ST_INVALID_OP) { 12434 str = "invalid operation"; 12435 } else { 12436 str = "integer overflow"; 12437 } 12438 return JS_ThrowRangeError(ctx, "%s", str); 12439 } 12440 12441 /* if the returned bigfloat is allocated it is equal to 12442 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return 12443 NULL in case of error. */ 12444 static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val) 12445 { 12446 uint32_t tag; 12447 bf_t *r; 12448 JSBigFloat *p; 12449 12450 tag = JS_VALUE_GET_NORM_TAG(val); 12451 switch(tag) { 12452 case JS_TAG_INT: 12453 case JS_TAG_BOOL: 12454 case JS_TAG_NULL: 12455 r = buf; 12456 bf_init(ctx->bf_ctx, r); 12457 if (bf_set_si(r, JS_VALUE_GET_INT(val))) 12458 goto fail; 12459 break; 12460 case JS_TAG_FLOAT64: 12461 r = buf; 12462 bf_init(ctx->bf_ctx, r); 12463 if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { 12464 fail: 12465 bf_delete(r); 12466 return NULL; 12467 } 12468 break; 12469 case JS_TAG_BIG_INT: 12470 #ifdef CONFIG_BIGNUM 12471 case JS_TAG_BIG_FLOAT: 12472 #endif 12473 p = JS_VALUE_GET_PTR(val); 12474 r = &p->num; 12475 break; 12476 case JS_TAG_UNDEFINED: 12477 default: 12478 r = buf; 12479 bf_init(ctx->bf_ctx, r); 12480 bf_set_nan(r); 12481 break; 12482 } 12483 return r; 12484 } 12485 12486 #ifdef CONFIG_BIGNUM 12487 /* return NULL if invalid type */ 12488 static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val) 12489 { 12490 uint32_t tag; 12491 JSBigDecimal *p; 12492 bfdec_t *r; 12493 12494 tag = JS_VALUE_GET_NORM_TAG(val); 12495 switch(tag) { 12496 case JS_TAG_BIG_DECIMAL: 12497 p = JS_VALUE_GET_PTR(val); 12498 r = &p->num; 12499 break; 12500 default: 12501 JS_ThrowTypeError(ctx, "bigdecimal expected"); 12502 r = NULL; 12503 break; 12504 } 12505 return r; 12506 } 12507 12508 static JSValue JS_NewBigFloat(JSContext *ctx) 12509 { 12510 JSBigFloat *p; 12511 p = js_malloc(ctx, sizeof(*p)); 12512 if (!p) 12513 return JS_EXCEPTION; 12514 p->header.ref_count = 1; 12515 bf_init(ctx->bf_ctx, &p->num); 12516 return JS_MKPTR(JS_TAG_BIG_FLOAT, p); 12517 } 12518 12519 static JSValue JS_NewBigDecimal(JSContext *ctx) 12520 { 12521 JSBigDecimal *p; 12522 p = js_malloc(ctx, sizeof(*p)); 12523 if (!p) 12524 return JS_EXCEPTION; 12525 p->header.ref_count = 1; 12526 bfdec_init(ctx->bf_ctx, &p->num); 12527 return JS_MKPTR(JS_TAG_BIG_DECIMAL, p); 12528 } 12529 12530 /* must be kept in sync with JSOverloadableOperatorEnum */ 12531 /* XXX: use atoms ? */ 12532 static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { 12533 "+", 12534 "-", 12535 "*", 12536 "/", 12537 "%", 12538 "**", 12539 "|", 12540 "&", 12541 "^", 12542 "<<", 12543 ">>", 12544 ">>>", 12545 "==", 12546 "<", 12547 "pos", 12548 "neg", 12549 "++", 12550 "--", 12551 "~", 12552 }; 12553 12554 static int get_ovop_from_opcode(OPCodeEnum op) 12555 { 12556 switch(op) { 12557 case OP_add: 12558 return JS_OVOP_ADD; 12559 case OP_sub: 12560 return JS_OVOP_SUB; 12561 case OP_mul: 12562 return JS_OVOP_MUL; 12563 case OP_div: 12564 return JS_OVOP_DIV; 12565 case OP_mod: 12566 case OP_math_mod: 12567 return JS_OVOP_MOD; 12568 case OP_pow: 12569 return JS_OVOP_POW; 12570 case OP_or: 12571 return JS_OVOP_OR; 12572 case OP_and: 12573 return JS_OVOP_AND; 12574 case OP_xor: 12575 return JS_OVOP_XOR; 12576 case OP_shl: 12577 return JS_OVOP_SHL; 12578 case OP_sar: 12579 return JS_OVOP_SAR; 12580 case OP_shr: 12581 return JS_OVOP_SHR; 12582 case OP_eq: 12583 case OP_neq: 12584 return JS_OVOP_EQ; 12585 case OP_lt: 12586 case OP_lte: 12587 case OP_gt: 12588 case OP_gte: 12589 return JS_OVOP_LESS; 12590 case OP_plus: 12591 return JS_OVOP_POS; 12592 case OP_neg: 12593 return JS_OVOP_NEG; 12594 case OP_inc: 12595 return JS_OVOP_INC; 12596 case OP_dec: 12597 return JS_OVOP_DEC; 12598 default: 12599 abort(); 12600 } 12601 } 12602 12603 /* return NULL if not present */ 12604 static JSObject *find_binary_op(JSBinaryOperatorDef *def, 12605 uint32_t operator_index, 12606 JSOverloadableOperatorEnum op) 12607 { 12608 JSBinaryOperatorDefEntry *ent; 12609 int i; 12610 for(i = 0; i < def->count; i++) { 12611 ent = &def->tab[i]; 12612 if (ent->operator_index == operator_index) 12613 return ent->ops[op]; 12614 } 12615 return NULL; 12616 } 12617 12618 /* return -1 if exception, 0 if no operator overloading, 1 if 12619 overloaded operator called */ 12620 static __exception int js_call_binary_op_fallback(JSContext *ctx, 12621 JSValue *pret, 12622 JSValueConst op1, 12623 JSValueConst op2, 12624 OPCodeEnum op, 12625 BOOL is_numeric, 12626 int hint) 12627 { 12628 JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2; 12629 JSOperatorSetData *opset1, *opset2; 12630 JSOverloadableOperatorEnum ovop; 12631 JSObject *p; 12632 JSValueConst args[2]; 12633 12634 if (!ctx->allow_operator_overloading) 12635 return 0; 12636 12637 opset2_obj = JS_UNDEFINED; 12638 opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); 12639 if (JS_IsException(opset1_obj)) 12640 goto exception; 12641 if (JS_IsUndefined(opset1_obj)) 12642 return 0; 12643 opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); 12644 if (!opset1) 12645 goto exception; 12646 12647 opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet); 12648 if (JS_IsException(opset2_obj)) 12649 goto exception; 12650 if (JS_IsUndefined(opset2_obj)) { 12651 JS_FreeValue(ctx, opset1_obj); 12652 return 0; 12653 } 12654 opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET); 12655 if (!opset2) 12656 goto exception; 12657 12658 if (opset1->is_primitive && opset2->is_primitive) { 12659 JS_FreeValue(ctx, opset1_obj); 12660 JS_FreeValue(ctx, opset2_obj); 12661 return 0; 12662 } 12663 12664 ovop = get_ovop_from_opcode(op); 12665 12666 if (opset1->operator_counter == opset2->operator_counter) { 12667 p = opset1->self_ops[ovop]; 12668 } else if (opset1->operator_counter > opset2->operator_counter) { 12669 p = find_binary_op(&opset1->left, opset2->operator_counter, ovop); 12670 } else { 12671 p = find_binary_op(&opset2->right, opset1->operator_counter, ovop); 12672 } 12673 if (!p) { 12674 JS_ThrowTypeError(ctx, "operator %s: no function defined", 12675 js_overloadable_operator_names[ovop]); 12676 goto exception; 12677 } 12678 12679 if (opset1->is_primitive) { 12680 if (is_numeric) { 12681 new_op1 = JS_ToNumeric(ctx, op1); 12682 } else { 12683 new_op1 = JS_ToPrimitive(ctx, op1, hint); 12684 } 12685 if (JS_IsException(new_op1)) 12686 goto exception; 12687 } else { 12688 new_op1 = JS_DupValue(ctx, op1); 12689 } 12690 12691 if (opset2->is_primitive) { 12692 if (is_numeric) { 12693 new_op2 = JS_ToNumeric(ctx, op2); 12694 } else { 12695 new_op2 = JS_ToPrimitive(ctx, op2, hint); 12696 } 12697 if (JS_IsException(new_op2)) { 12698 JS_FreeValue(ctx, new_op1); 12699 goto exception; 12700 } 12701 } else { 12702 new_op2 = JS_DupValue(ctx, op2); 12703 } 12704 12705 /* XXX: could apply JS_ToPrimitive() if primitive type so that the 12706 operator function does not get a value object */ 12707 12708 method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 12709 if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) { 12710 args[0] = new_op2; 12711 args[1] = new_op1; 12712 } else { 12713 args[0] = new_op1; 12714 args[1] = new_op2; 12715 } 12716 ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); 12717 JS_FreeValue(ctx, new_op1); 12718 JS_FreeValue(ctx, new_op2); 12719 if (JS_IsException(ret)) 12720 goto exception; 12721 if (ovop == JS_OVOP_EQ) { 12722 BOOL res = JS_ToBoolFree(ctx, ret); 12723 if (op == OP_neq) 12724 res ^= 1; 12725 ret = JS_NewBool(ctx, res); 12726 } else if (ovop == JS_OVOP_LESS) { 12727 if (JS_IsUndefined(ret)) { 12728 ret = JS_FALSE; 12729 } else { 12730 BOOL res = JS_ToBoolFree(ctx, ret); 12731 if (op == OP_lte || op == OP_gte) 12732 res ^= 1; 12733 ret = JS_NewBool(ctx, res); 12734 } 12735 } 12736 JS_FreeValue(ctx, opset1_obj); 12737 JS_FreeValue(ctx, opset2_obj); 12738 *pret = ret; 12739 return 1; 12740 exception: 12741 JS_FreeValue(ctx, opset1_obj); 12742 JS_FreeValue(ctx, opset2_obj); 12743 *pret = JS_UNDEFINED; 12744 return -1; 12745 } 12746 12747 /* try to call the operation on the operatorSet field of 'obj'. Only 12748 used for "/" and "**" on the BigInt prototype in math mode */ 12749 static __exception int js_call_binary_op_simple(JSContext *ctx, 12750 JSValue *pret, 12751 JSValueConst obj, 12752 JSValueConst op1, 12753 JSValueConst op2, 12754 OPCodeEnum op) 12755 { 12756 JSValue opset1_obj, method, ret, new_op1, new_op2; 12757 JSOperatorSetData *opset1; 12758 JSOverloadableOperatorEnum ovop; 12759 JSObject *p; 12760 JSValueConst args[2]; 12761 12762 opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); 12763 if (JS_IsException(opset1_obj)) 12764 goto exception; 12765 if (JS_IsUndefined(opset1_obj)) 12766 return 0; 12767 opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); 12768 if (!opset1) 12769 goto exception; 12770 ovop = get_ovop_from_opcode(op); 12771 12772 p = opset1->self_ops[ovop]; 12773 if (!p) { 12774 JS_FreeValue(ctx, opset1_obj); 12775 return 0; 12776 } 12777 12778 new_op1 = JS_ToNumeric(ctx, op1); 12779 if (JS_IsException(new_op1)) 12780 goto exception; 12781 new_op2 = JS_ToNumeric(ctx, op2); 12782 if (JS_IsException(new_op2)) { 12783 JS_FreeValue(ctx, new_op1); 12784 goto exception; 12785 } 12786 12787 method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 12788 args[0] = new_op1; 12789 args[1] = new_op2; 12790 ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args); 12791 JS_FreeValue(ctx, new_op1); 12792 JS_FreeValue(ctx, new_op2); 12793 if (JS_IsException(ret)) 12794 goto exception; 12795 JS_FreeValue(ctx, opset1_obj); 12796 *pret = ret; 12797 return 1; 12798 exception: 12799 JS_FreeValue(ctx, opset1_obj); 12800 *pret = JS_UNDEFINED; 12801 return -1; 12802 } 12803 12804 /* return -1 if exception, 0 if no operator overloading, 1 if 12805 overloaded operator called */ 12806 static __exception int js_call_unary_op_fallback(JSContext *ctx, 12807 JSValue *pret, 12808 JSValueConst op1, 12809 OPCodeEnum op) 12810 { 12811 JSValue opset1_obj, method, ret; 12812 JSOperatorSetData *opset1; 12813 JSOverloadableOperatorEnum ovop; 12814 JSObject *p; 12815 12816 if (!ctx->allow_operator_overloading) 12817 return 0; 12818 12819 opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet); 12820 if (JS_IsException(opset1_obj)) 12821 goto exception; 12822 if (JS_IsUndefined(opset1_obj)) 12823 return 0; 12824 opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET); 12825 if (!opset1) 12826 goto exception; 12827 if (opset1->is_primitive) { 12828 JS_FreeValue(ctx, opset1_obj); 12829 return 0; 12830 } 12831 12832 ovop = get_ovop_from_opcode(op); 12833 12834 p = opset1->self_ops[ovop]; 12835 if (!p) { 12836 JS_ThrowTypeError(ctx, "no overloaded operator %s", 12837 js_overloadable_operator_names[ovop]); 12838 goto exception; 12839 } 12840 method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); 12841 ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1); 12842 if (JS_IsException(ret)) 12843 goto exception; 12844 JS_FreeValue(ctx, opset1_obj); 12845 *pret = ret; 12846 return 1; 12847 exception: 12848 JS_FreeValue(ctx, opset1_obj); 12849 *pret = JS_UNDEFINED; 12850 return -1; 12851 } 12852 12853 static int js_unary_arith_bigfloat(JSContext *ctx, 12854 JSValue *pres, OPCodeEnum op, JSValue op1) 12855 { 12856 bf_t a_s, *r, *a; 12857 int ret, v; 12858 JSValue res; 12859 12860 if (op == OP_plus && !is_math_mode(ctx)) { 12861 JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); 12862 JS_FreeValue(ctx, op1); 12863 return -1; 12864 } 12865 12866 res = JS_NewBigFloat(ctx); 12867 if (JS_IsException(res)) { 12868 JS_FreeValue(ctx, op1); 12869 return -1; 12870 } 12871 r = JS_GetBigFloat(res); 12872 a = JS_ToBigFloat(ctx, &a_s, op1); 12873 if (!a) { 12874 JS_FreeValue(ctx, res); 12875 JS_FreeValue(ctx, op1); 12876 return -1; 12877 } 12878 ret = 0; 12879 switch(op) { 12880 case OP_inc: 12881 case OP_dec: 12882 v = 2 * (op - OP_dec) - 1; 12883 ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags); 12884 break; 12885 case OP_plus: 12886 ret = bf_set(r, a); 12887 break; 12888 case OP_neg: 12889 ret = bf_set(r, a); 12890 bf_neg(r); 12891 break; 12892 default: 12893 abort(); 12894 } 12895 if (a == &a_s) 12896 bf_delete(a); 12897 JS_FreeValue(ctx, op1); 12898 if (unlikely(ret & BF_ST_MEM_ERROR)) { 12899 JS_FreeValue(ctx, res); 12900 throw_bf_exception(ctx, ret); 12901 return -1; 12902 } 12903 *pres = res; 12904 return 0; 12905 } 12906 12907 static int js_unary_arith_bigdecimal(JSContext *ctx, 12908 JSValue *pres, OPCodeEnum op, JSValue op1) 12909 { 12910 bfdec_t *r, *a; 12911 int ret, v; 12912 JSValue res; 12913 12914 if (op == OP_plus && !is_math_mode(ctx)) { 12915 JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); 12916 JS_FreeValue(ctx, op1); 12917 return -1; 12918 } 12919 12920 res = JS_NewBigDecimal(ctx); 12921 if (JS_IsException(res)) { 12922 JS_FreeValue(ctx, op1); 12923 return -1; 12924 } 12925 r = JS_GetBigDecimal(res); 12926 a = JS_ToBigDecimal(ctx, op1); 12927 if (!a) { 12928 JS_FreeValue(ctx, res); 12929 JS_FreeValue(ctx, op1); 12930 return -1; 12931 } 12932 ret = 0; 12933 switch(op) { 12934 case OP_inc: 12935 case OP_dec: 12936 v = 2 * (op - OP_dec) - 1; 12937 ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); 12938 break; 12939 case OP_plus: 12940 ret = bfdec_set(r, a); 12941 break; 12942 case OP_neg: 12943 ret = bfdec_set(r, a); 12944 bfdec_neg(r); 12945 break; 12946 default: 12947 abort(); 12948 } 12949 JS_FreeValue(ctx, op1); 12950 if (unlikely(ret)) { 12951 JS_FreeValue(ctx, res); 12952 throw_bf_exception(ctx, ret); 12953 return -1; 12954 } 12955 *pres = res; 12956 return 0; 12957 } 12958 12959 #endif /* CONFIG_BIGNUM */ 12960 12961 static int js_unary_arith_bigint(JSContext *ctx, 12962 JSValue *pres, OPCodeEnum op, JSValue op1) 12963 { 12964 bf_t a_s, *r, *a; 12965 int ret, v; 12966 JSValue res; 12967 12968 if (op == OP_plus && !is_math_mode(ctx)) { 12969 JS_ThrowTypeError(ctx, "bigint argument with unary +"); 12970 JS_FreeValue(ctx, op1); 12971 return -1; 12972 } 12973 res = JS_NewBigInt(ctx); 12974 if (JS_IsException(res)) { 12975 JS_FreeValue(ctx, op1); 12976 return -1; 12977 } 12978 r = JS_GetBigInt(res); 12979 a = JS_ToBigInt(ctx, &a_s, op1); 12980 if (!a) { 12981 JS_FreeValue(ctx, res); 12982 JS_FreeValue(ctx, op1); 12983 return -1; 12984 } 12985 ret = 0; 12986 switch(op) { 12987 case OP_inc: 12988 case OP_dec: 12989 v = 2 * (op - OP_dec) - 1; 12990 ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); 12991 break; 12992 case OP_plus: 12993 ret = bf_set(r, a); 12994 break; 12995 case OP_neg: 12996 ret = bf_set(r, a); 12997 bf_neg(r); 12998 break; 12999 case OP_not: 13000 ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); 13001 bf_neg(r); 13002 break; 13003 default: 13004 abort(); 13005 } 13006 JS_FreeBigInt(ctx, a, &a_s); 13007 JS_FreeValue(ctx, op1); 13008 if (unlikely(ret)) { 13009 JS_FreeValue(ctx, res); 13010 throw_bf_exception(ctx, ret); 13011 return -1; 13012 } 13013 res = JS_CompactBigInt(ctx, res); 13014 *pres = res; 13015 return 0; 13016 } 13017 13018 static no_inline __exception int js_unary_arith_slow(JSContext *ctx, 13019 JSValue *sp, 13020 OPCodeEnum op) 13021 { 13022 JSValue op1; 13023 int v; 13024 uint32_t tag; 13025 13026 op1 = sp[-1]; 13027 /* fast path for float64 */ 13028 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) 13029 goto handle_float64; 13030 #ifdef CONFIG_BIGNUM 13031 if (JS_IsObject(op1)) { 13032 JSValue val; 13033 int ret = js_call_unary_op_fallback(ctx, &val, op1, op); 13034 if (ret < 0) 13035 return -1; 13036 if (ret) { 13037 JS_FreeValue(ctx, op1); 13038 sp[-1] = val; 13039 return 0; 13040 } 13041 } 13042 #endif 13043 op1 = JS_ToNumericFree(ctx, op1); 13044 if (JS_IsException(op1)) 13045 goto exception; 13046 tag = JS_VALUE_GET_TAG(op1); 13047 switch(tag) { 13048 case JS_TAG_INT: 13049 { 13050 int64_t v64; 13051 v64 = JS_VALUE_GET_INT(op1); 13052 switch(op) { 13053 case OP_inc: 13054 case OP_dec: 13055 v = 2 * (op - OP_dec) - 1; 13056 v64 += v; 13057 break; 13058 case OP_plus: 13059 break; 13060 case OP_neg: 13061 if (v64 == 0) { 13062 sp[-1] = __JS_NewFloat64(ctx, -0.0); 13063 return 0; 13064 } else { 13065 v64 = -v64; 13066 } 13067 break; 13068 default: 13069 abort(); 13070 } 13071 sp[-1] = JS_NewInt64(ctx, v64); 13072 } 13073 break; 13074 case JS_TAG_BIG_INT: 13075 handle_bigint: 13076 if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) 13077 goto exception; 13078 break; 13079 #ifdef CONFIG_BIGNUM 13080 case JS_TAG_BIG_FLOAT: 13081 if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) 13082 goto exception; 13083 break; 13084 case JS_TAG_BIG_DECIMAL: 13085 if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) 13086 goto exception; 13087 break; 13088 #endif 13089 default: 13090 handle_float64: 13091 { 13092 double d; 13093 if (is_math_mode(ctx)) 13094 goto handle_bigint; 13095 d = JS_VALUE_GET_FLOAT64(op1); 13096 switch(op) { 13097 case OP_inc: 13098 case OP_dec: 13099 v = 2 * (op - OP_dec) - 1; 13100 d += v; 13101 break; 13102 case OP_plus: 13103 break; 13104 case OP_neg: 13105 d = -d; 13106 break; 13107 default: 13108 abort(); 13109 } 13110 sp[-1] = __JS_NewFloat64(ctx, d); 13111 } 13112 break; 13113 } 13114 return 0; 13115 exception: 13116 sp[-1] = JS_UNDEFINED; 13117 return -1; 13118 } 13119 13120 static __exception int js_post_inc_slow(JSContext *ctx, 13121 JSValue *sp, OPCodeEnum op) 13122 { 13123 JSValue op1; 13124 13125 /* XXX: allow custom operators */ 13126 op1 = sp[-1]; 13127 op1 = JS_ToNumericFree(ctx, op1); 13128 if (JS_IsException(op1)) { 13129 sp[-1] = JS_UNDEFINED; 13130 return -1; 13131 } 13132 sp[-1] = op1; 13133 sp[0] = JS_DupValue(ctx, op1); 13134 return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); 13135 } 13136 13137 static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) 13138 { 13139 JSValue op1; 13140 13141 op1 = sp[-1]; 13142 #ifdef CONFIG_BIGNUM 13143 if (JS_IsObject(op1)) { 13144 JSValue val; 13145 int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); 13146 if (ret < 0) 13147 return -1; 13148 if (ret) { 13149 JS_FreeValue(ctx, op1); 13150 sp[-1] = val; 13151 return 0; 13152 } 13153 } 13154 #endif 13155 op1 = JS_ToNumericFree(ctx, op1); 13156 if (JS_IsException(op1)) 13157 goto exception; 13158 if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { 13159 if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1)) 13160 goto exception; 13161 } else { 13162 int32_t v1; 13163 if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) 13164 goto exception; 13165 sp[-1] = JS_NewInt32(ctx, ~v1); 13166 } 13167 return 0; 13168 exception: 13169 sp[-1] = JS_UNDEFINED; 13170 return -1; 13171 } 13172 13173 static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, 13174 JSValue *pres, JSValue op1, JSValue op2) 13175 { 13176 bf_t a_s, b_s, *r, *a, *b; 13177 int ret; 13178 JSValue res; 13179 13180 res = JS_NewBigInt(ctx); 13181 if (JS_IsException(res)) 13182 goto fail; 13183 a = JS_ToBigInt(ctx, &a_s, op1); 13184 if (!a) 13185 goto fail; 13186 b = JS_ToBigInt(ctx, &b_s, op2); 13187 if (!b) { 13188 JS_FreeBigInt(ctx, a, &a_s); 13189 goto fail; 13190 } 13191 r = JS_GetBigInt(res); 13192 ret = 0; 13193 switch(op) { 13194 case OP_add: 13195 ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); 13196 break; 13197 case OP_sub: 13198 ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); 13199 break; 13200 case OP_mul: 13201 ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); 13202 break; 13203 case OP_div: 13204 if (!is_math_mode(ctx)) { 13205 bf_t rem_s, *rem = &rem_s; 13206 bf_init(ctx->bf_ctx, rem); 13207 ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, 13208 BF_RNDZ); 13209 bf_delete(rem); 13210 } else { 13211 goto math_mode_div_pow; 13212 } 13213 break; 13214 #ifdef CONFIG_BIGNUM 13215 case OP_math_mod: 13216 /* Euclidian remainder */ 13217 ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, 13218 BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP; 13219 break; 13220 #endif 13221 case OP_mod: 13222 ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, 13223 BF_RNDZ) & BF_ST_INVALID_OP; 13224 break; 13225 case OP_pow: 13226 if (b->sign) { 13227 if (!is_math_mode(ctx)) { 13228 ret = BF_ST_INVALID_OP; 13229 } else { 13230 math_mode_div_pow: 13231 #ifdef CONFIG_BIGNUM 13232 JS_FreeValue(ctx, res); 13233 ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op); 13234 if (ret != 0) { 13235 JS_FreeBigInt(ctx, a, &a_s); 13236 JS_FreeBigInt(ctx, b, &b_s); 13237 JS_FreeValue(ctx, op1); 13238 JS_FreeValue(ctx, op2); 13239 if (ret < 0) { 13240 return -1; 13241 } else { 13242 *pres = res; 13243 return 0; 13244 } 13245 } 13246 /* if no BigInt power operator defined, return a 13247 bigfloat */ 13248 res = JS_NewBigFloat(ctx); 13249 if (JS_IsException(res)) { 13250 JS_FreeBigInt(ctx, a, &a_s); 13251 JS_FreeBigInt(ctx, b, &b_s); 13252 goto fail; 13253 } 13254 r = JS_GetBigFloat(res); 13255 if (op == OP_div) { 13256 ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR; 13257 } else { 13258 ret = bf_pow(r, a, b, ctx->fp_env.prec, 13259 ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR; 13260 } 13261 JS_FreeBigInt(ctx, a, &a_s); 13262 JS_FreeBigInt(ctx, b, &b_s); 13263 JS_FreeValue(ctx, op1); 13264 JS_FreeValue(ctx, op2); 13265 if (unlikely(ret)) { 13266 JS_FreeValue(ctx, res); 13267 throw_bf_exception(ctx, ret); 13268 return -1; 13269 } 13270 *pres = res; 13271 return 0; 13272 #else 13273 abort(); 13274 #endif 13275 } 13276 } else { 13277 ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); 13278 } 13279 break; 13280 13281 /* logical operations */ 13282 case OP_shl: 13283 case OP_sar: 13284 { 13285 slimb_t v2; 13286 #if LIMB_BITS == 32 13287 bf_get_int32(&v2, b, 0); 13288 if (v2 == INT32_MIN) 13289 v2 = INT32_MIN + 1; 13290 #else 13291 bf_get_int64(&v2, b, 0); 13292 if (v2 == INT64_MIN) 13293 v2 = INT64_MIN + 1; 13294 #endif 13295 if (op == OP_sar) 13296 v2 = -v2; 13297 ret = bf_set(r, a); 13298 ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); 13299 if (v2 < 0) { 13300 ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); 13301 } 13302 } 13303 break; 13304 case OP_and: 13305 ret = bf_logic_and(r, a, b); 13306 break; 13307 case OP_or: 13308 ret = bf_logic_or(r, a, b); 13309 break; 13310 case OP_xor: 13311 ret = bf_logic_xor(r, a, b); 13312 break; 13313 default: 13314 abort(); 13315 } 13316 JS_FreeBigInt(ctx, a, &a_s); 13317 JS_FreeBigInt(ctx, b, &b_s); 13318 JS_FreeValue(ctx, op1); 13319 JS_FreeValue(ctx, op2); 13320 if (unlikely(ret)) { 13321 JS_FreeValue(ctx, res); 13322 throw_bf_exception(ctx, ret); 13323 return -1; 13324 } 13325 *pres = JS_CompactBigInt(ctx, res); 13326 return 0; 13327 fail: 13328 JS_FreeValue(ctx, res); 13329 JS_FreeValue(ctx, op1); 13330 JS_FreeValue(ctx, op2); 13331 return -1; 13332 } 13333 13334 #ifdef CONFIG_BIGNUM 13335 static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op, 13336 JSValue *pres, JSValue op1, JSValue op2) 13337 { 13338 bf_t a_s, b_s, *r, *a, *b; 13339 int ret; 13340 JSValue res; 13341 13342 res = JS_NewBigFloat(ctx); 13343 if (JS_IsException(res)) 13344 goto fail; 13345 r = JS_GetBigFloat(res); 13346 a = JS_ToBigFloat(ctx, &a_s, op1); 13347 if (!a) { 13348 JS_FreeValue(ctx, res); 13349 goto fail; 13350 } 13351 b = JS_ToBigFloat(ctx, &b_s, op2); 13352 if (!b) { 13353 if (a == &a_s) 13354 bf_delete(a); 13355 JS_FreeValue(ctx, res); 13356 goto fail; 13357 } 13358 bf_init(ctx->bf_ctx, r); 13359 switch(op) { 13360 case OP_add: 13361 ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); 13362 break; 13363 case OP_sub: 13364 ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); 13365 break; 13366 case OP_mul: 13367 ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); 13368 break; 13369 case OP_div: 13370 ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags); 13371 break; 13372 case OP_math_mod: 13373 /* Euclidian remainder */ 13374 ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, 13375 BF_DIVREM_EUCLIDIAN); 13376 break; 13377 case OP_mod: 13378 ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags, 13379 BF_RNDZ); 13380 break; 13381 case OP_pow: 13382 ret = bf_pow(r, a, b, ctx->fp_env.prec, 13383 ctx->fp_env.flags | BF_POW_JS_QUIRKS); 13384 break; 13385 default: 13386 abort(); 13387 } 13388 if (a == &a_s) 13389 bf_delete(a); 13390 if (b == &b_s) 13391 bf_delete(b); 13392 JS_FreeValue(ctx, op1); 13393 JS_FreeValue(ctx, op2); 13394 if (unlikely(ret & BF_ST_MEM_ERROR)) { 13395 JS_FreeValue(ctx, res); 13396 throw_bf_exception(ctx, ret); 13397 return -1; 13398 } 13399 *pres = res; 13400 return 0; 13401 fail: 13402 JS_FreeValue(ctx, op1); 13403 JS_FreeValue(ctx, op2); 13404 return -1; 13405 } 13406 13407 /* b must be a positive integer */ 13408 static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b) 13409 { 13410 bfdec_t b1; 13411 int32_t b2; 13412 int ret; 13413 13414 bfdec_init(b->ctx, &b1); 13415 ret = bfdec_set(&b1, b); 13416 if (ret) { 13417 bfdec_delete(&b1); 13418 return ret; 13419 } 13420 ret = bfdec_rint(&b1, BF_RNDZ); 13421 if (ret) { 13422 bfdec_delete(&b1); 13423 return BF_ST_INVALID_OP; /* must be an integer */ 13424 } 13425 ret = bfdec_get_int32(&b2, &b1); 13426 bfdec_delete(&b1); 13427 if (ret) 13428 return ret; /* overflow */ 13429 if (b2 < 0) 13430 return BF_ST_INVALID_OP; /* must be positive */ 13431 return bfdec_pow_ui(r, a, b2); 13432 } 13433 13434 static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, 13435 JSValue *pres, JSValue op1, JSValue op2) 13436 { 13437 bfdec_t *r, *a, *b; 13438 int ret; 13439 JSValue res; 13440 13441 res = JS_NewBigDecimal(ctx); 13442 if (JS_IsException(res)) 13443 goto fail; 13444 r = JS_GetBigDecimal(res); 13445 13446 a = JS_ToBigDecimal(ctx, op1); 13447 if (!a) 13448 goto fail; 13449 b = JS_ToBigDecimal(ctx, op2); 13450 if (!b) 13451 goto fail; 13452 switch(op) { 13453 case OP_add: 13454 ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ); 13455 break; 13456 case OP_sub: 13457 ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ); 13458 break; 13459 case OP_mul: 13460 ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ); 13461 break; 13462 case OP_div: 13463 ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ); 13464 break; 13465 case OP_math_mod: 13466 /* Euclidian remainder */ 13467 ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN); 13468 break; 13469 case OP_mod: 13470 ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); 13471 break; 13472 case OP_pow: 13473 ret = js_bfdec_pow(r, a, b); 13474 break; 13475 default: 13476 abort(); 13477 } 13478 JS_FreeValue(ctx, op1); 13479 JS_FreeValue(ctx, op2); 13480 if (unlikely(ret)) { 13481 JS_FreeValue(ctx, res); 13482 throw_bf_exception(ctx, ret); 13483 return -1; 13484 } 13485 *pres = res; 13486 return 0; 13487 fail: 13488 JS_FreeValue(ctx, res); 13489 JS_FreeValue(ctx, op1); 13490 JS_FreeValue(ctx, op2); 13491 return -1; 13492 } 13493 #endif /* CONFIG_BIGNUM */ 13494 13495 static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, 13496 OPCodeEnum op) 13497 { 13498 JSValue op1, op2; 13499 uint32_t tag1, tag2; 13500 double d1, d2; 13501 13502 op1 = sp[-2]; 13503 op2 = sp[-1]; 13504 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13505 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13506 /* fast path for float operations */ 13507 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { 13508 d1 = JS_VALUE_GET_FLOAT64(op1); 13509 d2 = JS_VALUE_GET_FLOAT64(op2); 13510 goto handle_float64; 13511 } 13512 13513 #ifdef CONFIG_BIGNUM 13514 /* try to call an overloaded operator */ 13515 if ((tag1 == JS_TAG_OBJECT && 13516 (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || 13517 (tag2 == JS_TAG_OBJECT && 13518 (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { 13519 JSValue res; 13520 int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); 13521 if (ret != 0) { 13522 JS_FreeValue(ctx, op1); 13523 JS_FreeValue(ctx, op2); 13524 if (ret < 0) { 13525 goto exception; 13526 } else { 13527 sp[-2] = res; 13528 return 0; 13529 } 13530 } 13531 } 13532 #endif 13533 13534 op1 = JS_ToNumericFree(ctx, op1); 13535 if (JS_IsException(op1)) { 13536 JS_FreeValue(ctx, op2); 13537 goto exception; 13538 } 13539 op2 = JS_ToNumericFree(ctx, op2); 13540 if (JS_IsException(op2)) { 13541 JS_FreeValue(ctx, op1); 13542 goto exception; 13543 } 13544 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13545 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13546 13547 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 13548 int32_t v1, v2; 13549 int64_t v; 13550 v1 = JS_VALUE_GET_INT(op1); 13551 v2 = JS_VALUE_GET_INT(op2); 13552 switch(op) { 13553 case OP_sub: 13554 v = (int64_t)v1 - (int64_t)v2; 13555 break; 13556 case OP_mul: 13557 v = (int64_t)v1 * (int64_t)v2; 13558 if (is_math_mode(ctx) && 13559 (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) 13560 goto handle_bigint; 13561 if (v == 0 && (v1 | v2) < 0) { 13562 sp[-2] = __JS_NewFloat64(ctx, -0.0); 13563 return 0; 13564 } 13565 break; 13566 case OP_div: 13567 if (is_math_mode(ctx)) 13568 goto handle_bigint; 13569 sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2); 13570 return 0; 13571 #ifdef CONFIG_BIGNUM 13572 case OP_math_mod: 13573 if (unlikely(v2 == 0)) { 13574 throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); 13575 goto exception; 13576 } 13577 v = (int64_t)v1 % (int64_t)v2; 13578 if (v < 0) { 13579 if (v2 < 0) 13580 v -= v2; 13581 else 13582 v += v2; 13583 } 13584 break; 13585 #endif 13586 case OP_mod: 13587 if (v1 < 0 || v2 <= 0) { 13588 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); 13589 return 0; 13590 } else { 13591 v = (int64_t)v1 % (int64_t)v2; 13592 } 13593 break; 13594 case OP_pow: 13595 if (!is_math_mode(ctx)) { 13596 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2)); 13597 return 0; 13598 } else { 13599 goto handle_bigint; 13600 } 13601 break; 13602 default: 13603 abort(); 13604 } 13605 sp[-2] = JS_NewInt64(ctx, v); 13606 } else 13607 #ifdef CONFIG_BIGNUM 13608 if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { 13609 if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2)) 13610 goto exception; 13611 } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { 13612 if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2)) 13613 goto exception; 13614 } else 13615 #endif 13616 if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { 13617 handle_bigint: 13618 if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) 13619 goto exception; 13620 } else { 13621 double dr; 13622 /* float64 result */ 13623 if (JS_ToFloat64Free(ctx, &d1, op1)) { 13624 JS_FreeValue(ctx, op2); 13625 goto exception; 13626 } 13627 if (JS_ToFloat64Free(ctx, &d2, op2)) 13628 goto exception; 13629 handle_float64: 13630 if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) 13631 goto handle_bigint; 13632 switch(op) { 13633 case OP_sub: 13634 dr = d1 - d2; 13635 break; 13636 case OP_mul: 13637 dr = d1 * d2; 13638 break; 13639 case OP_div: 13640 dr = d1 / d2; 13641 break; 13642 case OP_mod: 13643 dr = fmod(d1, d2); 13644 break; 13645 #ifdef CONFIG_BIGNUM 13646 case OP_math_mod: 13647 d2 = fabs(d2); 13648 dr = fmod(d1, d2); 13649 /* XXX: loss of accuracy if dr < 0 */ 13650 if (dr < 0) 13651 dr += d2; 13652 break; 13653 #endif 13654 case OP_pow: 13655 dr = js_pow(d1, d2); 13656 break; 13657 default: 13658 abort(); 13659 } 13660 sp[-2] = __JS_NewFloat64(ctx, dr); 13661 } 13662 return 0; 13663 exception: 13664 sp[-2] = JS_UNDEFINED; 13665 sp[-1] = JS_UNDEFINED; 13666 return -1; 13667 } 13668 13669 static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) 13670 { 13671 JSValue op1, op2; 13672 uint32_t tag1, tag2; 13673 13674 op1 = sp[-2]; 13675 op2 = sp[-1]; 13676 13677 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13678 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13679 /* fast path for float64 */ 13680 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { 13681 double d1, d2; 13682 d1 = JS_VALUE_GET_FLOAT64(op1); 13683 d2 = JS_VALUE_GET_FLOAT64(op2); 13684 sp[-2] = __JS_NewFloat64(ctx, d1 + d2); 13685 return 0; 13686 } 13687 13688 if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { 13689 #ifdef CONFIG_BIGNUM 13690 /* try to call an overloaded operator */ 13691 if ((tag1 == JS_TAG_OBJECT && 13692 (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && 13693 tag2 != JS_TAG_STRING)) || 13694 (tag2 == JS_TAG_OBJECT && 13695 (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED && 13696 tag1 != JS_TAG_STRING))) { 13697 JSValue res; 13698 int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add, 13699 FALSE, HINT_NONE); 13700 if (ret != 0) { 13701 JS_FreeValue(ctx, op1); 13702 JS_FreeValue(ctx, op2); 13703 if (ret < 0) { 13704 goto exception; 13705 } else { 13706 sp[-2] = res; 13707 return 0; 13708 } 13709 } 13710 } 13711 #endif 13712 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); 13713 if (JS_IsException(op1)) { 13714 JS_FreeValue(ctx, op2); 13715 goto exception; 13716 } 13717 13718 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); 13719 if (JS_IsException(op2)) { 13720 JS_FreeValue(ctx, op1); 13721 goto exception; 13722 } 13723 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13724 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13725 } 13726 13727 if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { 13728 sp[-2] = JS_ConcatString(ctx, op1, op2); 13729 if (JS_IsException(sp[-2])) 13730 goto exception; 13731 return 0; 13732 } 13733 13734 op1 = JS_ToNumericFree(ctx, op1); 13735 if (JS_IsException(op1)) { 13736 JS_FreeValue(ctx, op2); 13737 goto exception; 13738 } 13739 op2 = JS_ToNumericFree(ctx, op2); 13740 if (JS_IsException(op2)) { 13741 JS_FreeValue(ctx, op1); 13742 goto exception; 13743 } 13744 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13745 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13746 13747 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 13748 int32_t v1, v2; 13749 int64_t v; 13750 v1 = JS_VALUE_GET_INT(op1); 13751 v2 = JS_VALUE_GET_INT(op2); 13752 v = (int64_t)v1 + (int64_t)v2; 13753 sp[-2] = JS_NewInt64(ctx, v); 13754 } else 13755 #ifdef CONFIG_BIGNUM 13756 if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { 13757 if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) 13758 goto exception; 13759 } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { 13760 if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) 13761 goto exception; 13762 } else 13763 #endif 13764 if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { 13765 handle_bigint: 13766 if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2)) 13767 goto exception; 13768 } else { 13769 double d1, d2; 13770 /* float64 result */ 13771 if (JS_ToFloat64Free(ctx, &d1, op1)) { 13772 JS_FreeValue(ctx, op2); 13773 goto exception; 13774 } 13775 if (JS_ToFloat64Free(ctx, &d2, op2)) 13776 goto exception; 13777 if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2)) 13778 goto handle_bigint; 13779 sp[-2] = __JS_NewFloat64(ctx, d1 + d2); 13780 } 13781 return 0; 13782 exception: 13783 sp[-2] = JS_UNDEFINED; 13784 sp[-1] = JS_UNDEFINED; 13785 return -1; 13786 } 13787 13788 static no_inline __exception int js_binary_logic_slow(JSContext *ctx, 13789 JSValue *sp, 13790 OPCodeEnum op) 13791 { 13792 JSValue op1, op2; 13793 uint32_t tag1, tag2; 13794 uint32_t v1, v2, r; 13795 13796 op1 = sp[-2]; 13797 op2 = sp[-1]; 13798 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13799 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13800 13801 #ifdef CONFIG_BIGNUM 13802 /* try to call an overloaded operator */ 13803 if ((tag1 == JS_TAG_OBJECT && 13804 (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || 13805 (tag2 == JS_TAG_OBJECT && 13806 (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { 13807 JSValue res; 13808 int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); 13809 if (ret != 0) { 13810 JS_FreeValue(ctx, op1); 13811 JS_FreeValue(ctx, op2); 13812 if (ret < 0) { 13813 goto exception; 13814 } else { 13815 sp[-2] = res; 13816 return 0; 13817 } 13818 } 13819 } 13820 #endif 13821 13822 op1 = JS_ToNumericFree(ctx, op1); 13823 if (JS_IsException(op1)) { 13824 JS_FreeValue(ctx, op2); 13825 goto exception; 13826 } 13827 op2 = JS_ToNumericFree(ctx, op2); 13828 if (JS_IsException(op2)) { 13829 JS_FreeValue(ctx, op1); 13830 goto exception; 13831 } 13832 13833 if (is_math_mode(ctx)) 13834 goto bigint_op; 13835 13836 tag1 = JS_VALUE_GET_TAG(op1); 13837 tag2 = JS_VALUE_GET_TAG(op2); 13838 if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { 13839 if (tag1 != tag2) { 13840 JS_FreeValue(ctx, op1); 13841 JS_FreeValue(ctx, op2); 13842 JS_ThrowTypeError(ctx, "both operands must be bigint"); 13843 goto exception; 13844 } else { 13845 bigint_op: 13846 if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2)) 13847 goto exception; 13848 } 13849 } else { 13850 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { 13851 JS_FreeValue(ctx, op2); 13852 goto exception; 13853 } 13854 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) 13855 goto exception; 13856 switch(op) { 13857 case OP_shl: 13858 r = v1 << (v2 & 0x1f); 13859 break; 13860 case OP_sar: 13861 r = (int)v1 >> (v2 & 0x1f); 13862 break; 13863 case OP_and: 13864 r = v1 & v2; 13865 break; 13866 case OP_or: 13867 r = v1 | v2; 13868 break; 13869 case OP_xor: 13870 r = v1 ^ v2; 13871 break; 13872 default: 13873 abort(); 13874 } 13875 sp[-2] = JS_NewInt32(ctx, r); 13876 } 13877 return 0; 13878 exception: 13879 sp[-2] = JS_UNDEFINED; 13880 sp[-1] = JS_UNDEFINED; 13881 return -1; 13882 } 13883 13884 /* Note: also used for bigint */ 13885 static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, 13886 JSValue op1, JSValue op2) 13887 { 13888 bf_t a_s, b_s, *a, *b; 13889 int res; 13890 13891 a = JS_ToBigFloat(ctx, &a_s, op1); 13892 if (!a) { 13893 JS_FreeValue(ctx, op2); 13894 return -1; 13895 } 13896 b = JS_ToBigFloat(ctx, &b_s, op2); 13897 if (!b) { 13898 if (a == &a_s) 13899 bf_delete(a); 13900 JS_FreeValue(ctx, op1); 13901 return -1; 13902 } 13903 switch(op) { 13904 case OP_lt: 13905 res = bf_cmp_lt(a, b); /* if NaN return false */ 13906 break; 13907 case OP_lte: 13908 res = bf_cmp_le(a, b); /* if NaN return false */ 13909 break; 13910 case OP_gt: 13911 res = bf_cmp_lt(b, a); /* if NaN return false */ 13912 break; 13913 case OP_gte: 13914 res = bf_cmp_le(b, a); /* if NaN return false */ 13915 break; 13916 case OP_eq: 13917 res = bf_cmp_eq(a, b); /* if NaN return false */ 13918 break; 13919 default: 13920 abort(); 13921 } 13922 if (a == &a_s) 13923 bf_delete(a); 13924 if (b == &b_s) 13925 bf_delete(b); 13926 JS_FreeValue(ctx, op1); 13927 JS_FreeValue(ctx, op2); 13928 return res; 13929 } 13930 13931 #ifdef CONFIG_BIGNUM 13932 static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, 13933 JSValue op1, JSValue op2) 13934 { 13935 bfdec_t *a, *b; 13936 int res; 13937 13938 /* Note: binary floats are converted to bigdecimal with 13939 toString(). It is not mathematically correct but is consistent 13940 with the BigDecimal() constructor behavior */ 13941 op1 = JS_ToBigDecimalFree(ctx, op1, TRUE); 13942 if (JS_IsException(op1)) { 13943 JS_FreeValue(ctx, op2); 13944 return -1; 13945 } 13946 op2 = JS_ToBigDecimalFree(ctx, op2, TRUE); 13947 if (JS_IsException(op2)) { 13948 JS_FreeValue(ctx, op1); 13949 return -1; 13950 } 13951 a = JS_ToBigDecimal(ctx, op1); /* cannot fail */ 13952 b = JS_ToBigDecimal(ctx, op2); /* cannot fail */ 13953 13954 switch(op) { 13955 case OP_lt: 13956 res = bfdec_cmp_lt(a, b); /* if NaN return false */ 13957 break; 13958 case OP_lte: 13959 res = bfdec_cmp_le(a, b); /* if NaN return false */ 13960 break; 13961 case OP_gt: 13962 res = bfdec_cmp_lt(b, a); /* if NaN return false */ 13963 break; 13964 case OP_gte: 13965 res = bfdec_cmp_le(b, a); /* if NaN return false */ 13966 break; 13967 case OP_eq: 13968 res = bfdec_cmp_eq(a, b); /* if NaN return false */ 13969 break; 13970 default: 13971 abort(); 13972 } 13973 JS_FreeValue(ctx, op1); 13974 JS_FreeValue(ctx, op2); 13975 return res; 13976 } 13977 #endif /* !CONFIG_BIGNUM */ 13978 13979 static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, 13980 OPCodeEnum op) 13981 { 13982 JSValue op1, op2; 13983 int res; 13984 uint32_t tag1, tag2; 13985 13986 op1 = sp[-2]; 13987 op2 = sp[-1]; 13988 tag1 = JS_VALUE_GET_NORM_TAG(op1); 13989 tag2 = JS_VALUE_GET_NORM_TAG(op2); 13990 #ifdef CONFIG_BIGNUM 13991 /* try to call an overloaded operator */ 13992 if ((tag1 == JS_TAG_OBJECT && 13993 (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) || 13994 (tag2 == JS_TAG_OBJECT && 13995 (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) { 13996 JSValue ret; 13997 res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, 13998 FALSE, HINT_NUMBER); 13999 if (res != 0) { 14000 JS_FreeValue(ctx, op1); 14001 JS_FreeValue(ctx, op2); 14002 if (res < 0) { 14003 goto exception; 14004 } else { 14005 sp[-2] = ret; 14006 return 0; 14007 } 14008 } 14009 } 14010 #endif 14011 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); 14012 if (JS_IsException(op1)) { 14013 JS_FreeValue(ctx, op2); 14014 goto exception; 14015 } 14016 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); 14017 if (JS_IsException(op2)) { 14018 JS_FreeValue(ctx, op1); 14019 goto exception; 14020 } 14021 tag1 = JS_VALUE_GET_NORM_TAG(op1); 14022 tag2 = JS_VALUE_GET_NORM_TAG(op2); 14023 14024 if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { 14025 JSString *p1, *p2; 14026 p1 = JS_VALUE_GET_STRING(op1); 14027 p2 = JS_VALUE_GET_STRING(op2); 14028 res = js_string_compare(ctx, p1, p2); 14029 switch(op) { 14030 case OP_lt: 14031 res = (res < 0); 14032 break; 14033 case OP_lte: 14034 res = (res <= 0); 14035 break; 14036 case OP_gt: 14037 res = (res > 0); 14038 break; 14039 default: 14040 case OP_gte: 14041 res = (res >= 0); 14042 break; 14043 } 14044 JS_FreeValue(ctx, op1); 14045 JS_FreeValue(ctx, op2); 14046 } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && 14047 (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { 14048 /* fast path for float64/int */ 14049 goto float64_compare; 14050 } else { 14051 if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || 14052 (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) && 14053 !is_math_mode(ctx)) { 14054 if (tag1 == JS_TAG_STRING) { 14055 op1 = JS_StringToBigInt(ctx, op1); 14056 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) 14057 goto invalid_bigint_string; 14058 } 14059 if (tag2 == JS_TAG_STRING) { 14060 op2 = JS_StringToBigInt(ctx, op2); 14061 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { 14062 invalid_bigint_string: 14063 JS_FreeValue(ctx, op1); 14064 JS_FreeValue(ctx, op2); 14065 res = FALSE; 14066 goto done; 14067 } 14068 } 14069 } else { 14070 op1 = JS_ToNumericFree(ctx, op1); 14071 if (JS_IsException(op1)) { 14072 JS_FreeValue(ctx, op2); 14073 goto exception; 14074 } 14075 op2 = JS_ToNumericFree(ctx, op2); 14076 if (JS_IsException(op2)) { 14077 JS_FreeValue(ctx, op1); 14078 goto exception; 14079 } 14080 } 14081 14082 tag1 = JS_VALUE_GET_NORM_TAG(op1); 14083 tag2 = JS_VALUE_GET_NORM_TAG(op2); 14084 14085 #ifdef CONFIG_BIGNUM 14086 if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { 14087 res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); 14088 if (res < 0) 14089 goto exception; 14090 } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { 14091 res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2); 14092 if (res < 0) 14093 goto exception; 14094 } else 14095 #endif 14096 if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { 14097 res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2); 14098 if (res < 0) 14099 goto exception; 14100 } else { 14101 double d1, d2; 14102 14103 float64_compare: 14104 /* can use floating point comparison */ 14105 if (tag1 == JS_TAG_FLOAT64) { 14106 d1 = JS_VALUE_GET_FLOAT64(op1); 14107 } else { 14108 d1 = JS_VALUE_GET_INT(op1); 14109 } 14110 if (tag2 == JS_TAG_FLOAT64) { 14111 d2 = JS_VALUE_GET_FLOAT64(op2); 14112 } else { 14113 d2 = JS_VALUE_GET_INT(op2); 14114 } 14115 switch(op) { 14116 case OP_lt: 14117 res = (d1 < d2); /* if NaN return false */ 14118 break; 14119 case OP_lte: 14120 res = (d1 <= d2); /* if NaN return false */ 14121 break; 14122 case OP_gt: 14123 res = (d1 > d2); /* if NaN return false */ 14124 break; 14125 default: 14126 case OP_gte: 14127 res = (d1 >= d2); /* if NaN return false */ 14128 break; 14129 } 14130 } 14131 } 14132 done: 14133 sp[-2] = JS_NewBool(ctx, res); 14134 return 0; 14135 exception: 14136 sp[-2] = JS_UNDEFINED; 14137 sp[-1] = JS_UNDEFINED; 14138 return -1; 14139 } 14140 14141 static BOOL tag_is_number(uint32_t tag) 14142 { 14143 return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || 14144 tag == JS_TAG_FLOAT64 14145 #ifdef CONFIG_BIGNUM 14146 || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL 14147 #endif 14148 ); 14149 } 14150 14151 static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, 14152 BOOL is_neq) 14153 { 14154 JSValue op1, op2; 14155 #ifdef CONFIG_BIGNUM 14156 JSValue ret; 14157 #endif 14158 int res; 14159 uint32_t tag1, tag2; 14160 14161 op1 = sp[-2]; 14162 op2 = sp[-1]; 14163 redo: 14164 tag1 = JS_VALUE_GET_NORM_TAG(op1); 14165 tag2 = JS_VALUE_GET_NORM_TAG(op2); 14166 if (tag_is_number(tag1) && tag_is_number(tag2)) { 14167 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { 14168 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); 14169 } else if ((tag1 == JS_TAG_FLOAT64 && 14170 (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || 14171 (tag2 == JS_TAG_FLOAT64 && 14172 (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { 14173 double d1, d2; 14174 if (tag1 == JS_TAG_FLOAT64) { 14175 d1 = JS_VALUE_GET_FLOAT64(op1); 14176 } else { 14177 d1 = JS_VALUE_GET_INT(op1); 14178 } 14179 if (tag2 == JS_TAG_FLOAT64) { 14180 d2 = JS_VALUE_GET_FLOAT64(op2); 14181 } else { 14182 d2 = JS_VALUE_GET_INT(op2); 14183 } 14184 res = (d1 == d2); 14185 } else 14186 #ifdef CONFIG_BIGNUM 14187 if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { 14188 res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2); 14189 if (res < 0) 14190 goto exception; 14191 } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) { 14192 res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); 14193 if (res < 0) 14194 goto exception; 14195 } else 14196 #endif 14197 { 14198 res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); 14199 if (res < 0) 14200 goto exception; 14201 } 14202 } else if (tag1 == tag2) { 14203 #ifdef CONFIG_BIGNUM 14204 if (tag1 == JS_TAG_OBJECT) { 14205 /* try the fallback operator */ 14206 res = js_call_binary_op_fallback(ctx, &ret, op1, op2, 14207 is_neq ? OP_neq : OP_eq, 14208 FALSE, HINT_NONE); 14209 if (res != 0) { 14210 JS_FreeValue(ctx, op1); 14211 JS_FreeValue(ctx, op2); 14212 if (res < 0) { 14213 goto exception; 14214 } else { 14215 sp[-2] = ret; 14216 return 0; 14217 } 14218 } 14219 } 14220 #endif 14221 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); 14222 } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || 14223 (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { 14224 res = TRUE; 14225 } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || 14226 (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { 14227 14228 if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) && 14229 !is_math_mode(ctx)) { 14230 if (tag1 == JS_TAG_STRING) { 14231 op1 = JS_StringToBigInt(ctx, op1); 14232 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) 14233 goto invalid_bigint_string; 14234 } 14235 if (tag2 == JS_TAG_STRING) { 14236 op2 = JS_StringToBigInt(ctx, op2); 14237 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { 14238 invalid_bigint_string: 14239 JS_FreeValue(ctx, op1); 14240 JS_FreeValue(ctx, op2); 14241 res = FALSE; 14242 goto done; 14243 } 14244 } 14245 } else { 14246 op1 = JS_ToNumericFree(ctx, op1); 14247 if (JS_IsException(op1)) { 14248 JS_FreeValue(ctx, op2); 14249 goto exception; 14250 } 14251 op2 = JS_ToNumericFree(ctx, op2); 14252 if (JS_IsException(op2)) { 14253 JS_FreeValue(ctx, op1); 14254 goto exception; 14255 } 14256 } 14257 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); 14258 } else if (tag1 == JS_TAG_BOOL) { 14259 op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); 14260 goto redo; 14261 } else if (tag2 == JS_TAG_BOOL) { 14262 op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2)); 14263 goto redo; 14264 } else if ((tag1 == JS_TAG_OBJECT && 14265 (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || 14266 (tag2 == JS_TAG_OBJECT && 14267 (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { 14268 #ifdef CONFIG_BIGNUM 14269 /* try the fallback operator */ 14270 res = js_call_binary_op_fallback(ctx, &ret, op1, op2, 14271 is_neq ? OP_neq : OP_eq, 14272 FALSE, HINT_NONE); 14273 if (res != 0) { 14274 JS_FreeValue(ctx, op1); 14275 JS_FreeValue(ctx, op2); 14276 if (res < 0) { 14277 goto exception; 14278 } else { 14279 sp[-2] = ret; 14280 return 0; 14281 } 14282 } 14283 #endif 14284 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); 14285 if (JS_IsException(op1)) { 14286 JS_FreeValue(ctx, op2); 14287 goto exception; 14288 } 14289 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); 14290 if (JS_IsException(op2)) { 14291 JS_FreeValue(ctx, op1); 14292 goto exception; 14293 } 14294 goto redo; 14295 } else { 14296 /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ 14297 if ((JS_IsHTMLDDA(ctx, op1) && 14298 (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || 14299 (JS_IsHTMLDDA(ctx, op2) && 14300 (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { 14301 res = TRUE; 14302 } else { 14303 res = FALSE; 14304 } 14305 JS_FreeValue(ctx, op1); 14306 JS_FreeValue(ctx, op2); 14307 } 14308 done: 14309 sp[-2] = JS_NewBool(ctx, res ^ is_neq); 14310 return 0; 14311 exception: 14312 sp[-2] = JS_UNDEFINED; 14313 sp[-1] = JS_UNDEFINED; 14314 return -1; 14315 } 14316 14317 static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) 14318 { 14319 JSValue op1, op2; 14320 uint32_t v1, v2, r; 14321 14322 op1 = sp[-2]; 14323 op2 = sp[-1]; 14324 op1 = JS_ToNumericFree(ctx, op1); 14325 if (JS_IsException(op1)) { 14326 JS_FreeValue(ctx, op2); 14327 goto exception; 14328 } 14329 op2 = JS_ToNumericFree(ctx, op2); 14330 if (JS_IsException(op2)) { 14331 JS_FreeValue(ctx, op1); 14332 goto exception; 14333 } 14334 /* XXX: could forbid >>> in bignum mode */ 14335 if (!is_math_mode(ctx) && 14336 (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || 14337 JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { 14338 JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>"); 14339 JS_FreeValue(ctx, op1); 14340 JS_FreeValue(ctx, op2); 14341 goto exception; 14342 } 14343 /* cannot give an exception */ 14344 JS_ToUint32Free(ctx, &v1, op1); 14345 JS_ToUint32Free(ctx, &v2, op2); 14346 r = v1 >> (v2 & 0x1f); 14347 sp[-2] = JS_NewUint32(ctx, r); 14348 return 0; 14349 exception: 14350 sp[-2] = JS_UNDEFINED; 14351 sp[-1] = JS_UNDEFINED; 14352 return -1; 14353 } 14354 14355 #ifdef CONFIG_BIGNUM 14356 static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, 14357 int64_t exponent) 14358 { 14359 bf_t r_s, *r = &r_s; 14360 double d; 14361 int ret; 14362 14363 /* always convert to Float64 */ 14364 bf_init(ctx->bf_ctx, r); 14365 ret = bf_mul_pow_radix(r, a, 10, exponent, 14366 53, bf_set_exp_bits(11) | BF_RNDN | 14367 BF_FLAG_SUBNORMAL); 14368 bf_get_float64(r, &d, BF_RNDN); 14369 bf_delete(r); 14370 if (ret & BF_ST_MEM_ERROR) 14371 return JS_ThrowOutOfMemory(ctx); 14372 else 14373 return __JS_NewFloat64(ctx, d); 14374 } 14375 14376 static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) 14377 { 14378 bf_t a_s, *a, *r; 14379 JSValue op1, op2, res; 14380 int64_t e; 14381 int ret; 14382 14383 res = JS_NewBigFloat(ctx); 14384 if (JS_IsException(res)) 14385 return -1; 14386 r = JS_GetBigFloat(res); 14387 op1 = sp[-2]; 14388 op2 = sp[-1]; 14389 a = JS_ToBigFloat(ctx, &a_s, op1); 14390 if (!a) { 14391 JS_FreeValue(ctx, res); 14392 return -1; 14393 } 14394 if (JS_IsBigInt(ctx, op2)) { 14395 ret = JS_ToBigInt64(ctx, &e, op2); 14396 } else { 14397 ret = JS_ToInt64(ctx, &e, op2); 14398 } 14399 if (ret) { 14400 if (a == &a_s) 14401 bf_delete(a); 14402 JS_FreeValue(ctx, res); 14403 return -1; 14404 } 14405 14406 bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags); 14407 if (a == &a_s) 14408 bf_delete(a); 14409 JS_FreeValue(ctx, op1); 14410 JS_FreeValue(ctx, op2); 14411 sp[-2] = res; 14412 return 0; 14413 } 14414 #endif 14415 14416 /* XXX: Should take JSValueConst arguments */ 14417 static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 14418 JSStrictEqModeEnum eq_mode) 14419 { 14420 BOOL res; 14421 int tag1, tag2; 14422 double d1, d2; 14423 14424 tag1 = JS_VALUE_GET_NORM_TAG(op1); 14425 tag2 = JS_VALUE_GET_NORM_TAG(op2); 14426 switch(tag1) { 14427 case JS_TAG_BOOL: 14428 if (tag1 != tag2) { 14429 res = FALSE; 14430 } else { 14431 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); 14432 goto done_no_free; 14433 } 14434 break; 14435 case JS_TAG_NULL: 14436 case JS_TAG_UNDEFINED: 14437 res = (tag1 == tag2); 14438 break; 14439 case JS_TAG_STRING: 14440 { 14441 JSString *p1, *p2; 14442 if (tag1 != tag2) { 14443 res = FALSE; 14444 } else { 14445 p1 = JS_VALUE_GET_STRING(op1); 14446 p2 = JS_VALUE_GET_STRING(op2); 14447 res = (js_string_compare(ctx, p1, p2) == 0); 14448 } 14449 } 14450 break; 14451 case JS_TAG_SYMBOL: 14452 { 14453 JSAtomStruct *p1, *p2; 14454 if (tag1 != tag2) { 14455 res = FALSE; 14456 } else { 14457 p1 = JS_VALUE_GET_PTR(op1); 14458 p2 = JS_VALUE_GET_PTR(op2); 14459 res = (p1 == p2); 14460 } 14461 } 14462 break; 14463 case JS_TAG_OBJECT: 14464 if (tag1 != tag2) 14465 res = FALSE; 14466 else 14467 res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); 14468 break; 14469 case JS_TAG_INT: 14470 d1 = JS_VALUE_GET_INT(op1); 14471 if (tag2 == JS_TAG_INT) { 14472 d2 = JS_VALUE_GET_INT(op2); 14473 goto number_test; 14474 } else if (tag2 == JS_TAG_FLOAT64) { 14475 d2 = JS_VALUE_GET_FLOAT64(op2); 14476 goto number_test; 14477 } else { 14478 res = FALSE; 14479 } 14480 break; 14481 case JS_TAG_FLOAT64: 14482 d1 = JS_VALUE_GET_FLOAT64(op1); 14483 if (tag2 == JS_TAG_FLOAT64) { 14484 d2 = JS_VALUE_GET_FLOAT64(op2); 14485 } else if (tag2 == JS_TAG_INT) { 14486 d2 = JS_VALUE_GET_INT(op2); 14487 } else { 14488 res = FALSE; 14489 break; 14490 } 14491 number_test: 14492 if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { 14493 JSFloat64Union u1, u2; 14494 /* NaN is not always normalized, so this test is necessary */ 14495 if (isnan(d1) || isnan(d2)) { 14496 res = isnan(d1) == isnan(d2); 14497 } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { 14498 res = (d1 == d2); /* +0 == -0 */ 14499 } else { 14500 u1.d = d1; 14501 u2.d = d2; 14502 res = (u1.u64 == u2.u64); /* +0 != -0 */ 14503 } 14504 } else { 14505 res = (d1 == d2); /* if NaN return false and +0 == -0 */ 14506 } 14507 goto done_no_free; 14508 case JS_TAG_BIG_INT: 14509 { 14510 bf_t a_s, *a, b_s, *b; 14511 if (tag1 != tag2) { 14512 res = FALSE; 14513 break; 14514 } 14515 a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */ 14516 b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */ 14517 res = bf_cmp_eq(a, b); 14518 if (a == &a_s) 14519 bf_delete(a); 14520 if (b == &b_s) 14521 bf_delete(b); 14522 } 14523 break; 14524 #ifdef CONFIG_BIGNUM 14525 case JS_TAG_BIG_FLOAT: 14526 { 14527 JSBigFloat *p1, *p2; 14528 const bf_t *a, *b; 14529 if (tag1 != tag2) { 14530 res = FALSE; 14531 break; 14532 } 14533 p1 = JS_VALUE_GET_PTR(op1); 14534 p2 = JS_VALUE_GET_PTR(op2); 14535 a = &p1->num; 14536 b = &p2->num; 14537 if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { 14538 if (eq_mode == JS_EQ_SAME_VALUE_ZERO && 14539 a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) { 14540 res = TRUE; 14541 } else { 14542 res = (bf_cmp_full(a, b) == 0); 14543 } 14544 } else { 14545 res = bf_cmp_eq(a, b); 14546 } 14547 } 14548 break; 14549 case JS_TAG_BIG_DECIMAL: 14550 { 14551 JSBigDecimal *p1, *p2; 14552 const bfdec_t *a, *b; 14553 if (tag1 != tag2) { 14554 res = FALSE; 14555 break; 14556 } 14557 p1 = JS_VALUE_GET_PTR(op1); 14558 p2 = JS_VALUE_GET_PTR(op2); 14559 a = &p1->num; 14560 b = &p2->num; 14561 res = bfdec_cmp_eq(a, b); 14562 } 14563 break; 14564 #endif 14565 default: 14566 res = FALSE; 14567 break; 14568 } 14569 JS_FreeValue(ctx, op1); 14570 JS_FreeValue(ctx, op2); 14571 done_no_free: 14572 return res; 14573 } 14574 14575 static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14576 { 14577 return js_strict_eq2(ctx, 14578 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 14579 JS_EQ_STRICT); 14580 } 14581 14582 BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14583 { 14584 return js_strict_eq(ctx, op1, op2); 14585 } 14586 14587 static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14588 { 14589 return js_strict_eq2(ctx, 14590 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 14591 JS_EQ_SAME_VALUE); 14592 } 14593 14594 BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14595 { 14596 return js_same_value(ctx, op1, op2); 14597 } 14598 14599 static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14600 { 14601 return js_strict_eq2(ctx, 14602 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), 14603 JS_EQ_SAME_VALUE_ZERO); 14604 } 14605 14606 BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) 14607 { 14608 return js_same_value_zero(ctx, op1, op2); 14609 } 14610 14611 static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, 14612 BOOL is_neq) 14613 { 14614 BOOL res; 14615 res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); 14616 sp[-2] = JS_NewBool(ctx, res ^ is_neq); 14617 return 0; 14618 } 14619 14620 static __exception int js_operator_in(JSContext *ctx, JSValue *sp) 14621 { 14622 JSValue op1, op2; 14623 JSAtom atom; 14624 int ret; 14625 14626 op1 = sp[-2]; 14627 op2 = sp[-1]; 14628 14629 if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { 14630 JS_ThrowTypeError(ctx, "invalid 'in' operand"); 14631 return -1; 14632 } 14633 atom = JS_ValueToAtom(ctx, op1); 14634 if (unlikely(atom == JS_ATOM_NULL)) 14635 return -1; 14636 ret = JS_HasProperty(ctx, op2, atom); 14637 JS_FreeAtom(ctx, atom); 14638 if (ret < 0) 14639 return -1; 14640 JS_FreeValue(ctx, op1); 14641 JS_FreeValue(ctx, op2); 14642 sp[-2] = JS_NewBool(ctx, ret); 14643 return 0; 14644 } 14645 14646 static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) 14647 { 14648 JSValue op1, op2; 14649 int ret; 14650 14651 op1 = sp[-2]; /* object */ 14652 op2 = sp[-1]; /* field name or method function */ 14653 14654 if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { 14655 JS_ThrowTypeError(ctx, "invalid 'in' operand"); 14656 return -1; 14657 } 14658 if (JS_IsObject(op2)) { 14659 /* method: use the brand */ 14660 ret = JS_CheckBrand(ctx, op1, op2); 14661 if (ret < 0) 14662 return -1; 14663 } else { 14664 JSAtom atom; 14665 JSObject *p; 14666 JSShapeProperty *prs; 14667 JSProperty *pr; 14668 /* field */ 14669 atom = JS_ValueToAtom(ctx, op2); 14670 if (unlikely(atom == JS_ATOM_NULL)) 14671 return -1; 14672 p = JS_VALUE_GET_OBJ(op1); 14673 prs = find_own_property(&pr, p, atom); 14674 JS_FreeAtom(ctx, atom); 14675 ret = (prs != NULL); 14676 } 14677 JS_FreeValue(ctx, op1); 14678 JS_FreeValue(ctx, op2); 14679 sp[-2] = JS_NewBool(ctx, ret); 14680 return 0; 14681 } 14682 14683 static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, 14684 JSAtom atom) 14685 { 14686 JSValue arr, val; 14687 int ret; 14688 14689 arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); 14690 if (JS_IsException(arr)) 14691 return -1; 14692 ret = 0; 14693 if (JS_IsObject(arr)) { 14694 val = JS_GetProperty(ctx, arr, atom); 14695 ret = JS_ToBoolFree(ctx, val); 14696 } 14697 JS_FreeValue(ctx, arr); 14698 return ret; 14699 } 14700 14701 static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) 14702 { 14703 JSValue op1, op2; 14704 BOOL ret; 14705 14706 op1 = sp[-2]; 14707 op2 = sp[-1]; 14708 ret = JS_IsInstanceOf(ctx, op1, op2); 14709 if (ret < 0) 14710 return ret; 14711 JS_FreeValue(ctx, op1); 14712 JS_FreeValue(ctx, op2); 14713 sp[-2] = JS_NewBool(ctx, ret); 14714 return 0; 14715 } 14716 14717 static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) 14718 { 14719 JSAtom atom; 14720 uint32_t tag; 14721 14722 tag = JS_VALUE_GET_NORM_TAG(op1); 14723 switch(tag) { 14724 case JS_TAG_BIG_INT: 14725 atom = JS_ATOM_bigint; 14726 break; 14727 #ifdef CONFIG_BIGNUM 14728 case JS_TAG_BIG_FLOAT: 14729 atom = JS_ATOM_bigfloat; 14730 break; 14731 case JS_TAG_BIG_DECIMAL: 14732 atom = JS_ATOM_bigdecimal; 14733 break; 14734 #endif 14735 case JS_TAG_INT: 14736 case JS_TAG_FLOAT64: 14737 atom = JS_ATOM_number; 14738 break; 14739 case JS_TAG_UNDEFINED: 14740 atom = JS_ATOM_undefined; 14741 break; 14742 case JS_TAG_BOOL: 14743 atom = JS_ATOM_boolean; 14744 break; 14745 case JS_TAG_STRING: 14746 atom = JS_ATOM_string; 14747 break; 14748 case JS_TAG_OBJECT: 14749 { 14750 JSObject *p; 14751 p = JS_VALUE_GET_OBJ(op1); 14752 if (unlikely(p->is_HTMLDDA)) 14753 atom = JS_ATOM_undefined; 14754 else if (JS_IsFunction(ctx, op1)) 14755 atom = JS_ATOM_function; 14756 else 14757 goto obj_type; 14758 } 14759 break; 14760 case JS_TAG_NULL: 14761 obj_type: 14762 atom = JS_ATOM_object; 14763 break; 14764 case JS_TAG_SYMBOL: 14765 atom = JS_ATOM_symbol; 14766 break; 14767 default: 14768 atom = JS_ATOM_unknown; 14769 break; 14770 } 14771 return atom; 14772 } 14773 14774 static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) 14775 { 14776 JSValue op1, op2; 14777 JSAtom atom; 14778 int ret; 14779 14780 op1 = sp[-2]; 14781 op2 = sp[-1]; 14782 atom = JS_ValueToAtom(ctx, op2); 14783 if (unlikely(atom == JS_ATOM_NULL)) 14784 return -1; 14785 ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); 14786 JS_FreeAtom(ctx, atom); 14787 if (unlikely(ret < 0)) 14788 return -1; 14789 JS_FreeValue(ctx, op1); 14790 JS_FreeValue(ctx, op2); 14791 sp[-2] = JS_NewBool(ctx, ret); 14792 return 0; 14793 } 14794 14795 static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val, 14796 int argc, JSValueConst *argv) 14797 { 14798 return JS_ThrowTypeError(ctx, "invalid property access"); 14799 } 14800 14801 /* XXX: not 100% compatible, but mozilla seems to use a similar 14802 implementation to ensure that caller in non strict mode does not 14803 throw (ES5 compatibility) */ 14804 static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val, 14805 int argc, JSValueConst *argv) 14806 { 14807 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 14808 if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { 14809 return js_throw_type_error(ctx, this_val, 0, NULL); 14810 } 14811 return JS_UNDEFINED; 14812 } 14813 14814 static JSValue js_function_proto_fileName(JSContext *ctx, 14815 JSValueConst this_val) 14816 { 14817 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 14818 if (b && b->has_debug) { 14819 return JS_AtomToString(ctx, b->debug.filename); 14820 } 14821 return JS_UNDEFINED; 14822 } 14823 14824 static JSValue js_function_proto_lineNumber(JSContext *ctx, 14825 JSValueConst this_val) 14826 { 14827 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); 14828 if (b && b->has_debug) { 14829 return JS_NewInt32(ctx, b->debug.line_num); 14830 } 14831 return JS_UNDEFINED; 14832 } 14833 14834 static int js_arguments_define_own_property(JSContext *ctx, 14835 JSValueConst this_obj, 14836 JSAtom prop, JSValueConst val, 14837 JSValueConst getter, JSValueConst setter, int flags) 14838 { 14839 JSObject *p; 14840 uint32_t idx; 14841 p = JS_VALUE_GET_OBJ(this_obj); 14842 /* convert to normal array when redefining an existing numeric field */ 14843 if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && 14844 idx < p->u.array.count) { 14845 if (convert_fast_array_to_array(ctx, p)) 14846 return -1; 14847 } 14848 /* run the default define own property */ 14849 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, 14850 flags | JS_PROP_NO_EXOTIC); 14851 } 14852 14853 static const JSClassExoticMethods js_arguments_exotic_methods = { 14854 .define_own_property = js_arguments_define_own_property, 14855 }; 14856 14857 static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv) 14858 { 14859 JSValue val, *tab; 14860 JSProperty *pr; 14861 JSObject *p; 14862 int i; 14863 14864 val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 14865 JS_CLASS_ARGUMENTS); 14866 if (JS_IsException(val)) 14867 return val; 14868 p = JS_VALUE_GET_OBJ(val); 14869 14870 /* add the length field (cannot fail) */ 14871 pr = add_property(ctx, p, JS_ATOM_length, 14872 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 14873 pr->u.value = JS_NewInt32(ctx, argc); 14874 14875 /* initialize the fast array part */ 14876 tab = NULL; 14877 if (argc > 0) { 14878 tab = js_malloc(ctx, sizeof(tab[0]) * argc); 14879 if (!tab) { 14880 JS_FreeValue(ctx, val); 14881 return JS_EXCEPTION; 14882 } 14883 for(i = 0; i < argc; i++) { 14884 tab[i] = JS_DupValue(ctx, argv[i]); 14885 } 14886 } 14887 p->u.array.u.values = tab; 14888 p->u.array.count = argc; 14889 14890 JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, 14891 JS_DupValue(ctx, ctx->array_proto_values), 14892 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); 14893 /* add callee property to throw a TypeError in strict mode */ 14894 JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, 14895 ctx->throw_type_error, ctx->throw_type_error, 14896 JS_PROP_HAS_GET | JS_PROP_HAS_SET); 14897 return val; 14898 } 14899 14900 #define GLOBAL_VAR_OFFSET 0x40000000 14901 #define ARGUMENT_VAR_OFFSET 0x20000000 14902 14903 /* legacy arguments object: add references to the function arguments */ 14904 static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, 14905 JSValueConst *argv, 14906 JSStackFrame *sf, int arg_count) 14907 { 14908 JSValue val; 14909 JSProperty *pr; 14910 JSObject *p; 14911 int i; 14912 14913 val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 14914 JS_CLASS_MAPPED_ARGUMENTS); 14915 if (JS_IsException(val)) 14916 return val; 14917 p = JS_VALUE_GET_OBJ(val); 14918 14919 /* add the length field (cannot fail) */ 14920 pr = add_property(ctx, p, JS_ATOM_length, 14921 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 14922 pr->u.value = JS_NewInt32(ctx, argc); 14923 14924 for(i = 0; i < arg_count; i++) { 14925 JSVarRef *var_ref; 14926 var_ref = get_var_ref(ctx, sf, i, TRUE); 14927 if (!var_ref) 14928 goto fail; 14929 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); 14930 if (!pr) { 14931 free_var_ref(ctx->rt, var_ref); 14932 goto fail; 14933 } 14934 pr->u.var_ref = var_ref; 14935 } 14936 14937 /* the arguments not mapped to the arguments of the function can 14938 be normal properties */ 14939 for(i = arg_count; i < argc; i++) { 14940 if (JS_DefinePropertyValueUint32(ctx, val, i, 14941 JS_DupValue(ctx, argv[i]), 14942 JS_PROP_C_W_E) < 0) 14943 goto fail; 14944 } 14945 14946 JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, 14947 JS_DupValue(ctx, ctx->array_proto_values), 14948 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); 14949 /* callee returns this function in non strict mode */ 14950 JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, 14951 JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func), 14952 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); 14953 return val; 14954 fail: 14955 JS_FreeValue(ctx, val); 14956 return JS_EXCEPTION; 14957 } 14958 14959 static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv) 14960 { 14961 JSValue val; 14962 int i, ret; 14963 14964 val = JS_NewArray(ctx); 14965 if (JS_IsException(val)) 14966 return val; 14967 for (i = first; i < argc; i++) { 14968 ret = JS_DefinePropertyValueUint32(ctx, val, i - first, 14969 JS_DupValue(ctx, argv[i]), 14970 JS_PROP_C_W_E); 14971 if (ret < 0) { 14972 JS_FreeValue(ctx, val); 14973 return JS_EXCEPTION; 14974 } 14975 } 14976 return val; 14977 } 14978 14979 static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) 14980 { 14981 JSObject *p, *p1; 14982 JSPropertyEnum *tab_atom; 14983 int i; 14984 JSValue enum_obj; 14985 JSForInIterator *it; 14986 uint32_t tag, tab_atom_count; 14987 14988 tag = JS_VALUE_GET_TAG(obj); 14989 if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { 14990 obj = JS_ToObjectFree(ctx, obj); 14991 } 14992 14993 it = js_malloc(ctx, sizeof(*it)); 14994 if (!it) { 14995 JS_FreeValue(ctx, obj); 14996 return JS_EXCEPTION; 14997 } 14998 enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); 14999 if (JS_IsException(enum_obj)) { 15000 js_free(ctx, it); 15001 JS_FreeValue(ctx, obj); 15002 return JS_EXCEPTION; 15003 } 15004 it->is_array = FALSE; 15005 it->obj = obj; 15006 it->idx = 0; 15007 it->tab_atom = NULL; 15008 it->atom_count = 0; 15009 it->in_prototype_chain = FALSE; 15010 p1 = JS_VALUE_GET_OBJ(enum_obj); 15011 p1->u.for_in_iterator = it; 15012 15013 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) 15014 return enum_obj; 15015 15016 p = JS_VALUE_GET_OBJ(obj); 15017 if (p->fast_array) { 15018 JSShape *sh; 15019 JSShapeProperty *prs; 15020 /* check that there are no enumerable normal fields */ 15021 sh = p->shape; 15022 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { 15023 if (prs->flags & JS_PROP_ENUMERABLE) 15024 goto normal_case; 15025 } 15026 /* for fast arrays, we only store the number of elements */ 15027 it->is_array = TRUE; 15028 it->atom_count = p->u.array.count; 15029 } else { 15030 normal_case: 15031 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, 15032 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 15033 JS_FreeValue(ctx, enum_obj); 15034 return JS_EXCEPTION; 15035 } 15036 it->tab_atom = tab_atom; 15037 it->atom_count = tab_atom_count; 15038 } 15039 return enum_obj; 15040 } 15041 15042 /* obj -> enum_obj */ 15043 static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) 15044 { 15045 sp[-1] = build_for_in_iterator(ctx, sp[-1]); 15046 if (JS_IsException(sp[-1])) 15047 return -1; 15048 return 0; 15049 } 15050 15051 /* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */ 15052 static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx, 15053 JSValueConst enum_obj) 15054 { 15055 JSObject *p; 15056 JSForInIterator *it; 15057 JSPropertyEnum *tab_atom; 15058 uint32_t tab_atom_count, i; 15059 JSValue obj1; 15060 15061 p = JS_VALUE_GET_OBJ(enum_obj); 15062 it = p->u.for_in_iterator; 15063 15064 /* check if there are enumerable properties in the prototype chain (fast path) */ 15065 obj1 = JS_DupValue(ctx, it->obj); 15066 for(;;) { 15067 obj1 = JS_GetPrototypeFree(ctx, obj1); 15068 if (JS_IsNull(obj1)) 15069 break; 15070 if (JS_IsException(obj1)) 15071 goto fail; 15072 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 15073 JS_VALUE_GET_OBJ(obj1), 15074 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { 15075 JS_FreeValue(ctx, obj1); 15076 goto fail; 15077 } 15078 js_free_prop_enum(ctx, tab_atom, tab_atom_count); 15079 if (tab_atom_count != 0) { 15080 JS_FreeValue(ctx, obj1); 15081 goto slow_path; 15082 } 15083 /* must check for timeout to avoid infinite loop */ 15084 if (js_poll_interrupts(ctx)) { 15085 JS_FreeValue(ctx, obj1); 15086 goto fail; 15087 } 15088 } 15089 JS_FreeValue(ctx, obj1); 15090 return 1; 15091 15092 slow_path: 15093 /* add the visited properties, even if they are not enumerable */ 15094 if (it->is_array) { 15095 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 15096 JS_VALUE_GET_OBJ(it->obj), 15097 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 15098 goto fail; 15099 } 15100 it->is_array = FALSE; 15101 it->tab_atom = tab_atom; 15102 it->atom_count = tab_atom_count; 15103 } 15104 15105 for(i = 0; i < it->atom_count; i++) { 15106 if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0) 15107 goto fail; 15108 } 15109 return 0; 15110 fail: 15111 return -1; 15112 } 15113 15114 /* enum_obj -> enum_obj value done */ 15115 static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) 15116 { 15117 JSValueConst enum_obj; 15118 JSObject *p; 15119 JSAtom prop; 15120 JSForInIterator *it; 15121 JSPropertyEnum *tab_atom; 15122 uint32_t tab_atom_count; 15123 int ret; 15124 15125 enum_obj = sp[-1]; 15126 /* fail safe */ 15127 if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) 15128 goto done; 15129 p = JS_VALUE_GET_OBJ(enum_obj); 15130 if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) 15131 goto done; 15132 it = p->u.for_in_iterator; 15133 15134 for(;;) { 15135 if (it->idx >= it->atom_count) { 15136 if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj)) 15137 goto done; /* not an object */ 15138 /* no more property in the current object: look in the prototype */ 15139 if (!it->in_prototype_chain) { 15140 ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj); 15141 if (ret < 0) 15142 return -1; 15143 if (ret) 15144 goto done; 15145 it->in_prototype_chain = TRUE; 15146 } 15147 it->obj = JS_GetPrototypeFree(ctx, it->obj); 15148 if (JS_IsException(it->obj)) 15149 return -1; 15150 if (JS_IsNull(it->obj)) 15151 goto done; /* no more prototype */ 15152 15153 /* must check for timeout to avoid infinite loop */ 15154 if (js_poll_interrupts(ctx)) 15155 return -1; 15156 15157 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, 15158 JS_VALUE_GET_OBJ(it->obj), 15159 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { 15160 return -1; 15161 } 15162 js_free_prop_enum(ctx, it->tab_atom, it->atom_count); 15163 it->tab_atom = tab_atom; 15164 it->atom_count = tab_atom_count; 15165 it->idx = 0; 15166 } else { 15167 if (it->is_array) { 15168 prop = __JS_AtomFromUInt32(it->idx); 15169 it->idx++; 15170 } else { 15171 BOOL is_enumerable; 15172 prop = it->tab_atom[it->idx].atom; 15173 is_enumerable = it->tab_atom[it->idx].is_enumerable; 15174 it->idx++; 15175 if (it->in_prototype_chain) { 15176 /* slow case: we are in the prototype chain */ 15177 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop); 15178 if (ret < 0) 15179 return ret; 15180 if (ret) 15181 continue; /* already visited */ 15182 /* add to the visited property list */ 15183 if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL, 15184 JS_PROP_ENUMERABLE) < 0) 15185 return -1; 15186 } 15187 if (!is_enumerable) 15188 continue; 15189 } 15190 /* check if the property was deleted */ 15191 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop); 15192 if (ret < 0) 15193 return ret; 15194 if (ret) 15195 break; 15196 } 15197 } 15198 /* return the property */ 15199 sp[0] = JS_AtomToValue(ctx, prop); 15200 sp[1] = JS_FALSE; 15201 return 0; 15202 done: 15203 /* return the end */ 15204 sp[0] = JS_UNDEFINED; 15205 sp[1] = JS_TRUE; 15206 return 0; 15207 } 15208 15209 static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj, 15210 JSValueConst method) 15211 { 15212 JSValue enum_obj; 15213 15214 enum_obj = JS_Call(ctx, method, obj, 0, NULL); 15215 if (JS_IsException(enum_obj)) 15216 return enum_obj; 15217 if (!JS_IsObject(enum_obj)) { 15218 JS_FreeValue(ctx, enum_obj); 15219 return JS_ThrowTypeErrorNotAnObject(ctx); 15220 } 15221 return enum_obj; 15222 } 15223 15224 static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async) 15225 { 15226 JSValue method, ret, sync_iter; 15227 15228 if (is_async) { 15229 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); 15230 if (JS_IsException(method)) 15231 return method; 15232 if (JS_IsUndefined(method) || JS_IsNull(method)) { 15233 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 15234 if (JS_IsException(method)) 15235 return method; 15236 sync_iter = JS_GetIterator2(ctx, obj, method); 15237 JS_FreeValue(ctx, method); 15238 if (JS_IsException(sync_iter)) 15239 return sync_iter; 15240 ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); 15241 JS_FreeValue(ctx, sync_iter); 15242 return ret; 15243 } 15244 } else { 15245 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 15246 if (JS_IsException(method)) 15247 return method; 15248 } 15249 if (!JS_IsFunction(ctx, method)) { 15250 JS_FreeValue(ctx, method); 15251 return JS_ThrowTypeError(ctx, "value is not iterable"); 15252 } 15253 ret = JS_GetIterator2(ctx, obj, method); 15254 JS_FreeValue(ctx, method); 15255 return ret; 15256 } 15257 15258 /* return *pdone = 2 if the iterator object is not parsed */ 15259 static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj, 15260 JSValueConst method, 15261 int argc, JSValueConst *argv, int *pdone) 15262 { 15263 JSValue obj; 15264 15265 /* fast path for the built-in iterators (avoid creating the 15266 intermediate result object) */ 15267 if (JS_IsObject(method)) { 15268 JSObject *p = JS_VALUE_GET_OBJ(method); 15269 if (p->class_id == JS_CLASS_C_FUNCTION && 15270 p->u.cfunc.cproto == JS_CFUNC_iterator_next) { 15271 JSCFunctionType func; 15272 JSValueConst args[1]; 15273 15274 /* in case the function expects one argument */ 15275 if (argc == 0) { 15276 args[0] = JS_UNDEFINED; 15277 argv = args; 15278 } 15279 func = p->u.cfunc.c_function; 15280 return func.iterator_next(ctx, enum_obj, argc, argv, 15281 pdone, p->u.cfunc.magic); 15282 } 15283 } 15284 obj = JS_Call(ctx, method, enum_obj, argc, argv); 15285 if (JS_IsException(obj)) 15286 goto fail; 15287 if (!JS_IsObject(obj)) { 15288 JS_FreeValue(ctx, obj); 15289 JS_ThrowTypeError(ctx, "iterator must return an object"); 15290 goto fail; 15291 } 15292 *pdone = 2; 15293 return obj; 15294 fail: 15295 *pdone = FALSE; 15296 return JS_EXCEPTION; 15297 } 15298 15299 static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj, 15300 JSValueConst method, 15301 int argc, JSValueConst *argv, BOOL *pdone) 15302 { 15303 JSValue obj, value, done_val; 15304 int done; 15305 15306 obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); 15307 if (JS_IsException(obj)) 15308 goto fail; 15309 if (done != 2) { 15310 *pdone = done; 15311 return obj; 15312 } else { 15313 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); 15314 if (JS_IsException(done_val)) 15315 goto fail; 15316 *pdone = JS_ToBoolFree(ctx, done_val); 15317 value = JS_UNDEFINED; 15318 if (!*pdone) { 15319 value = JS_GetProperty(ctx, obj, JS_ATOM_value); 15320 } 15321 JS_FreeValue(ctx, obj); 15322 return value; 15323 } 15324 fail: 15325 JS_FreeValue(ctx, obj); 15326 *pdone = FALSE; 15327 return JS_EXCEPTION; 15328 } 15329 15330 /* return < 0 in case of exception */ 15331 static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, 15332 BOOL is_exception_pending) 15333 { 15334 JSValue method, ret, ex_obj; 15335 int res; 15336 15337 if (is_exception_pending) { 15338 ex_obj = ctx->rt->current_exception; 15339 ctx->rt->current_exception = JS_UNINITIALIZED; 15340 res = -1; 15341 } else { 15342 ex_obj = JS_UNDEFINED; 15343 res = 0; 15344 } 15345 method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); 15346 if (JS_IsException(method)) { 15347 res = -1; 15348 goto done; 15349 } 15350 if (JS_IsUndefined(method) || JS_IsNull(method)) { 15351 goto done; 15352 } 15353 ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); 15354 if (!is_exception_pending) { 15355 if (JS_IsException(ret)) { 15356 res = -1; 15357 } else if (!JS_IsObject(ret)) { 15358 JS_ThrowTypeErrorNotAnObject(ctx); 15359 res = -1; 15360 } 15361 } 15362 JS_FreeValue(ctx, ret); 15363 done: 15364 if (is_exception_pending) { 15365 JS_Throw(ctx, ex_obj); 15366 } 15367 return res; 15368 } 15369 15370 /* obj -> enum_rec (3 slots) */ 15371 static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, 15372 BOOL is_async) 15373 { 15374 JSValue op1, obj, method; 15375 op1 = sp[-1]; 15376 obj = JS_GetIterator(ctx, op1, is_async); 15377 if (JS_IsException(obj)) 15378 return -1; 15379 JS_FreeValue(ctx, op1); 15380 sp[-1] = obj; 15381 method = JS_GetProperty(ctx, obj, JS_ATOM_next); 15382 if (JS_IsException(method)) 15383 return -1; 15384 sp[0] = method; 15385 return 0; 15386 } 15387 15388 /* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' 15389 objs. If 'done' is true or in case of exception, 'enum_rec' is set 15390 to undefined. If 'done' is true, 'value' is always set to 15391 undefined. */ 15392 static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) 15393 { 15394 JSValue value = JS_UNDEFINED; 15395 int done = 1; 15396 15397 if (likely(!JS_IsUndefined(sp[offset]))) { 15398 value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); 15399 if (JS_IsException(value)) 15400 done = -1; 15401 if (done) { 15402 /* value is JS_UNDEFINED or JS_EXCEPTION */ 15403 /* replace the iteration object with undefined */ 15404 JS_FreeValue(ctx, sp[offset]); 15405 sp[offset] = JS_UNDEFINED; 15406 if (done < 0) { 15407 return -1; 15408 } else { 15409 JS_FreeValue(ctx, value); 15410 value = JS_UNDEFINED; 15411 } 15412 } 15413 } 15414 sp[0] = value; 15415 sp[1] = JS_NewBool(ctx, done); 15416 return 0; 15417 } 15418 15419 static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, 15420 BOOL *pdone) 15421 { 15422 JSValue done_val, value; 15423 BOOL done; 15424 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); 15425 if (JS_IsException(done_val)) 15426 goto fail; 15427 done = JS_ToBoolFree(ctx, done_val); 15428 value = JS_GetProperty(ctx, obj, JS_ATOM_value); 15429 if (JS_IsException(value)) 15430 goto fail; 15431 *pdone = done; 15432 return value; 15433 fail: 15434 *pdone = FALSE; 15435 return JS_EXCEPTION; 15436 } 15437 15438 static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) 15439 { 15440 JSValue obj, value; 15441 BOOL done; 15442 obj = sp[-1]; 15443 if (!JS_IsObject(obj)) { 15444 JS_ThrowTypeError(ctx, "iterator must return an object"); 15445 return -1; 15446 } 15447 value = JS_IteratorGetCompleteValue(ctx, obj, &done); 15448 if (JS_IsException(value)) 15449 return -1; 15450 JS_FreeValue(ctx, obj); 15451 sp[-1] = value; 15452 sp[0] = JS_NewBool(ctx, done); 15453 return 0; 15454 } 15455 15456 static JSValue js_create_iterator_result(JSContext *ctx, 15457 JSValue val, 15458 BOOL done) 15459 { 15460 JSValue obj; 15461 obj = JS_NewObject(ctx); 15462 if (JS_IsException(obj)) { 15463 JS_FreeValue(ctx, val); 15464 return obj; 15465 } 15466 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, 15467 val, JS_PROP_C_W_E) < 0) { 15468 goto fail; 15469 } 15470 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, 15471 JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) { 15472 fail: 15473 JS_FreeValue(ctx, obj); 15474 return JS_EXCEPTION; 15475 } 15476 return obj; 15477 } 15478 15479 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, 15480 int argc, JSValueConst *argv, 15481 BOOL *pdone, int magic); 15482 15483 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, 15484 int argc, JSValueConst *argv, int magic); 15485 15486 static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj) 15487 { 15488 /* Try and handle fast arrays explicitly */ 15489 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 15490 JSObject *p = JS_VALUE_GET_OBJ(obj); 15491 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { 15492 return TRUE; 15493 } 15494 } 15495 return FALSE; 15496 } 15497 15498 /* Access an Array's internal JSValue array if available */ 15499 static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, 15500 JSValue **arrpp, uint32_t *countp) 15501 { 15502 /* Try and handle fast arrays explicitly */ 15503 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 15504 JSObject *p = JS_VALUE_GET_OBJ(obj); 15505 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { 15506 *countp = p->u.array.count; 15507 *arrpp = p->u.array.u.values; 15508 return TRUE; 15509 } 15510 } 15511 return FALSE; 15512 } 15513 15514 static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) 15515 { 15516 JSValue iterator, enumobj, method, value; 15517 int is_array_iterator; 15518 JSValue *arrp; 15519 uint32_t i, count32, pos; 15520 15521 if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { 15522 JS_ThrowInternalError(ctx, "invalid index for append"); 15523 return -1; 15524 } 15525 15526 pos = JS_VALUE_GET_INT(sp[-2]); 15527 15528 /* XXX: further optimisations: 15529 - use ctx->array_proto_values? 15530 - check if array_iterator_prototype next method is built-in and 15531 avoid constructing actual iterator object? 15532 - build this into js_for_of_start and use in all `for (x of o)` loops 15533 */ 15534 iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); 15535 if (JS_IsException(iterator)) 15536 return -1; 15537 is_array_iterator = JS_IsCFunction(ctx, iterator, 15538 (JSCFunction *)js_create_array_iterator, 15539 JS_ITERATOR_KIND_VALUE); 15540 JS_FreeValue(ctx, iterator); 15541 15542 enumobj = JS_GetIterator(ctx, sp[-1], FALSE); 15543 if (JS_IsException(enumobj)) 15544 return -1; 15545 method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); 15546 if (JS_IsException(method)) { 15547 JS_FreeValue(ctx, enumobj); 15548 return -1; 15549 } 15550 if (is_array_iterator 15551 && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0) 15552 && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { 15553 uint32_t len; 15554 if (js_get_length32(ctx, &len, sp[-1])) 15555 goto exception; 15556 /* if len > count32, the elements >= count32 might be read in 15557 the prototypes and might have side effects */ 15558 if (len != count32) 15559 goto general_case; 15560 /* Handle fast arrays explicitly */ 15561 for (i = 0; i < count32; i++) { 15562 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, 15563 JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0) 15564 goto exception; 15565 } 15566 } else { 15567 general_case: 15568 for (;;) { 15569 BOOL done; 15570 value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); 15571 if (JS_IsException(value)) 15572 goto exception; 15573 if (done) { 15574 /* value is JS_UNDEFINED */ 15575 break; 15576 } 15577 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) 15578 goto exception; 15579 } 15580 } 15581 /* Note: could raise an error if too many elements */ 15582 sp[-2] = JS_NewInt32(ctx, pos); 15583 JS_FreeValue(ctx, enumobj); 15584 JS_FreeValue(ctx, method); 15585 return 0; 15586 15587 exception: 15588 JS_IteratorClose(ctx, enumobj, TRUE); 15589 JS_FreeValue(ctx, enumobj); 15590 JS_FreeValue(ctx, method); 15591 return -1; 15592 } 15593 15594 static __exception int JS_CopyDataProperties(JSContext *ctx, 15595 JSValueConst target, 15596 JSValueConst source, 15597 JSValueConst excluded, 15598 BOOL setprop) 15599 { 15600 JSPropertyEnum *tab_atom; 15601 JSValue val; 15602 uint32_t i, tab_atom_count; 15603 JSObject *p; 15604 JSObject *pexcl = NULL; 15605 int ret, gpn_flags; 15606 JSPropertyDescriptor desc; 15607 BOOL is_enumerable; 15608 15609 if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) 15610 return 0; 15611 15612 if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) 15613 pexcl = JS_VALUE_GET_OBJ(excluded); 15614 15615 p = JS_VALUE_GET_OBJ(source); 15616 15617 gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; 15618 if (p->is_exotic) { 15619 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; 15620 /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it 15621 introduces a visible change */ 15622 if (em && em->get_own_property_names) { 15623 gpn_flags &= ~JS_GPN_ENUM_ONLY; 15624 } 15625 } 15626 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, 15627 gpn_flags)) 15628 return -1; 15629 15630 for (i = 0; i < tab_atom_count; i++) { 15631 if (pexcl) { 15632 ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); 15633 if (ret) { 15634 if (ret < 0) 15635 goto exception; 15636 continue; 15637 } 15638 } 15639 if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { 15640 /* test if the property is enumerable */ 15641 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); 15642 if (ret < 0) 15643 goto exception; 15644 if (!ret) 15645 continue; 15646 is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; 15647 js_free_desc(ctx, &desc); 15648 if (!is_enumerable) 15649 continue; 15650 } 15651 val = JS_GetProperty(ctx, source, tab_atom[i].atom); 15652 if (JS_IsException(val)) 15653 goto exception; 15654 if (setprop) 15655 ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); 15656 else 15657 ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, 15658 JS_PROP_C_W_E); 15659 if (ret < 0) 15660 goto exception; 15661 } 15662 js_free_prop_enum(ctx, tab_atom, tab_atom_count); 15663 return 0; 15664 exception: 15665 js_free_prop_enum(ctx, tab_atom, tab_atom_count); 15666 return -1; 15667 } 15668 15669 /* only valid inside C functions */ 15670 static JSValueConst JS_GetActiveFunction(JSContext *ctx) 15671 { 15672 return ctx->rt->current_stack_frame->cur_func; 15673 } 15674 15675 static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, 15676 int var_idx, BOOL is_arg) 15677 { 15678 JSVarRef *var_ref; 15679 struct list_head *el; 15680 15681 list_for_each(el, &sf->var_ref_list) { 15682 var_ref = list_entry(el, JSVarRef, var_ref_link); 15683 if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { 15684 var_ref->header.ref_count++; 15685 return var_ref; 15686 } 15687 } 15688 /* create a new one */ 15689 var_ref = js_malloc(ctx, sizeof(JSVarRef)); 15690 if (!var_ref) 15691 return NULL; 15692 var_ref->header.ref_count = 1; 15693 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); 15694 var_ref->is_detached = FALSE; 15695 var_ref->is_arg = is_arg; 15696 var_ref->var_idx = var_idx; 15697 list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list); 15698 if (sf->js_mode & JS_MODE_ASYNC) { 15699 /* The stack frame is detached and may be destroyed at any 15700 time so its reference count must be increased. Calling 15701 close_var_refs() when destroying the stack frame is not 15702 possible because it would change the graph between the GC 15703 objects. Another solution could be to temporarily detach 15704 the JSVarRef of async functions during the GC. It would 15705 have the advantage of allowing the release of unused stack 15706 frames in a cycle. */ 15707 var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame); 15708 var_ref->async_func->header.ref_count++; 15709 } else { 15710 var_ref->async_func = NULL; 15711 } 15712 if (is_arg) 15713 var_ref->pvalue = &sf->arg_buf[var_idx]; 15714 else 15715 var_ref->pvalue = &sf->var_buf[var_idx]; 15716 return var_ref; 15717 } 15718 15719 static JSValue js_closure2(JSContext *ctx, JSValue func_obj, 15720 JSFunctionBytecode *b, 15721 JSVarRef **cur_var_refs, 15722 JSStackFrame *sf) 15723 { 15724 JSObject *p; 15725 JSVarRef **var_refs; 15726 int i; 15727 15728 p = JS_VALUE_GET_OBJ(func_obj); 15729 p->u.func.function_bytecode = b; 15730 p->u.func.home_object = NULL; 15731 p->u.func.var_refs = NULL; 15732 if (b->closure_var_count) { 15733 var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); 15734 if (!var_refs) 15735 goto fail; 15736 p->u.func.var_refs = var_refs; 15737 for(i = 0; i < b->closure_var_count; i++) { 15738 JSClosureVar *cv = &b->closure_var[i]; 15739 JSVarRef *var_ref; 15740 if (cv->is_local) { 15741 /* reuse the existing variable reference if it already exists */ 15742 var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); 15743 if (!var_ref) 15744 goto fail; 15745 } else { 15746 var_ref = cur_var_refs[cv->var_idx]; 15747 var_ref->header.ref_count++; 15748 } 15749 var_refs[i] = var_ref; 15750 } 15751 } 15752 return func_obj; 15753 fail: 15754 /* bfunc is freed when func_obj is freed */ 15755 JS_FreeValue(ctx, func_obj); 15756 return JS_EXCEPTION; 15757 } 15758 15759 static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) 15760 { 15761 JSValue obj, this_val; 15762 int ret; 15763 15764 this_val = JS_MKPTR(JS_TAG_OBJECT, p); 15765 obj = JS_NewObject(ctx); 15766 if (JS_IsException(obj)) 15767 return JS_EXCEPTION; 15768 set_cycle_flag(ctx, obj); 15769 set_cycle_flag(ctx, this_val); 15770 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, 15771 JS_DupValue(ctx, this_val), 15772 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 15773 if (ret < 0) { 15774 JS_FreeValue(ctx, obj); 15775 return JS_EXCEPTION; 15776 } 15777 return obj; 15778 } 15779 15780 static const uint16_t func_kind_to_class_id[] = { 15781 [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, 15782 [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, 15783 [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, 15784 [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, 15785 }; 15786 15787 static JSValue js_closure(JSContext *ctx, JSValue bfunc, 15788 JSVarRef **cur_var_refs, 15789 JSStackFrame *sf) 15790 { 15791 JSFunctionBytecode *b; 15792 JSValue func_obj; 15793 JSAtom name_atom; 15794 15795 b = JS_VALUE_GET_PTR(bfunc); 15796 func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); 15797 if (JS_IsException(func_obj)) { 15798 JS_FreeValue(ctx, bfunc); 15799 return JS_EXCEPTION; 15800 } 15801 func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); 15802 if (JS_IsException(func_obj)) { 15803 /* bfunc has been freed */ 15804 goto fail; 15805 } 15806 name_atom = b->func_name; 15807 if (name_atom == JS_ATOM_NULL) 15808 name_atom = JS_ATOM_empty_string; 15809 js_function_set_properties(ctx, func_obj, name_atom, 15810 b->defined_arg_count); 15811 15812 if (b->func_kind & JS_FUNC_GENERATOR) { 15813 JSValue proto; 15814 int proto_class_id; 15815 /* generators have a prototype field which is used as 15816 prototype for the generator object */ 15817 if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) 15818 proto_class_id = JS_CLASS_ASYNC_GENERATOR; 15819 else 15820 proto_class_id = JS_CLASS_GENERATOR; 15821 proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); 15822 if (JS_IsException(proto)) 15823 goto fail; 15824 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, 15825 JS_PROP_WRITABLE); 15826 } else if (b->has_prototype) { 15827 /* add the 'prototype' property: delay instantiation to avoid 15828 creating cycles for every javascript function. The prototype 15829 object is created on the fly when first accessed */ 15830 JS_SetConstructorBit(ctx, func_obj, TRUE); 15831 JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, 15832 JS_AUTOINIT_ID_PROTOTYPE, NULL, 15833 JS_PROP_WRITABLE); 15834 } 15835 return func_obj; 15836 fail: 15837 /* bfunc is freed when func_obj is freed */ 15838 JS_FreeValue(ctx, func_obj); 15839 return JS_EXCEPTION; 15840 } 15841 15842 #define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) 15843 15844 static int js_op_define_class(JSContext *ctx, JSValue *sp, 15845 JSAtom class_name, int class_flags, 15846 JSVarRef **cur_var_refs, 15847 JSStackFrame *sf, BOOL is_computed_name) 15848 { 15849 JSValue bfunc, parent_class, proto = JS_UNDEFINED; 15850 JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; 15851 JSFunctionBytecode *b; 15852 15853 parent_class = sp[-2]; 15854 bfunc = sp[-1]; 15855 15856 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { 15857 if (JS_IsNull(parent_class)) { 15858 parent_proto = JS_NULL; 15859 parent_class = JS_DupValue(ctx, ctx->function_proto); 15860 } else { 15861 if (!JS_IsConstructor(ctx, parent_class)) { 15862 JS_ThrowTypeError(ctx, "parent class must be constructor"); 15863 goto fail; 15864 } 15865 parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); 15866 if (JS_IsException(parent_proto)) 15867 goto fail; 15868 if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { 15869 JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); 15870 goto fail; 15871 } 15872 } 15873 } else { 15874 /* parent_class is JS_UNDEFINED in this case */ 15875 parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]); 15876 parent_class = JS_DupValue(ctx, ctx->function_proto); 15877 } 15878 proto = JS_NewObjectProto(ctx, parent_proto); 15879 if (JS_IsException(proto)) 15880 goto fail; 15881 15882 b = JS_VALUE_GET_PTR(bfunc); 15883 assert(b->func_kind == JS_FUNC_NORMAL); 15884 ctor = JS_NewObjectProtoClass(ctx, parent_class, 15885 JS_CLASS_BYTECODE_FUNCTION); 15886 if (JS_IsException(ctor)) 15887 goto fail; 15888 ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); 15889 bfunc = JS_UNDEFINED; 15890 if (JS_IsException(ctor)) 15891 goto fail; 15892 js_method_set_home_object(ctx, ctor, proto); 15893 JS_SetConstructorBit(ctx, ctor, TRUE); 15894 15895 JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, 15896 JS_NewInt32(ctx, b->defined_arg_count), 15897 JS_PROP_CONFIGURABLE); 15898 15899 if (is_computed_name) { 15900 if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], 15901 JS_PROP_CONFIGURABLE) < 0) 15902 goto fail; 15903 } else { 15904 if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) 15905 goto fail; 15906 } 15907 15908 /* the constructor property must be first. It can be overriden by 15909 computed property names */ 15910 if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, 15911 JS_DupValue(ctx, ctor), 15912 JS_PROP_CONFIGURABLE | 15913 JS_PROP_WRITABLE | JS_PROP_THROW) < 0) 15914 goto fail; 15915 /* set the prototype property */ 15916 if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, 15917 JS_DupValue(ctx, proto), JS_PROP_THROW) < 0) 15918 goto fail; 15919 set_cycle_flag(ctx, ctor); 15920 set_cycle_flag(ctx, proto); 15921 15922 JS_FreeValue(ctx, parent_proto); 15923 JS_FreeValue(ctx, parent_class); 15924 15925 sp[-2] = ctor; 15926 sp[-1] = proto; 15927 return 0; 15928 fail: 15929 JS_FreeValue(ctx, parent_class); 15930 JS_FreeValue(ctx, parent_proto); 15931 JS_FreeValue(ctx, bfunc); 15932 JS_FreeValue(ctx, proto); 15933 JS_FreeValue(ctx, ctor); 15934 sp[-2] = JS_UNDEFINED; 15935 sp[-1] = JS_UNDEFINED; 15936 return -1; 15937 } 15938 15939 static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) 15940 { 15941 struct list_head *el, *el1; 15942 JSVarRef *var_ref; 15943 int var_idx; 15944 15945 list_for_each_safe(el, el1, &sf->var_ref_list) { 15946 var_ref = list_entry(el, JSVarRef, var_ref_link); 15947 /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */ 15948 if (var_ref->async_func) 15949 async_func_free(rt, var_ref->async_func); 15950 var_idx = var_ref->var_idx; 15951 if (var_ref->is_arg) 15952 var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); 15953 else 15954 var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]); 15955 var_ref->pvalue = &var_ref->value; 15956 /* the reference is no longer to a local variable */ 15957 var_ref->is_detached = TRUE; 15958 } 15959 } 15960 15961 static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) 15962 { 15963 struct list_head *el, *el1; 15964 JSVarRef *var_ref; 15965 int var_idx = idx; 15966 15967 list_for_each_safe(el, el1, &sf->var_ref_list) { 15968 var_ref = list_entry(el, JSVarRef, var_ref_link); 15969 if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { 15970 list_del(&var_ref->var_ref_link); 15971 if (var_ref->async_func) 15972 async_func_free(ctx->rt, var_ref->async_func); 15973 var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]); 15974 var_ref->pvalue = &var_ref->value; 15975 /* the reference is no longer to a local variable */ 15976 var_ref->is_detached = TRUE; 15977 } 15978 } 15979 } 15980 15981 #define JS_CALL_FLAG_COPY_ARGV (1 << 1) 15982 #define JS_CALL_FLAG_GENERATOR (1 << 2) 15983 15984 static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, 15985 JSValueConst this_obj, 15986 int argc, JSValueConst *argv, int flags) 15987 { 15988 JSRuntime *rt = ctx->rt; 15989 JSCFunctionType func; 15990 JSObject *p; 15991 JSStackFrame sf_s, *sf = &sf_s, *prev_sf; 15992 JSValue ret_val; 15993 JSValueConst *arg_buf; 15994 int arg_count, i; 15995 JSCFunctionEnum cproto; 15996 15997 p = JS_VALUE_GET_OBJ(func_obj); 15998 cproto = p->u.cfunc.cproto; 15999 arg_count = p->u.cfunc.length; 16000 16001 /* better to always check stack overflow */ 16002 if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) 16003 return JS_ThrowStackOverflow(ctx); 16004 16005 prev_sf = rt->current_stack_frame; 16006 sf->prev_frame = prev_sf; 16007 rt->current_stack_frame = sf; 16008 ctx = p->u.cfunc.realm; /* change the current realm */ 16009 16010 #ifdef CONFIG_BIGNUM 16011 /* we only propagate the bignum mode as some runtime functions 16012 test it */ 16013 if (prev_sf) 16014 sf->js_mode = prev_sf->js_mode & JS_MODE_MATH; 16015 else 16016 sf->js_mode = 0; 16017 #else 16018 sf->js_mode = 0; 16019 #endif 16020 sf->cur_func = (JSValue)func_obj; 16021 sf->arg_count = argc; 16022 arg_buf = argv; 16023 16024 if (unlikely(argc < arg_count)) { 16025 /* ensure that at least argc_count arguments are readable */ 16026 arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); 16027 for(i = 0; i < argc; i++) 16028 arg_buf[i] = argv[i]; 16029 for(i = argc; i < arg_count; i++) 16030 arg_buf[i] = JS_UNDEFINED; 16031 sf->arg_count = arg_count; 16032 } 16033 sf->arg_buf = (JSValue*)arg_buf; 16034 16035 func = p->u.cfunc.c_function; 16036 switch(cproto) { 16037 case JS_CFUNC_constructor: 16038 case JS_CFUNC_constructor_or_func: 16039 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { 16040 if (cproto == JS_CFUNC_constructor) { 16041 not_a_constructor: 16042 ret_val = JS_ThrowTypeError(ctx, "must be called with new"); 16043 break; 16044 } else { 16045 this_obj = JS_UNDEFINED; 16046 } 16047 } 16048 /* here this_obj is new_target */ 16049 /* fall thru */ 16050 case JS_CFUNC_generic: 16051 ret_val = func.generic(ctx, this_obj, argc, arg_buf); 16052 break; 16053 case JS_CFUNC_constructor_magic: 16054 case JS_CFUNC_constructor_or_func_magic: 16055 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { 16056 if (cproto == JS_CFUNC_constructor_magic) { 16057 goto not_a_constructor; 16058 } else { 16059 this_obj = JS_UNDEFINED; 16060 } 16061 } 16062 /* fall thru */ 16063 case JS_CFUNC_generic_magic: 16064 ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, 16065 p->u.cfunc.magic); 16066 break; 16067 case JS_CFUNC_getter: 16068 ret_val = func.getter(ctx, this_obj); 16069 break; 16070 case JS_CFUNC_setter: 16071 ret_val = func.setter(ctx, this_obj, arg_buf[0]); 16072 break; 16073 case JS_CFUNC_getter_magic: 16074 ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); 16075 break; 16076 case JS_CFUNC_setter_magic: 16077 ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); 16078 break; 16079 case JS_CFUNC_f_f: 16080 { 16081 double d1; 16082 16083 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { 16084 ret_val = JS_EXCEPTION; 16085 break; 16086 } 16087 ret_val = JS_NewFloat64(ctx, func.f_f(d1)); 16088 } 16089 break; 16090 case JS_CFUNC_f_f_f: 16091 { 16092 double d1, d2; 16093 16094 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { 16095 ret_val = JS_EXCEPTION; 16096 break; 16097 } 16098 if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { 16099 ret_val = JS_EXCEPTION; 16100 break; 16101 } 16102 ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2)); 16103 } 16104 break; 16105 case JS_CFUNC_iterator_next: 16106 { 16107 int done; 16108 ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, 16109 &done, p->u.cfunc.magic); 16110 if (!JS_IsException(ret_val) && done != 2) { 16111 ret_val = js_create_iterator_result(ctx, ret_val, done); 16112 } 16113 } 16114 break; 16115 default: 16116 abort(); 16117 } 16118 16119 rt->current_stack_frame = sf->prev_frame; 16120 return ret_val; 16121 } 16122 16123 static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj, 16124 JSValueConst this_obj, 16125 int argc, JSValueConst *argv, int flags) 16126 { 16127 JSObject *p; 16128 JSBoundFunction *bf; 16129 JSValueConst *arg_buf, new_target; 16130 int arg_count, i; 16131 16132 p = JS_VALUE_GET_OBJ(func_obj); 16133 bf = p->u.bound_function; 16134 arg_count = bf->argc + argc; 16135 if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) 16136 return JS_ThrowStackOverflow(ctx); 16137 arg_buf = alloca(sizeof(JSValue) * arg_count); 16138 for(i = 0; i < bf->argc; i++) { 16139 arg_buf[i] = bf->argv[i]; 16140 } 16141 for(i = 0; i < argc; i++) { 16142 arg_buf[bf->argc + i] = argv[i]; 16143 } 16144 if (flags & JS_CALL_FLAG_CONSTRUCTOR) { 16145 new_target = this_obj; 16146 if (js_same_value(ctx, func_obj, new_target)) 16147 new_target = bf->func_obj; 16148 return JS_CallConstructor2(ctx, bf->func_obj, new_target, 16149 arg_count, arg_buf); 16150 } else { 16151 return JS_Call(ctx, bf->func_obj, bf->this_val, 16152 arg_count, arg_buf); 16153 } 16154 } 16155 16156 /* argument of OP_special_object */ 16157 typedef enum { 16158 OP_SPECIAL_OBJECT_ARGUMENTS, 16159 OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, 16160 OP_SPECIAL_OBJECT_THIS_FUNC, 16161 OP_SPECIAL_OBJECT_NEW_TARGET, 16162 OP_SPECIAL_OBJECT_HOME_OBJECT, 16163 OP_SPECIAL_OBJECT_VAR_OBJECT, 16164 OP_SPECIAL_OBJECT_IMPORT_META, 16165 } OPSpecialObjectEnum; 16166 16167 #define FUNC_RET_AWAIT 0 16168 #define FUNC_RET_YIELD 1 16169 #define FUNC_RET_YIELD_STAR 2 16170 #define FUNC_RET_INITIAL_YIELD 3 16171 16172 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ 16173 static JSValue __JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 16174 JSValueConst this_obj, JSValueConst new_target, 16175 int argc, JSValue *argv, int flags) 16176 { 16177 JSRuntime *rt = caller_ctx->rt; 16178 JSContext *ctx; 16179 JSObject *p; 16180 JSFunctionBytecode *b; 16181 JSStackFrame sf_s, *sf = &sf_s; 16182 const uint8_t *pc; 16183 int opcode, arg_allocated_size, i; 16184 JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; 16185 JSVarRef **var_refs; 16186 size_t alloca_size; 16187 16188 #if !DIRECT_DISPATCH 16189 #define SWITCH(pc) switch (opcode = *pc++) 16190 #define CASE(op) case op 16191 #define DEFAULT default 16192 #define BREAK break 16193 #else 16194 static const void * const dispatch_table[256] = { 16195 #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, 16196 #if SHORT_OPCODES 16197 #define def(id, size, n_pop, n_push, f) 16198 #else 16199 #define def(id, size, n_pop, n_push, f) && case_default, 16200 #endif 16201 #include "quickjs-opcode.h" 16202 [ OP_COUNT ... 255 ] = &&case_default 16203 }; 16204 #define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; 16205 #define CASE(op) case_ ## op 16206 #define DEFAULT case_default 16207 #define BREAK SWITCH(pc) 16208 #endif 16209 16210 if (js_poll_interrupts(caller_ctx)) 16211 return JS_EXCEPTION; 16212 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { 16213 if (flags & JS_CALL_FLAG_GENERATOR) { 16214 JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); 16215 /* func_obj get contains a pointer to JSFuncAsyncState */ 16216 /* the stack frame is already allocated */ 16217 sf = &s->frame; 16218 p = JS_VALUE_GET_OBJ(sf->cur_func); 16219 b = p->u.func.function_bytecode; 16220 ctx = b->realm; 16221 var_refs = p->u.func.var_refs; 16222 local_buf = arg_buf = sf->arg_buf; 16223 var_buf = sf->var_buf; 16224 stack_buf = sf->var_buf + b->var_count; 16225 sp = sf->cur_sp; 16226 sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ 16227 pc = sf->cur_pc; 16228 sf->prev_frame = rt->current_stack_frame; 16229 rt->current_stack_frame = sf; 16230 if (s->throw_flag) 16231 goto exception; 16232 else 16233 goto restart; 16234 } else { 16235 goto not_a_function; 16236 } 16237 } 16238 p = JS_VALUE_GET_OBJ(func_obj); 16239 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { 16240 JSClassCall *call_func; 16241 call_func = rt->class_array[p->class_id].call; 16242 if (!call_func) { 16243 not_a_function: 16244 return JS_ThrowTypeError(caller_ctx, "not a function"); 16245 } 16246 return call_func(caller_ctx, func_obj, this_obj, argc, 16247 (JSValueConst *)argv, flags); 16248 } 16249 b = p->u.func.function_bytecode; 16250 16251 if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { 16252 arg_allocated_size = b->arg_count; 16253 } else { 16254 arg_allocated_size = 0; 16255 } 16256 16257 alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + 16258 b->stack_size); 16259 if (js_check_stack_overflow(rt, alloca_size)) 16260 return JS_ThrowStackOverflow(caller_ctx); 16261 16262 sf->js_mode = b->js_mode; 16263 arg_buf = argv; 16264 sf->arg_count = argc; 16265 sf->cur_func = (JSValue)func_obj; 16266 init_list_head(&sf->var_ref_list); 16267 var_refs = p->u.func.var_refs; 16268 16269 local_buf = alloca(alloca_size); 16270 if (unlikely(arg_allocated_size)) { 16271 int n = min_int(argc, b->arg_count); 16272 arg_buf = local_buf; 16273 for(i = 0; i < n; i++) 16274 arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); 16275 for(; i < b->arg_count; i++) 16276 arg_buf[i] = JS_UNDEFINED; 16277 sf->arg_count = b->arg_count; 16278 } 16279 var_buf = local_buf + arg_allocated_size; 16280 sf->var_buf = var_buf; 16281 sf->arg_buf = arg_buf; 16282 16283 for(i = 0; i < b->var_count; i++) 16284 var_buf[i] = JS_UNDEFINED; 16285 16286 stack_buf = var_buf + b->var_count; 16287 sp = stack_buf; 16288 pc = b->byte_code_buf; 16289 sf->prev_frame = rt->current_stack_frame; 16290 rt->current_stack_frame = sf; 16291 ctx = b->realm; /* set the current realm */ 16292 16293 restart: 16294 for(;;) { 16295 int call_argc; 16296 JSValue *call_argv; 16297 16298 SWITCH(pc) { 16299 CASE(OP_push_i32): 16300 *sp++ = JS_NewInt32(ctx, get_u32(pc)); 16301 pc += 4; 16302 BREAK; 16303 CASE(OP_push_const): 16304 *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]); 16305 pc += 4; 16306 BREAK; 16307 #if SHORT_OPCODES 16308 CASE(OP_push_minus1): 16309 CASE(OP_push_0): 16310 CASE(OP_push_1): 16311 CASE(OP_push_2): 16312 CASE(OP_push_3): 16313 CASE(OP_push_4): 16314 CASE(OP_push_5): 16315 CASE(OP_push_6): 16316 CASE(OP_push_7): 16317 *sp++ = JS_NewInt32(ctx, opcode - OP_push_0); 16318 BREAK; 16319 CASE(OP_push_i8): 16320 *sp++ = JS_NewInt32(ctx, get_i8(pc)); 16321 pc += 1; 16322 BREAK; 16323 CASE(OP_push_i16): 16324 *sp++ = JS_NewInt32(ctx, get_i16(pc)); 16325 pc += 2; 16326 BREAK; 16327 CASE(OP_push_const8): 16328 *sp++ = JS_DupValue(ctx, b->cpool[*pc++]); 16329 BREAK; 16330 CASE(OP_fclosure8): 16331 *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf); 16332 if (unlikely(JS_IsException(sp[-1]))) 16333 goto exception; 16334 BREAK; 16335 CASE(OP_push_empty_string): 16336 *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); 16337 BREAK; 16338 CASE(OP_get_length): 16339 { 16340 JSValue val; 16341 16342 val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); 16343 if (unlikely(JS_IsException(val))) 16344 goto exception; 16345 JS_FreeValue(ctx, sp[-1]); 16346 sp[-1] = val; 16347 } 16348 BREAK; 16349 #endif 16350 CASE(OP_push_atom_value): 16351 *sp++ = JS_AtomToValue(ctx, get_u32(pc)); 16352 pc += 4; 16353 BREAK; 16354 CASE(OP_undefined): 16355 *sp++ = JS_UNDEFINED; 16356 BREAK; 16357 CASE(OP_null): 16358 *sp++ = JS_NULL; 16359 BREAK; 16360 CASE(OP_push_this): 16361 /* OP_push_this is only called at the start of a function */ 16362 { 16363 JSValue val; 16364 if (!(b->js_mode & JS_MODE_STRICT)) { 16365 uint32_t tag = JS_VALUE_GET_TAG(this_obj); 16366 if (likely(tag == JS_TAG_OBJECT)) 16367 goto normal_this; 16368 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { 16369 val = JS_DupValue(ctx, ctx->global_obj); 16370 } else { 16371 val = JS_ToObject(ctx, this_obj); 16372 if (JS_IsException(val)) 16373 goto exception; 16374 } 16375 } else { 16376 normal_this: 16377 val = JS_DupValue(ctx, this_obj); 16378 } 16379 *sp++ = val; 16380 } 16381 BREAK; 16382 CASE(OP_push_false): 16383 *sp++ = JS_FALSE; 16384 BREAK; 16385 CASE(OP_push_true): 16386 *sp++ = JS_TRUE; 16387 BREAK; 16388 CASE(OP_object): 16389 *sp++ = JS_NewObject(ctx); 16390 if (unlikely(JS_IsException(sp[-1]))) 16391 goto exception; 16392 BREAK; 16393 CASE(OP_special_object): 16394 { 16395 int arg = *pc++; 16396 switch(arg) { 16397 case OP_SPECIAL_OBJECT_ARGUMENTS: 16398 *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); 16399 if (unlikely(JS_IsException(sp[-1]))) 16400 goto exception; 16401 break; 16402 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: 16403 *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, 16404 sf, min_int(argc, b->arg_count)); 16405 if (unlikely(JS_IsException(sp[-1]))) 16406 goto exception; 16407 break; 16408 case OP_SPECIAL_OBJECT_THIS_FUNC: 16409 *sp++ = JS_DupValue(ctx, sf->cur_func); 16410 break; 16411 case OP_SPECIAL_OBJECT_NEW_TARGET: 16412 *sp++ = JS_DupValue(ctx, new_target); 16413 break; 16414 case OP_SPECIAL_OBJECT_HOME_OBJECT: 16415 { 16416 JSObject *p1; 16417 p1 = p->u.func.home_object; 16418 if (unlikely(!p1)) 16419 *sp++ = JS_UNDEFINED; 16420 else 16421 *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); 16422 } 16423 break; 16424 case OP_SPECIAL_OBJECT_VAR_OBJECT: 16425 *sp++ = JS_NewObjectProto(ctx, JS_NULL); 16426 if (unlikely(JS_IsException(sp[-1]))) 16427 goto exception; 16428 break; 16429 case OP_SPECIAL_OBJECT_IMPORT_META: 16430 *sp++ = js_import_meta(ctx); 16431 if (unlikely(JS_IsException(sp[-1]))) 16432 goto exception; 16433 break; 16434 default: 16435 abort(); 16436 } 16437 } 16438 BREAK; 16439 CASE(OP_rest): 16440 { 16441 int first = get_u16(pc); 16442 pc += 2; 16443 *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); 16444 if (unlikely(JS_IsException(sp[-1]))) 16445 goto exception; 16446 } 16447 BREAK; 16448 16449 CASE(OP_drop): 16450 JS_FreeValue(ctx, sp[-1]); 16451 sp--; 16452 BREAK; 16453 CASE(OP_nip): 16454 JS_FreeValue(ctx, sp[-2]); 16455 sp[-2] = sp[-1]; 16456 sp--; 16457 BREAK; 16458 CASE(OP_nip1): /* a b c -> b c */ 16459 JS_FreeValue(ctx, sp[-3]); 16460 sp[-3] = sp[-2]; 16461 sp[-2] = sp[-1]; 16462 sp--; 16463 BREAK; 16464 CASE(OP_dup): 16465 sp[0] = JS_DupValue(ctx, sp[-1]); 16466 sp++; 16467 BREAK; 16468 CASE(OP_dup2): /* a b -> a b a b */ 16469 sp[0] = JS_DupValue(ctx, sp[-2]); 16470 sp[1] = JS_DupValue(ctx, sp[-1]); 16471 sp += 2; 16472 BREAK; 16473 CASE(OP_dup3): /* a b c -> a b c a b c */ 16474 sp[0] = JS_DupValue(ctx, sp[-3]); 16475 sp[1] = JS_DupValue(ctx, sp[-2]); 16476 sp[2] = JS_DupValue(ctx, sp[-1]); 16477 sp += 3; 16478 BREAK; 16479 CASE(OP_dup1): /* a b -> a a b */ 16480 sp[0] = sp[-1]; 16481 sp[-1] = JS_DupValue(ctx, sp[-2]); 16482 sp++; 16483 BREAK; 16484 CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ 16485 sp[0] = sp[-1]; 16486 sp[-1] = sp[-2]; 16487 sp[-2] = JS_DupValue(ctx, sp[0]); 16488 sp++; 16489 BREAK; 16490 CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ 16491 sp[0] = sp[-1]; 16492 sp[-1] = sp[-2]; 16493 sp[-2] = sp[-3]; 16494 sp[-3] = JS_DupValue(ctx, sp[0]); 16495 sp++; 16496 BREAK; 16497 CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ 16498 sp[0] = sp[-1]; 16499 sp[-1] = sp[-2]; 16500 sp[-2] = sp[-3]; 16501 sp[-3] = sp[-4]; 16502 sp[-4] = JS_DupValue(ctx, sp[0]); 16503 sp++; 16504 BREAK; 16505 CASE(OP_perm3): /* obj a b -> a obj b (213) */ 16506 { 16507 JSValue tmp; 16508 tmp = sp[-2]; 16509 sp[-2] = sp[-3]; 16510 sp[-3] = tmp; 16511 } 16512 BREAK; 16513 CASE(OP_rot3l): /* x a b -> a b x (231) */ 16514 { 16515 JSValue tmp; 16516 tmp = sp[-3]; 16517 sp[-3] = sp[-2]; 16518 sp[-2] = sp[-1]; 16519 sp[-1] = tmp; 16520 } 16521 BREAK; 16522 CASE(OP_rot4l): /* x a b c -> a b c x */ 16523 { 16524 JSValue tmp; 16525 tmp = sp[-4]; 16526 sp[-4] = sp[-3]; 16527 sp[-3] = sp[-2]; 16528 sp[-2] = sp[-1]; 16529 sp[-1] = tmp; 16530 } 16531 BREAK; 16532 CASE(OP_rot5l): /* x a b c d -> a b c d x */ 16533 { 16534 JSValue tmp; 16535 tmp = sp[-5]; 16536 sp[-5] = sp[-4]; 16537 sp[-4] = sp[-3]; 16538 sp[-3] = sp[-2]; 16539 sp[-2] = sp[-1]; 16540 sp[-1] = tmp; 16541 } 16542 BREAK; 16543 CASE(OP_rot3r): /* a b x -> x a b (312) */ 16544 { 16545 JSValue tmp; 16546 tmp = sp[-1]; 16547 sp[-1] = sp[-2]; 16548 sp[-2] = sp[-3]; 16549 sp[-3] = tmp; 16550 } 16551 BREAK; 16552 CASE(OP_perm4): /* obj prop a b -> a obj prop b */ 16553 { 16554 JSValue tmp; 16555 tmp = sp[-2]; 16556 sp[-2] = sp[-3]; 16557 sp[-3] = sp[-4]; 16558 sp[-4] = tmp; 16559 } 16560 BREAK; 16561 CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ 16562 { 16563 JSValue tmp; 16564 tmp = sp[-2]; 16565 sp[-2] = sp[-3]; 16566 sp[-3] = sp[-4]; 16567 sp[-4] = sp[-5]; 16568 sp[-5] = tmp; 16569 } 16570 BREAK; 16571 CASE(OP_swap): /* a b -> b a */ 16572 { 16573 JSValue tmp; 16574 tmp = sp[-2]; 16575 sp[-2] = sp[-1]; 16576 sp[-1] = tmp; 16577 } 16578 BREAK; 16579 CASE(OP_swap2): /* a b c d -> c d a b */ 16580 { 16581 JSValue tmp1, tmp2; 16582 tmp1 = sp[-4]; 16583 tmp2 = sp[-3]; 16584 sp[-4] = sp[-2]; 16585 sp[-3] = sp[-1]; 16586 sp[-2] = tmp1; 16587 sp[-1] = tmp2; 16588 } 16589 BREAK; 16590 16591 CASE(OP_fclosure): 16592 { 16593 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]); 16594 pc += 4; 16595 *sp++ = js_closure(ctx, bfunc, var_refs, sf); 16596 if (unlikely(JS_IsException(sp[-1]))) 16597 goto exception; 16598 } 16599 BREAK; 16600 #if SHORT_OPCODES 16601 CASE(OP_call0): 16602 CASE(OP_call1): 16603 CASE(OP_call2): 16604 CASE(OP_call3): 16605 call_argc = opcode - OP_call0; 16606 goto has_call_argc; 16607 #endif 16608 CASE(OP_call): 16609 CASE(OP_tail_call): 16610 { 16611 call_argc = get_u16(pc); 16612 pc += 2; 16613 goto has_call_argc; 16614 has_call_argc: 16615 call_argv = sp - call_argc; 16616 sf->cur_pc = pc; 16617 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, 16618 JS_UNDEFINED, call_argc, call_argv, 0); 16619 if (unlikely(JS_IsException(ret_val))) 16620 goto exception; 16621 if (opcode == OP_tail_call) 16622 goto done; 16623 for(i = -1; i < call_argc; i++) 16624 JS_FreeValue(ctx, call_argv[i]); 16625 sp -= call_argc + 1; 16626 *sp++ = ret_val; 16627 } 16628 BREAK; 16629 CASE(OP_call_constructor): 16630 { 16631 call_argc = get_u16(pc); 16632 pc += 2; 16633 call_argv = sp - call_argc; 16634 sf->cur_pc = pc; 16635 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], 16636 call_argv[-1], 16637 call_argc, call_argv, 0); 16638 if (unlikely(JS_IsException(ret_val))) 16639 goto exception; 16640 for(i = -2; i < call_argc; i++) 16641 JS_FreeValue(ctx, call_argv[i]); 16642 sp -= call_argc + 2; 16643 *sp++ = ret_val; 16644 } 16645 BREAK; 16646 CASE(OP_call_method): 16647 CASE(OP_tail_call_method): 16648 { 16649 call_argc = get_u16(pc); 16650 pc += 2; 16651 call_argv = sp - call_argc; 16652 sf->cur_pc = pc; 16653 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], 16654 JS_UNDEFINED, call_argc, call_argv, 0); 16655 if (unlikely(JS_IsException(ret_val))) 16656 goto exception; 16657 if (opcode == OP_tail_call_method) 16658 goto done; 16659 for(i = -2; i < call_argc; i++) 16660 JS_FreeValue(ctx, call_argv[i]); 16661 sp -= call_argc + 2; 16662 *sp++ = ret_val; 16663 } 16664 BREAK; 16665 CASE(OP_array_from): 16666 { 16667 int i, ret; 16668 16669 call_argc = get_u16(pc); 16670 pc += 2; 16671 ret_val = JS_NewArray(ctx); 16672 if (unlikely(JS_IsException(ret_val))) 16673 goto exception; 16674 call_argv = sp - call_argc; 16675 for(i = 0; i < call_argc; i++) { 16676 ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], 16677 JS_PROP_C_W_E | JS_PROP_THROW); 16678 call_argv[i] = JS_UNDEFINED; 16679 if (ret < 0) { 16680 JS_FreeValue(ctx, ret_val); 16681 goto exception; 16682 } 16683 } 16684 sp -= call_argc; 16685 *sp++ = ret_val; 16686 } 16687 BREAK; 16688 16689 CASE(OP_apply): 16690 { 16691 int magic; 16692 magic = get_u16(pc); 16693 pc += 2; 16694 16695 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); 16696 if (unlikely(JS_IsException(ret_val))) 16697 goto exception; 16698 JS_FreeValue(ctx, sp[-3]); 16699 JS_FreeValue(ctx, sp[-2]); 16700 JS_FreeValue(ctx, sp[-1]); 16701 sp -= 3; 16702 *sp++ = ret_val; 16703 } 16704 BREAK; 16705 CASE(OP_return): 16706 ret_val = *--sp; 16707 goto done; 16708 CASE(OP_return_undef): 16709 ret_val = JS_UNDEFINED; 16710 goto done; 16711 16712 CASE(OP_check_ctor_return): 16713 /* return TRUE if 'this' should be returned */ 16714 if (!JS_IsObject(sp[-1])) { 16715 if (!JS_IsUndefined(sp[-1])) { 16716 JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); 16717 goto exception; 16718 } 16719 sp[0] = JS_TRUE; 16720 } else { 16721 sp[0] = JS_FALSE; 16722 } 16723 sp++; 16724 BREAK; 16725 CASE(OP_check_ctor): 16726 if (JS_IsUndefined(new_target)) { 16727 JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); 16728 goto exception; 16729 } 16730 BREAK; 16731 CASE(OP_check_brand): 16732 { 16733 int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); 16734 if (ret < 0) 16735 goto exception; 16736 if (!ret) { 16737 JS_ThrowTypeError(ctx, "invalid brand on object"); 16738 goto exception; 16739 } 16740 } 16741 BREAK; 16742 CASE(OP_add_brand): 16743 if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) 16744 goto exception; 16745 JS_FreeValue(ctx, sp[-2]); 16746 JS_FreeValue(ctx, sp[-1]); 16747 sp -= 2; 16748 BREAK; 16749 16750 CASE(OP_throw): 16751 JS_Throw(ctx, *--sp); 16752 goto exception; 16753 16754 CASE(OP_throw_error): 16755 #define JS_THROW_VAR_RO 0 16756 #define JS_THROW_VAR_REDECL 1 16757 #define JS_THROW_VAR_UNINITIALIZED 2 16758 #define JS_THROW_ERROR_DELETE_SUPER 3 16759 #define JS_THROW_ERROR_ITERATOR_THROW 4 16760 { 16761 JSAtom atom; 16762 int type; 16763 atom = get_u32(pc); 16764 type = pc[4]; 16765 pc += 5; 16766 if (type == JS_THROW_VAR_RO) 16767 JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); 16768 else 16769 if (type == JS_THROW_VAR_REDECL) 16770 JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); 16771 else 16772 if (type == JS_THROW_VAR_UNINITIALIZED) 16773 JS_ThrowReferenceErrorUninitialized(ctx, atom); 16774 else 16775 if (type == JS_THROW_ERROR_DELETE_SUPER) 16776 JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); 16777 else 16778 if (type == JS_THROW_ERROR_ITERATOR_THROW) 16779 JS_ThrowTypeError(ctx, "iterator does not have a throw method"); 16780 else 16781 JS_ThrowInternalError(ctx, "invalid throw var type %d", type); 16782 } 16783 goto exception; 16784 16785 CASE(OP_eval): 16786 { 16787 JSValueConst obj; 16788 int scope_idx; 16789 call_argc = get_u16(pc); 16790 scope_idx = get_u16(pc + 2) - 1; 16791 pc += 4; 16792 call_argv = sp - call_argc; 16793 sf->cur_pc = pc; 16794 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { 16795 if (call_argc >= 1) 16796 obj = call_argv[0]; 16797 else 16798 obj = JS_UNDEFINED; 16799 ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, 16800 JS_EVAL_TYPE_DIRECT, scope_idx); 16801 } else { 16802 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, 16803 JS_UNDEFINED, call_argc, call_argv, 0); 16804 } 16805 if (unlikely(JS_IsException(ret_val))) 16806 goto exception; 16807 for(i = -1; i < call_argc; i++) 16808 JS_FreeValue(ctx, call_argv[i]); 16809 sp -= call_argc + 1; 16810 *sp++ = ret_val; 16811 } 16812 BREAK; 16813 /* could merge with OP_apply */ 16814 CASE(OP_apply_eval): 16815 { 16816 int scope_idx; 16817 uint32_t len; 16818 JSValue *tab; 16819 JSValueConst obj; 16820 16821 scope_idx = get_u16(pc) - 1; 16822 pc += 2; 16823 tab = build_arg_list(ctx, &len, sp[-1]); 16824 if (!tab) 16825 goto exception; 16826 if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { 16827 if (len >= 1) 16828 obj = tab[0]; 16829 else 16830 obj = JS_UNDEFINED; 16831 ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, 16832 JS_EVAL_TYPE_DIRECT, scope_idx); 16833 } else { 16834 ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, 16835 (JSValueConst *)tab); 16836 } 16837 free_arg_list(ctx, tab, len); 16838 if (unlikely(JS_IsException(ret_val))) 16839 goto exception; 16840 JS_FreeValue(ctx, sp[-2]); 16841 JS_FreeValue(ctx, sp[-1]); 16842 sp -= 2; 16843 *sp++ = ret_val; 16844 } 16845 BREAK; 16846 16847 CASE(OP_regexp): 16848 { 16849 sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, 16850 sp[-2], sp[-1]); 16851 sp--; 16852 } 16853 BREAK; 16854 16855 CASE(OP_get_super): 16856 { 16857 JSValue proto; 16858 proto = JS_GetPrototype(ctx, sp[-1]); 16859 if (JS_IsException(proto)) 16860 goto exception; 16861 JS_FreeValue(ctx, sp[-1]); 16862 sp[-1] = proto; 16863 } 16864 BREAK; 16865 16866 CASE(OP_import): 16867 { 16868 JSValue val; 16869 val = js_dynamic_import(ctx, sp[-1]); 16870 if (JS_IsException(val)) 16871 goto exception; 16872 JS_FreeValue(ctx, sp[-1]); 16873 sp[-1] = val; 16874 } 16875 BREAK; 16876 16877 CASE(OP_check_var): 16878 { 16879 int ret; 16880 JSAtom atom; 16881 atom = get_u32(pc); 16882 pc += 4; 16883 16884 ret = JS_CheckGlobalVar(ctx, atom); 16885 if (ret < 0) 16886 goto exception; 16887 *sp++ = JS_NewBool(ctx, ret); 16888 } 16889 BREAK; 16890 16891 CASE(OP_get_var_undef): 16892 CASE(OP_get_var): 16893 { 16894 JSValue val; 16895 JSAtom atom; 16896 atom = get_u32(pc); 16897 pc += 4; 16898 16899 val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); 16900 if (unlikely(JS_IsException(val))) 16901 goto exception; 16902 *sp++ = val; 16903 } 16904 BREAK; 16905 16906 CASE(OP_put_var): 16907 CASE(OP_put_var_init): 16908 { 16909 int ret; 16910 JSAtom atom; 16911 atom = get_u32(pc); 16912 pc += 4; 16913 16914 ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); 16915 sp--; 16916 if (unlikely(ret < 0)) 16917 goto exception; 16918 } 16919 BREAK; 16920 16921 CASE(OP_put_var_strict): 16922 { 16923 int ret; 16924 JSAtom atom; 16925 atom = get_u32(pc); 16926 pc += 4; 16927 16928 /* sp[-2] is JS_TRUE or JS_FALSE */ 16929 if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { 16930 JS_ThrowReferenceErrorNotDefined(ctx, atom); 16931 goto exception; 16932 } 16933 ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); 16934 sp -= 2; 16935 if (unlikely(ret < 0)) 16936 goto exception; 16937 } 16938 BREAK; 16939 16940 CASE(OP_check_define_var): 16941 { 16942 JSAtom atom; 16943 int flags; 16944 atom = get_u32(pc); 16945 flags = pc[4]; 16946 pc += 5; 16947 if (JS_CheckDefineGlobalVar(ctx, atom, flags)) 16948 goto exception; 16949 } 16950 BREAK; 16951 CASE(OP_define_var): 16952 { 16953 JSAtom atom; 16954 int flags; 16955 atom = get_u32(pc); 16956 flags = pc[4]; 16957 pc += 5; 16958 if (JS_DefineGlobalVar(ctx, atom, flags)) 16959 goto exception; 16960 } 16961 BREAK; 16962 CASE(OP_define_func): 16963 { 16964 JSAtom atom; 16965 int flags; 16966 atom = get_u32(pc); 16967 flags = pc[4]; 16968 pc += 5; 16969 if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) 16970 goto exception; 16971 JS_FreeValue(ctx, sp[-1]); 16972 sp--; 16973 } 16974 BREAK; 16975 16976 CASE(OP_get_loc): 16977 { 16978 int idx; 16979 idx = get_u16(pc); 16980 pc += 2; 16981 sp[0] = JS_DupValue(ctx, var_buf[idx]); 16982 sp++; 16983 } 16984 BREAK; 16985 CASE(OP_put_loc): 16986 { 16987 int idx; 16988 idx = get_u16(pc); 16989 pc += 2; 16990 set_value(ctx, &var_buf[idx], sp[-1]); 16991 sp--; 16992 } 16993 BREAK; 16994 CASE(OP_set_loc): 16995 { 16996 int idx; 16997 idx = get_u16(pc); 16998 pc += 2; 16999 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1])); 17000 } 17001 BREAK; 17002 CASE(OP_get_arg): 17003 { 17004 int idx; 17005 idx = get_u16(pc); 17006 pc += 2; 17007 sp[0] = JS_DupValue(ctx, arg_buf[idx]); 17008 sp++; 17009 } 17010 BREAK; 17011 CASE(OP_put_arg): 17012 { 17013 int idx; 17014 idx = get_u16(pc); 17015 pc += 2; 17016 set_value(ctx, &arg_buf[idx], sp[-1]); 17017 sp--; 17018 } 17019 BREAK; 17020 CASE(OP_set_arg): 17021 { 17022 int idx; 17023 idx = get_u16(pc); 17024 pc += 2; 17025 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1])); 17026 } 17027 BREAK; 17028 17029 #if SHORT_OPCODES 17030 CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK; 17031 CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; 17032 CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK; 17033 17034 CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK; 17035 CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK; 17036 CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK; 17037 CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK; 17038 CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; 17039 CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; 17040 CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; 17041 CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; 17042 CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; 17043 CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; 17044 CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; 17045 CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; 17046 CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK; 17047 CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK; 17048 CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK; 17049 CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK; 17050 CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; 17051 CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; 17052 CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; 17053 CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; 17054 CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK; 17055 CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK; 17056 CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK; 17057 CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK; 17058 CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK; 17059 CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK; 17060 CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK; 17061 CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK; 17062 CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; 17063 CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; 17064 CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; 17065 CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; 17066 CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 17067 CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 17068 CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 17069 CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK; 17070 #endif 17071 17072 CASE(OP_get_var_ref): 17073 { 17074 int idx; 17075 JSValue val; 17076 idx = get_u16(pc); 17077 pc += 2; 17078 val = *var_refs[idx]->pvalue; 17079 sp[0] = JS_DupValue(ctx, val); 17080 sp++; 17081 } 17082 BREAK; 17083 CASE(OP_put_var_ref): 17084 { 17085 int idx; 17086 idx = get_u16(pc); 17087 pc += 2; 17088 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 17089 sp--; 17090 } 17091 BREAK; 17092 CASE(OP_set_var_ref): 17093 { 17094 int idx; 17095 idx = get_u16(pc); 17096 pc += 2; 17097 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1])); 17098 } 17099 BREAK; 17100 CASE(OP_get_var_ref_check): 17101 { 17102 int idx; 17103 JSValue val; 17104 idx = get_u16(pc); 17105 pc += 2; 17106 val = *var_refs[idx]->pvalue; 17107 if (unlikely(JS_IsUninitialized(val))) { 17108 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 17109 goto exception; 17110 } 17111 sp[0] = JS_DupValue(ctx, val); 17112 sp++; 17113 } 17114 BREAK; 17115 CASE(OP_put_var_ref_check): 17116 { 17117 int idx; 17118 idx = get_u16(pc); 17119 pc += 2; 17120 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { 17121 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 17122 goto exception; 17123 } 17124 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 17125 sp--; 17126 } 17127 BREAK; 17128 CASE(OP_put_var_ref_check_init): 17129 { 17130 int idx; 17131 idx = get_u16(pc); 17132 pc += 2; 17133 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { 17134 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); 17135 goto exception; 17136 } 17137 set_value(ctx, var_refs[idx]->pvalue, sp[-1]); 17138 sp--; 17139 } 17140 BREAK; 17141 CASE(OP_set_loc_uninitialized): 17142 { 17143 int idx; 17144 idx = get_u16(pc); 17145 pc += 2; 17146 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); 17147 } 17148 BREAK; 17149 CASE(OP_get_loc_check): 17150 { 17151 int idx; 17152 idx = get_u16(pc); 17153 pc += 2; 17154 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 17155 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); 17156 goto exception; 17157 } 17158 sp[0] = JS_DupValue(ctx, var_buf[idx]); 17159 sp++; 17160 } 17161 BREAK; 17162 CASE(OP_get_loc_checkthis): 17163 { 17164 int idx; 17165 idx = get_u16(pc); 17166 pc += 2; 17167 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 17168 JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE); 17169 goto exception; 17170 } 17171 sp[0] = JS_DupValue(ctx, var_buf[idx]); 17172 sp++; 17173 } 17174 BREAK; 17175 CASE(OP_put_loc_check): 17176 { 17177 int idx; 17178 idx = get_u16(pc); 17179 pc += 2; 17180 if (unlikely(JS_IsUninitialized(var_buf[idx]))) { 17181 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE); 17182 goto exception; 17183 } 17184 set_value(ctx, &var_buf[idx], sp[-1]); 17185 sp--; 17186 } 17187 BREAK; 17188 CASE(OP_put_loc_check_init): 17189 { 17190 int idx; 17191 idx = get_u16(pc); 17192 pc += 2; 17193 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { 17194 JS_ThrowReferenceError(ctx, "'this' can be initialized only once"); 17195 goto exception; 17196 } 17197 set_value(ctx, &var_buf[idx], sp[-1]); 17198 sp--; 17199 } 17200 BREAK; 17201 CASE(OP_close_loc): 17202 { 17203 int idx; 17204 idx = get_u16(pc); 17205 pc += 2; 17206 close_lexical_var(ctx, sf, idx, FALSE); 17207 } 17208 BREAK; 17209 17210 CASE(OP_make_loc_ref): 17211 CASE(OP_make_arg_ref): 17212 CASE(OP_make_var_ref_ref): 17213 { 17214 JSVarRef *var_ref; 17215 JSProperty *pr; 17216 JSAtom atom; 17217 int idx; 17218 atom = get_u32(pc); 17219 idx = get_u16(pc + 4); 17220 pc += 6; 17221 *sp++ = JS_NewObjectProto(ctx, JS_NULL); 17222 if (unlikely(JS_IsException(sp[-1]))) 17223 goto exception; 17224 if (opcode == OP_make_var_ref_ref) { 17225 var_ref = var_refs[idx]; 17226 var_ref->header.ref_count++; 17227 } else { 17228 var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); 17229 if (!var_ref) 17230 goto exception; 17231 } 17232 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, 17233 JS_PROP_WRITABLE | JS_PROP_VARREF); 17234 if (!pr) { 17235 free_var_ref(rt, var_ref); 17236 goto exception; 17237 } 17238 pr->u.var_ref = var_ref; 17239 *sp++ = JS_AtomToValue(ctx, atom); 17240 } 17241 BREAK; 17242 CASE(OP_make_var_ref): 17243 { 17244 JSAtom atom; 17245 atom = get_u32(pc); 17246 pc += 4; 17247 17248 if (JS_GetGlobalVarRef(ctx, atom, sp)) 17249 goto exception; 17250 sp += 2; 17251 } 17252 BREAK; 17253 17254 CASE(OP_goto): 17255 pc += (int32_t)get_u32(pc); 17256 if (unlikely(js_poll_interrupts(ctx))) 17257 goto exception; 17258 BREAK; 17259 #if SHORT_OPCODES 17260 CASE(OP_goto16): 17261 pc += (int16_t)get_u16(pc); 17262 if (unlikely(js_poll_interrupts(ctx))) 17263 goto exception; 17264 BREAK; 17265 CASE(OP_goto8): 17266 pc += (int8_t)pc[0]; 17267 if (unlikely(js_poll_interrupts(ctx))) 17268 goto exception; 17269 BREAK; 17270 #endif 17271 CASE(OP_if_true): 17272 { 17273 int res; 17274 JSValue op1; 17275 17276 op1 = sp[-1]; 17277 pc += 4; 17278 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 17279 res = JS_VALUE_GET_INT(op1); 17280 } else { 17281 res = JS_ToBoolFree(ctx, op1); 17282 } 17283 sp--; 17284 if (res) { 17285 pc += (int32_t)get_u32(pc - 4) - 4; 17286 } 17287 if (unlikely(js_poll_interrupts(ctx))) 17288 goto exception; 17289 } 17290 BREAK; 17291 CASE(OP_if_false): 17292 { 17293 int res; 17294 JSValue op1; 17295 17296 op1 = sp[-1]; 17297 pc += 4; 17298 /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */ 17299 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 17300 res = JS_VALUE_GET_INT(op1); 17301 } else { 17302 res = JS_ToBoolFree(ctx, op1); 17303 } 17304 sp--; 17305 if (!res) { 17306 pc += (int32_t)get_u32(pc - 4) - 4; 17307 } 17308 if (unlikely(js_poll_interrupts(ctx))) 17309 goto exception; 17310 } 17311 BREAK; 17312 #if SHORT_OPCODES 17313 CASE(OP_if_true8): 17314 { 17315 int res; 17316 JSValue op1; 17317 17318 op1 = sp[-1]; 17319 pc += 1; 17320 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 17321 res = JS_VALUE_GET_INT(op1); 17322 } else { 17323 res = JS_ToBoolFree(ctx, op1); 17324 } 17325 sp--; 17326 if (res) { 17327 pc += (int8_t)pc[-1] - 1; 17328 } 17329 if (unlikely(js_poll_interrupts(ctx))) 17330 goto exception; 17331 } 17332 BREAK; 17333 CASE(OP_if_false8): 17334 { 17335 int res; 17336 JSValue op1; 17337 17338 op1 = sp[-1]; 17339 pc += 1; 17340 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 17341 res = JS_VALUE_GET_INT(op1); 17342 } else { 17343 res = JS_ToBoolFree(ctx, op1); 17344 } 17345 sp--; 17346 if (!res) { 17347 pc += (int8_t)pc[-1] - 1; 17348 } 17349 if (unlikely(js_poll_interrupts(ctx))) 17350 goto exception; 17351 } 17352 BREAK; 17353 #endif 17354 CASE(OP_catch): 17355 { 17356 int32_t diff; 17357 diff = get_u32(pc); 17358 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); 17359 sp++; 17360 pc += 4; 17361 } 17362 BREAK; 17363 CASE(OP_gosub): 17364 { 17365 int32_t diff; 17366 diff = get_u32(pc); 17367 /* XXX: should have a different tag to avoid security flaw */ 17368 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf); 17369 sp++; 17370 pc += diff; 17371 } 17372 BREAK; 17373 CASE(OP_ret): 17374 { 17375 JSValue op1; 17376 uint32_t pos; 17377 op1 = sp[-1]; 17378 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) 17379 goto ret_fail; 17380 pos = JS_VALUE_GET_INT(op1); 17381 if (unlikely(pos >= b->byte_code_len)) { 17382 ret_fail: 17383 JS_ThrowInternalError(ctx, "invalid ret value"); 17384 goto exception; 17385 } 17386 sp--; 17387 pc = b->byte_code_buf + pos; 17388 } 17389 BREAK; 17390 17391 CASE(OP_for_in_start): 17392 if (js_for_in_start(ctx, sp)) 17393 goto exception; 17394 BREAK; 17395 CASE(OP_for_in_next): 17396 if (js_for_in_next(ctx, sp)) 17397 goto exception; 17398 sp += 2; 17399 BREAK; 17400 CASE(OP_for_of_start): 17401 if (js_for_of_start(ctx, sp, FALSE)) 17402 goto exception; 17403 sp += 1; 17404 *sp++ = JS_NewCatchOffset(ctx, 0); 17405 BREAK; 17406 CASE(OP_for_of_next): 17407 { 17408 int offset = -3 - pc[0]; 17409 pc += 1; 17410 if (js_for_of_next(ctx, sp, offset)) 17411 goto exception; 17412 sp += 2; 17413 } 17414 BREAK; 17415 CASE(OP_for_await_of_start): 17416 if (js_for_of_start(ctx, sp, TRUE)) 17417 goto exception; 17418 sp += 1; 17419 *sp++ = JS_NewCatchOffset(ctx, 0); 17420 BREAK; 17421 CASE(OP_iterator_get_value_done): 17422 if (js_iterator_get_value_done(ctx, sp)) 17423 goto exception; 17424 sp += 1; 17425 BREAK; 17426 CASE(OP_iterator_check_object): 17427 if (unlikely(!JS_IsObject(sp[-1]))) { 17428 JS_ThrowTypeError(ctx, "iterator must return an object"); 17429 goto exception; 17430 } 17431 BREAK; 17432 17433 CASE(OP_iterator_close): 17434 /* iter_obj next catch_offset -> */ 17435 sp--; /* drop the catch offset to avoid getting caught by exception */ 17436 JS_FreeValue(ctx, sp[-1]); /* drop the next method */ 17437 sp--; 17438 if (!JS_IsUndefined(sp[-1])) { 17439 if (JS_IteratorClose(ctx, sp[-1], FALSE)) 17440 goto exception; 17441 JS_FreeValue(ctx, sp[-1]); 17442 } 17443 sp--; 17444 BREAK; 17445 CASE(OP_nip_catch): 17446 { 17447 JSValue ret_val; 17448 /* catch_offset ... ret_val -> ret_eval */ 17449 ret_val = *--sp; 17450 while (sp > stack_buf && 17451 JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { 17452 JS_FreeValue(ctx, *--sp); 17453 } 17454 if (unlikely(sp == stack_buf)) { 17455 JS_ThrowInternalError(ctx, "nip_catch"); 17456 JS_FreeValue(ctx, ret_val); 17457 goto exception; 17458 } 17459 sp[-1] = ret_val; 17460 } 17461 BREAK; 17462 17463 CASE(OP_iterator_next): 17464 /* stack: iter_obj next catch_offset val */ 17465 { 17466 JSValue ret; 17467 ret = JS_Call(ctx, sp[-3], sp[-4], 17468 1, (JSValueConst *)(sp - 1)); 17469 if (JS_IsException(ret)) 17470 goto exception; 17471 JS_FreeValue(ctx, sp[-1]); 17472 sp[-1] = ret; 17473 } 17474 BREAK; 17475 17476 CASE(OP_iterator_call): 17477 /* stack: iter_obj next catch_offset val */ 17478 { 17479 JSValue method, ret; 17480 BOOL ret_flag; 17481 int flags; 17482 flags = *pc++; 17483 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? 17484 JS_ATOM_throw : JS_ATOM_return); 17485 if (JS_IsException(method)) 17486 goto exception; 17487 if (JS_IsUndefined(method) || JS_IsNull(method)) { 17488 ret_flag = TRUE; 17489 } else { 17490 if (flags & 2) { 17491 /* no argument */ 17492 ret = JS_CallFree(ctx, method, sp[-4], 17493 0, NULL); 17494 } else { 17495 ret = JS_CallFree(ctx, method, sp[-4], 17496 1, (JSValueConst *)(sp - 1)); 17497 } 17498 if (JS_IsException(ret)) 17499 goto exception; 17500 JS_FreeValue(ctx, sp[-1]); 17501 sp[-1] = ret; 17502 ret_flag = FALSE; 17503 } 17504 sp[0] = JS_NewBool(ctx, ret_flag); 17505 sp += 1; 17506 } 17507 BREAK; 17508 17509 CASE(OP_lnot): 17510 { 17511 int res; 17512 JSValue op1; 17513 17514 op1 = sp[-1]; 17515 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { 17516 res = JS_VALUE_GET_INT(op1) != 0; 17517 } else { 17518 res = JS_ToBoolFree(ctx, op1); 17519 } 17520 sp[-1] = JS_NewBool(ctx, !res); 17521 } 17522 BREAK; 17523 17524 CASE(OP_get_field): 17525 { 17526 JSValue val; 17527 JSAtom atom; 17528 atom = get_u32(pc); 17529 pc += 4; 17530 17531 val = JS_GetProperty(ctx, sp[-1], atom); 17532 if (unlikely(JS_IsException(val))) 17533 goto exception; 17534 JS_FreeValue(ctx, sp[-1]); 17535 sp[-1] = val; 17536 } 17537 BREAK; 17538 17539 CASE(OP_get_field2): 17540 { 17541 JSValue val; 17542 JSAtom atom; 17543 atom = get_u32(pc); 17544 pc += 4; 17545 17546 val = JS_GetProperty(ctx, sp[-1], atom); 17547 if (unlikely(JS_IsException(val))) 17548 goto exception; 17549 *sp++ = val; 17550 } 17551 BREAK; 17552 17553 CASE(OP_put_field): 17554 { 17555 int ret; 17556 JSAtom atom; 17557 atom = get_u32(pc); 17558 pc += 4; 17559 17560 ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2], 17561 JS_PROP_THROW_STRICT); 17562 JS_FreeValue(ctx, sp[-2]); 17563 sp -= 2; 17564 if (unlikely(ret < 0)) 17565 goto exception; 17566 } 17567 BREAK; 17568 17569 CASE(OP_private_symbol): 17570 { 17571 JSAtom atom; 17572 JSValue val; 17573 17574 atom = get_u32(pc); 17575 pc += 4; 17576 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); 17577 if (JS_IsException(val)) 17578 goto exception; 17579 *sp++ = val; 17580 } 17581 BREAK; 17582 17583 CASE(OP_get_private_field): 17584 { 17585 JSValue val; 17586 17587 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); 17588 JS_FreeValue(ctx, sp[-1]); 17589 JS_FreeValue(ctx, sp[-2]); 17590 sp[-2] = val; 17591 sp--; 17592 if (unlikely(JS_IsException(val))) 17593 goto exception; 17594 } 17595 BREAK; 17596 17597 CASE(OP_put_private_field): 17598 { 17599 int ret; 17600 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); 17601 JS_FreeValue(ctx, sp[-3]); 17602 JS_FreeValue(ctx, sp[-1]); 17603 sp -= 3; 17604 if (unlikely(ret < 0)) 17605 goto exception; 17606 } 17607 BREAK; 17608 17609 CASE(OP_define_private_field): 17610 { 17611 int ret; 17612 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); 17613 JS_FreeValue(ctx, sp[-2]); 17614 sp -= 2; 17615 if (unlikely(ret < 0)) 17616 goto exception; 17617 } 17618 BREAK; 17619 17620 CASE(OP_define_field): 17621 { 17622 int ret; 17623 JSAtom atom; 17624 atom = get_u32(pc); 17625 pc += 4; 17626 17627 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], 17628 JS_PROP_C_W_E | JS_PROP_THROW); 17629 sp--; 17630 if (unlikely(ret < 0)) 17631 goto exception; 17632 } 17633 BREAK; 17634 17635 CASE(OP_set_name): 17636 { 17637 int ret; 17638 JSAtom atom; 17639 atom = get_u32(pc); 17640 pc += 4; 17641 17642 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); 17643 if (unlikely(ret < 0)) 17644 goto exception; 17645 } 17646 BREAK; 17647 CASE(OP_set_name_computed): 17648 { 17649 int ret; 17650 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); 17651 if (unlikely(ret < 0)) 17652 goto exception; 17653 } 17654 BREAK; 17655 CASE(OP_set_proto): 17656 { 17657 JSValue proto; 17658 proto = sp[-1]; 17659 if (JS_IsObject(proto) || JS_IsNull(proto)) { 17660 if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) 17661 goto exception; 17662 } 17663 JS_FreeValue(ctx, proto); 17664 sp--; 17665 } 17666 BREAK; 17667 CASE(OP_set_home_object): 17668 js_method_set_home_object(ctx, sp[-1], sp[-2]); 17669 BREAK; 17670 CASE(OP_define_method): 17671 CASE(OP_define_method_computed): 17672 { 17673 JSValue getter, setter, value; 17674 JSValueConst obj; 17675 JSAtom atom; 17676 int flags, ret, op_flags; 17677 BOOL is_computed; 17678 #define OP_DEFINE_METHOD_METHOD 0 17679 #define OP_DEFINE_METHOD_GETTER 1 17680 #define OP_DEFINE_METHOD_SETTER 2 17681 #define OP_DEFINE_METHOD_ENUMERABLE 4 17682 17683 is_computed = (opcode == OP_define_method_computed); 17684 if (is_computed) { 17685 atom = JS_ValueToAtom(ctx, sp[-2]); 17686 if (unlikely(atom == JS_ATOM_NULL)) 17687 goto exception; 17688 opcode += OP_define_method - OP_define_method_computed; 17689 } else { 17690 atom = get_u32(pc); 17691 pc += 4; 17692 } 17693 op_flags = *pc++; 17694 17695 obj = sp[-2 - is_computed]; 17696 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | 17697 JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; 17698 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) 17699 flags |= JS_PROP_ENUMERABLE; 17700 op_flags &= 3; 17701 value = JS_UNDEFINED; 17702 getter = JS_UNDEFINED; 17703 setter = JS_UNDEFINED; 17704 if (op_flags == OP_DEFINE_METHOD_METHOD) { 17705 value = sp[-1]; 17706 flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; 17707 } else if (op_flags == OP_DEFINE_METHOD_GETTER) { 17708 getter = sp[-1]; 17709 flags |= JS_PROP_HAS_GET; 17710 } else { 17711 setter = sp[-1]; 17712 flags |= JS_PROP_HAS_SET; 17713 } 17714 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); 17715 if (ret >= 0) { 17716 ret = JS_DefineProperty(ctx, obj, atom, value, 17717 getter, setter, flags); 17718 } 17719 JS_FreeValue(ctx, sp[-1]); 17720 if (is_computed) { 17721 JS_FreeAtom(ctx, atom); 17722 JS_FreeValue(ctx, sp[-2]); 17723 } 17724 sp -= 1 + is_computed; 17725 if (unlikely(ret < 0)) 17726 goto exception; 17727 } 17728 BREAK; 17729 17730 CASE(OP_define_class): 17731 CASE(OP_define_class_computed): 17732 { 17733 int class_flags; 17734 JSAtom atom; 17735 17736 atom = get_u32(pc); 17737 class_flags = pc[4]; 17738 pc += 5; 17739 if (js_op_define_class(ctx, sp, atom, class_flags, 17740 var_refs, sf, 17741 (opcode == OP_define_class_computed)) < 0) 17742 goto exception; 17743 } 17744 BREAK; 17745 17746 CASE(OP_get_array_el): 17747 { 17748 JSValue val; 17749 17750 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); 17751 JS_FreeValue(ctx, sp[-2]); 17752 sp[-2] = val; 17753 sp--; 17754 if (unlikely(JS_IsException(val))) 17755 goto exception; 17756 } 17757 BREAK; 17758 17759 CASE(OP_get_array_el2): 17760 { 17761 JSValue val; 17762 17763 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); 17764 sp[-1] = val; 17765 if (unlikely(JS_IsException(val))) 17766 goto exception; 17767 } 17768 BREAK; 17769 17770 CASE(OP_get_ref_value): 17771 { 17772 JSValue val; 17773 if (unlikely(JS_IsUndefined(sp[-2]))) { 17774 JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); 17775 if (atom != JS_ATOM_NULL) { 17776 JS_ThrowReferenceErrorNotDefined(ctx, atom); 17777 JS_FreeAtom(ctx, atom); 17778 } 17779 goto exception; 17780 } 17781 val = JS_GetPropertyValue(ctx, sp[-2], 17782 JS_DupValue(ctx, sp[-1])); 17783 if (unlikely(JS_IsException(val))) 17784 goto exception; 17785 sp[0] = val; 17786 sp++; 17787 } 17788 BREAK; 17789 17790 CASE(OP_get_super_value): 17791 { 17792 JSValue val; 17793 JSAtom atom; 17794 atom = JS_ValueToAtom(ctx, sp[-1]); 17795 if (unlikely(atom == JS_ATOM_NULL)) 17796 goto exception; 17797 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE); 17798 JS_FreeAtom(ctx, atom); 17799 if (unlikely(JS_IsException(val))) 17800 goto exception; 17801 JS_FreeValue(ctx, sp[-1]); 17802 JS_FreeValue(ctx, sp[-2]); 17803 JS_FreeValue(ctx, sp[-3]); 17804 sp[-3] = val; 17805 sp -= 2; 17806 } 17807 BREAK; 17808 17809 CASE(OP_put_array_el): 17810 { 17811 int ret; 17812 17813 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); 17814 JS_FreeValue(ctx, sp[-3]); 17815 sp -= 3; 17816 if (unlikely(ret < 0)) 17817 goto exception; 17818 } 17819 BREAK; 17820 17821 CASE(OP_put_ref_value): 17822 { 17823 int ret, flags; 17824 flags = JS_PROP_THROW_STRICT; 17825 if (unlikely(JS_IsUndefined(sp[-3]))) { 17826 if (is_strict_mode(ctx)) { 17827 JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); 17828 if (atom != JS_ATOM_NULL) { 17829 JS_ThrowReferenceErrorNotDefined(ctx, atom); 17830 JS_FreeAtom(ctx, atom); 17831 } 17832 goto exception; 17833 } else { 17834 sp[-3] = JS_DupValue(ctx, ctx->global_obj); 17835 } 17836 } else { 17837 if (is_strict_mode(ctx)) 17838 flags |= JS_PROP_NO_ADD; 17839 } 17840 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); 17841 JS_FreeValue(ctx, sp[-3]); 17842 sp -= 3; 17843 if (unlikely(ret < 0)) 17844 goto exception; 17845 } 17846 BREAK; 17847 17848 CASE(OP_put_super_value): 17849 { 17850 int ret; 17851 JSAtom atom; 17852 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { 17853 JS_ThrowTypeErrorNotAnObject(ctx); 17854 goto exception; 17855 } 17856 atom = JS_ValueToAtom(ctx, sp[-2]); 17857 if (unlikely(atom == JS_ATOM_NULL)) 17858 goto exception; 17859 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4], 17860 JS_PROP_THROW_STRICT); 17861 JS_FreeAtom(ctx, atom); 17862 JS_FreeValue(ctx, sp[-4]); 17863 JS_FreeValue(ctx, sp[-3]); 17864 JS_FreeValue(ctx, sp[-2]); 17865 sp -= 4; 17866 if (ret < 0) 17867 goto exception; 17868 } 17869 BREAK; 17870 17871 CASE(OP_define_array_el): 17872 { 17873 int ret; 17874 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1], 17875 JS_PROP_C_W_E | JS_PROP_THROW); 17876 sp -= 1; 17877 if (unlikely(ret < 0)) 17878 goto exception; 17879 } 17880 BREAK; 17881 17882 CASE(OP_append): /* array pos enumobj -- array pos */ 17883 { 17884 if (js_append_enumerate(ctx, sp)) 17885 goto exception; 17886 JS_FreeValue(ctx, *--sp); 17887 } 17888 BREAK; 17889 17890 CASE(OP_copy_data_properties): /* target source excludeList */ 17891 { 17892 /* stack offsets (-1 based): 17893 2 bits for target, 17894 3 bits for source, 17895 2 bits for exclusionList */ 17896 int mask; 17897 17898 mask = *pc++; 17899 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], 17900 sp[-1 - ((mask >> 2) & 7)], 17901 sp[-1 - ((mask >> 5) & 7)], 0)) 17902 goto exception; 17903 } 17904 BREAK; 17905 17906 CASE(OP_add): 17907 { 17908 JSValue op1, op2; 17909 op1 = sp[-2]; 17910 op2 = sp[-1]; 17911 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 17912 int64_t r; 17913 r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); 17914 if (unlikely((int)r != r)) 17915 goto add_slow; 17916 sp[-2] = JS_NewInt32(ctx, r); 17917 sp--; 17918 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { 17919 sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + 17920 JS_VALUE_GET_FLOAT64(op2)); 17921 sp--; 17922 } else if (JS_IsString(op1) && JS_IsString(op2)) { 17923 sp[-2] = JS_ConcatString(ctx, op1, op2); 17924 sp--; 17925 if (JS_IsException(sp[-1])) 17926 goto exception; 17927 } else { 17928 add_slow: 17929 if (js_add_slow(ctx, sp)) 17930 goto exception; 17931 sp--; 17932 } 17933 } 17934 BREAK; 17935 CASE(OP_add_loc): 17936 { 17937 JSValue op2; 17938 JSValue *pv; 17939 int idx; 17940 idx = *pc; 17941 pc += 1; 17942 17943 op2 = sp[-1]; 17944 pv = &var_buf[idx]; 17945 if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { 17946 int64_t r; 17947 r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); 17948 if (unlikely((int)r != r)) 17949 goto add_loc_slow; 17950 *pv = JS_NewInt32(ctx, r); 17951 sp--; 17952 } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { 17953 *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + 17954 JS_VALUE_GET_FLOAT64(op2)); 17955 sp--; 17956 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { 17957 sp--; 17958 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); 17959 if (JS_IsException(op2)) 17960 goto exception; 17961 if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { 17962 JS_FreeValue(ctx, op2); 17963 } else { 17964 op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); 17965 if (JS_IsException(op2)) 17966 goto exception; 17967 set_value(ctx, pv, op2); 17968 } 17969 } else { 17970 JSValue ops[2]; 17971 add_loc_slow: 17972 /* In case of exception, js_add_slow frees ops[0] 17973 and ops[1], so we must duplicate *pv */ 17974 ops[0] = JS_DupValue(ctx, *pv); 17975 ops[1] = op2; 17976 sp--; 17977 if (js_add_slow(ctx, ops + 2)) 17978 goto exception; 17979 set_value(ctx, pv, ops[0]); 17980 } 17981 } 17982 BREAK; 17983 CASE(OP_sub): 17984 { 17985 JSValue op1, op2; 17986 op1 = sp[-2]; 17987 op2 = sp[-1]; 17988 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 17989 int64_t r; 17990 r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); 17991 if (unlikely((int)r != r)) 17992 goto binary_arith_slow; 17993 sp[-2] = JS_NewInt32(ctx, r); 17994 sp--; 17995 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { 17996 sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - 17997 JS_VALUE_GET_FLOAT64(op2)); 17998 sp--; 17999 } else { 18000 goto binary_arith_slow; 18001 } 18002 } 18003 BREAK; 18004 CASE(OP_mul): 18005 { 18006 JSValue op1, op2; 18007 double d; 18008 op1 = sp[-2]; 18009 op2 = sp[-1]; 18010 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18011 int32_t v1, v2; 18012 int64_t r; 18013 v1 = JS_VALUE_GET_INT(op1); 18014 v2 = JS_VALUE_GET_INT(op2); 18015 r = (int64_t)v1 * v2; 18016 if (unlikely((int)r != r)) { 18017 #ifdef CONFIG_BIGNUM 18018 if (unlikely(sf->js_mode & JS_MODE_MATH) && 18019 (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER)) 18020 goto binary_arith_slow; 18021 #endif 18022 d = (double)r; 18023 goto mul_fp_res; 18024 } 18025 /* need to test zero case for -0 result */ 18026 if (unlikely(r == 0 && (v1 | v2) < 0)) { 18027 d = -0.0; 18028 goto mul_fp_res; 18029 } 18030 sp[-2] = JS_NewInt32(ctx, r); 18031 sp--; 18032 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { 18033 #ifdef CONFIG_BIGNUM 18034 if (unlikely(sf->js_mode & JS_MODE_MATH)) 18035 goto binary_arith_slow; 18036 #endif 18037 d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); 18038 mul_fp_res: 18039 sp[-2] = __JS_NewFloat64(ctx, d); 18040 sp--; 18041 } else { 18042 goto binary_arith_slow; 18043 } 18044 } 18045 BREAK; 18046 CASE(OP_div): 18047 { 18048 JSValue op1, op2; 18049 op1 = sp[-2]; 18050 op2 = sp[-1]; 18051 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18052 int v1, v2; 18053 if (unlikely(sf->js_mode & JS_MODE_MATH)) 18054 goto binary_arith_slow; 18055 v1 = JS_VALUE_GET_INT(op1); 18056 v2 = JS_VALUE_GET_INT(op2); 18057 sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); 18058 sp--; 18059 } else { 18060 goto binary_arith_slow; 18061 } 18062 } 18063 BREAK; 18064 CASE(OP_mod): 18065 #ifdef CONFIG_BIGNUM 18066 CASE(OP_math_mod): 18067 #endif 18068 { 18069 JSValue op1, op2; 18070 op1 = sp[-2]; 18071 op2 = sp[-1]; 18072 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18073 int v1, v2, r; 18074 v1 = JS_VALUE_GET_INT(op1); 18075 v2 = JS_VALUE_GET_INT(op2); 18076 /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = 18077 -1 and the cases where the result is -0. */ 18078 if (unlikely(v1 < 0 || v2 <= 0)) 18079 goto binary_arith_slow; 18080 r = v1 % v2; 18081 sp[-2] = JS_NewInt32(ctx, r); 18082 sp--; 18083 } else { 18084 goto binary_arith_slow; 18085 } 18086 } 18087 BREAK; 18088 CASE(OP_pow): 18089 binary_arith_slow: 18090 if (js_binary_arith_slow(ctx, sp, opcode)) 18091 goto exception; 18092 sp--; 18093 BREAK; 18094 18095 CASE(OP_plus): 18096 { 18097 JSValue op1; 18098 uint32_t tag; 18099 op1 = sp[-1]; 18100 tag = JS_VALUE_GET_TAG(op1); 18101 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { 18102 } else { 18103 if (js_unary_arith_slow(ctx, sp, opcode)) 18104 goto exception; 18105 } 18106 } 18107 BREAK; 18108 CASE(OP_neg): 18109 { 18110 JSValue op1; 18111 uint32_t tag; 18112 int val; 18113 double d; 18114 op1 = sp[-1]; 18115 tag = JS_VALUE_GET_TAG(op1); 18116 if (tag == JS_TAG_INT) { 18117 val = JS_VALUE_GET_INT(op1); 18118 /* Note: -0 cannot be expressed as integer */ 18119 if (unlikely(val == 0)) { 18120 d = -0.0; 18121 goto neg_fp_res; 18122 } 18123 if (unlikely(val == INT32_MIN)) { 18124 d = -(double)val; 18125 goto neg_fp_res; 18126 } 18127 sp[-1] = JS_NewInt32(ctx, -val); 18128 } else if (JS_TAG_IS_FLOAT64(tag)) { 18129 d = -JS_VALUE_GET_FLOAT64(op1); 18130 neg_fp_res: 18131 sp[-1] = __JS_NewFloat64(ctx, d); 18132 } else { 18133 if (js_unary_arith_slow(ctx, sp, opcode)) 18134 goto exception; 18135 } 18136 } 18137 BREAK; 18138 CASE(OP_inc): 18139 { 18140 JSValue op1; 18141 int val; 18142 op1 = sp[-1]; 18143 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 18144 val = JS_VALUE_GET_INT(op1); 18145 if (unlikely(val == INT32_MAX)) 18146 goto inc_slow; 18147 sp[-1] = JS_NewInt32(ctx, val + 1); 18148 } else { 18149 inc_slow: 18150 if (js_unary_arith_slow(ctx, sp, opcode)) 18151 goto exception; 18152 } 18153 } 18154 BREAK; 18155 CASE(OP_dec): 18156 { 18157 JSValue op1; 18158 int val; 18159 op1 = sp[-1]; 18160 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 18161 val = JS_VALUE_GET_INT(op1); 18162 if (unlikely(val == INT32_MIN)) 18163 goto dec_slow; 18164 sp[-1] = JS_NewInt32(ctx, val - 1); 18165 } else { 18166 dec_slow: 18167 if (js_unary_arith_slow(ctx, sp, opcode)) 18168 goto exception; 18169 } 18170 } 18171 BREAK; 18172 CASE(OP_post_inc): 18173 CASE(OP_post_dec): 18174 if (js_post_inc_slow(ctx, sp, opcode)) 18175 goto exception; 18176 sp++; 18177 BREAK; 18178 CASE(OP_inc_loc): 18179 { 18180 JSValue op1; 18181 int val; 18182 int idx; 18183 idx = *pc; 18184 pc += 1; 18185 18186 op1 = var_buf[idx]; 18187 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 18188 val = JS_VALUE_GET_INT(op1); 18189 if (unlikely(val == INT32_MAX)) 18190 goto inc_loc_slow; 18191 var_buf[idx] = JS_NewInt32(ctx, val + 1); 18192 } else { 18193 inc_loc_slow: 18194 /* must duplicate otherwise the variable value may 18195 be destroyed before JS code accesses it */ 18196 op1 = JS_DupValue(ctx, op1); 18197 if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) 18198 goto exception; 18199 set_value(ctx, &var_buf[idx], op1); 18200 } 18201 } 18202 BREAK; 18203 CASE(OP_dec_loc): 18204 { 18205 JSValue op1; 18206 int val; 18207 int idx; 18208 idx = *pc; 18209 pc += 1; 18210 18211 op1 = var_buf[idx]; 18212 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 18213 val = JS_VALUE_GET_INT(op1); 18214 if (unlikely(val == INT32_MIN)) 18215 goto dec_loc_slow; 18216 var_buf[idx] = JS_NewInt32(ctx, val - 1); 18217 } else { 18218 dec_loc_slow: 18219 /* must duplicate otherwise the variable value may 18220 be destroyed before JS code accesses it */ 18221 op1 = JS_DupValue(ctx, op1); 18222 if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) 18223 goto exception; 18224 set_value(ctx, &var_buf[idx], op1); 18225 } 18226 } 18227 BREAK; 18228 CASE(OP_not): 18229 { 18230 JSValue op1; 18231 op1 = sp[-1]; 18232 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { 18233 sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1)); 18234 } else { 18235 if (js_not_slow(ctx, sp)) 18236 goto exception; 18237 } 18238 } 18239 BREAK; 18240 18241 CASE(OP_shl): 18242 { 18243 JSValue op1, op2; 18244 op1 = sp[-2]; 18245 op2 = sp[-1]; 18246 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18247 uint32_t v1, v2; 18248 v1 = JS_VALUE_GET_INT(op1); 18249 v2 = JS_VALUE_GET_INT(op2); 18250 #ifdef CONFIG_BIGNUM 18251 { 18252 int64_t r; 18253 if (unlikely(sf->js_mode & JS_MODE_MATH)) { 18254 if (v2 > 0x1f) 18255 goto shl_slow; 18256 r = (int64_t)v1 << v2; 18257 if ((int)r != r) 18258 goto shl_slow; 18259 } else { 18260 v2 &= 0x1f; 18261 } 18262 } 18263 #else 18264 v2 &= 0x1f; 18265 #endif 18266 sp[-2] = JS_NewInt32(ctx, v1 << v2); 18267 sp--; 18268 } else { 18269 #ifdef CONFIG_BIGNUM 18270 shl_slow: 18271 #endif 18272 if (js_binary_logic_slow(ctx, sp, opcode)) 18273 goto exception; 18274 sp--; 18275 } 18276 } 18277 BREAK; 18278 CASE(OP_shr): 18279 { 18280 JSValue op1, op2; 18281 op1 = sp[-2]; 18282 op2 = sp[-1]; 18283 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18284 uint32_t v2; 18285 v2 = JS_VALUE_GET_INT(op2); 18286 /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */ 18287 v2 &= 0x1f; 18288 sp[-2] = JS_NewUint32(ctx, 18289 (uint32_t)JS_VALUE_GET_INT(op1) >> 18290 v2); 18291 sp--; 18292 } else { 18293 if (js_shr_slow(ctx, sp)) 18294 goto exception; 18295 sp--; 18296 } 18297 } 18298 BREAK; 18299 CASE(OP_sar): 18300 { 18301 JSValue op1, op2; 18302 op1 = sp[-2]; 18303 op2 = sp[-1]; 18304 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18305 uint32_t v2; 18306 v2 = JS_VALUE_GET_INT(op2); 18307 #ifdef CONFIG_BIGNUM 18308 if (unlikely(v2 > 0x1f)) { 18309 if (unlikely(sf->js_mode & JS_MODE_MATH)) 18310 goto sar_slow; 18311 else 18312 v2 &= 0x1f; 18313 } 18314 #else 18315 v2 &= 0x1f; 18316 #endif 18317 sp[-2] = JS_NewInt32(ctx, 18318 (int)JS_VALUE_GET_INT(op1) >> v2); 18319 sp--; 18320 } else { 18321 #ifdef CONFIG_BIGNUM 18322 sar_slow: 18323 #endif 18324 if (js_binary_logic_slow(ctx, sp, opcode)) 18325 goto exception; 18326 sp--; 18327 } 18328 } 18329 BREAK; 18330 CASE(OP_and): 18331 { 18332 JSValue op1, op2; 18333 op1 = sp[-2]; 18334 op2 = sp[-1]; 18335 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18336 sp[-2] = JS_NewInt32(ctx, 18337 JS_VALUE_GET_INT(op1) & 18338 JS_VALUE_GET_INT(op2)); 18339 sp--; 18340 } else { 18341 if (js_binary_logic_slow(ctx, sp, opcode)) 18342 goto exception; 18343 sp--; 18344 } 18345 } 18346 BREAK; 18347 CASE(OP_or): 18348 { 18349 JSValue op1, op2; 18350 op1 = sp[-2]; 18351 op2 = sp[-1]; 18352 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18353 sp[-2] = JS_NewInt32(ctx, 18354 JS_VALUE_GET_INT(op1) | 18355 JS_VALUE_GET_INT(op2)); 18356 sp--; 18357 } else { 18358 if (js_binary_logic_slow(ctx, sp, opcode)) 18359 goto exception; 18360 sp--; 18361 } 18362 } 18363 BREAK; 18364 CASE(OP_xor): 18365 { 18366 JSValue op1, op2; 18367 op1 = sp[-2]; 18368 op2 = sp[-1]; 18369 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { 18370 sp[-2] = JS_NewInt32(ctx, 18371 JS_VALUE_GET_INT(op1) ^ 18372 JS_VALUE_GET_INT(op2)); 18373 sp--; 18374 } else { 18375 if (js_binary_logic_slow(ctx, sp, opcode)) 18376 goto exception; 18377 sp--; 18378 } 18379 } 18380 BREAK; 18381 18382 18383 #define OP_CMP(opcode, binary_op, slow_call) \ 18384 CASE(opcode): \ 18385 { \ 18386 JSValue op1, op2; \ 18387 op1 = sp[-2]; \ 18388 op2 = sp[-1]; \ 18389 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ 18390 sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ 18391 sp--; \ 18392 } else { \ 18393 if (slow_call) \ 18394 goto exception; \ 18395 sp--; \ 18396 } \ 18397 } \ 18398 BREAK 18399 18400 OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); 18401 OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); 18402 OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); 18403 OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); 18404 OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); 18405 OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); 18406 OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); 18407 OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); 18408 18409 #ifdef CONFIG_BIGNUM 18410 CASE(OP_mul_pow10): 18411 if (rt->bigfloat_ops.mul_pow10(ctx, sp)) 18412 goto exception; 18413 sp--; 18414 BREAK; 18415 #endif 18416 CASE(OP_in): 18417 if (js_operator_in(ctx, sp)) 18418 goto exception; 18419 sp--; 18420 BREAK; 18421 CASE(OP_private_in): 18422 if (js_operator_private_in(ctx, sp)) 18423 goto exception; 18424 sp--; 18425 BREAK; 18426 CASE(OP_instanceof): 18427 if (js_operator_instanceof(ctx, sp)) 18428 goto exception; 18429 sp--; 18430 BREAK; 18431 CASE(OP_typeof): 18432 { 18433 JSValue op1; 18434 JSAtom atom; 18435 18436 op1 = sp[-1]; 18437 atom = js_operator_typeof(ctx, op1); 18438 JS_FreeValue(ctx, op1); 18439 sp[-1] = JS_AtomToString(ctx, atom); 18440 } 18441 BREAK; 18442 CASE(OP_delete): 18443 if (js_operator_delete(ctx, sp)) 18444 goto exception; 18445 sp--; 18446 BREAK; 18447 CASE(OP_delete_var): 18448 { 18449 JSAtom atom; 18450 int ret; 18451 18452 atom = get_u32(pc); 18453 pc += 4; 18454 18455 ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); 18456 if (unlikely(ret < 0)) 18457 goto exception; 18458 *sp++ = JS_NewBool(ctx, ret); 18459 } 18460 BREAK; 18461 18462 CASE(OP_to_object): 18463 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { 18464 ret_val = JS_ToObject(ctx, sp[-1]); 18465 if (JS_IsException(ret_val)) 18466 goto exception; 18467 JS_FreeValue(ctx, sp[-1]); 18468 sp[-1] = ret_val; 18469 } 18470 BREAK; 18471 18472 CASE(OP_to_propkey): 18473 switch (JS_VALUE_GET_TAG(sp[-1])) { 18474 case JS_TAG_INT: 18475 case JS_TAG_STRING: 18476 case JS_TAG_SYMBOL: 18477 break; 18478 default: 18479 ret_val = JS_ToPropertyKey(ctx, sp[-1]); 18480 if (JS_IsException(ret_val)) 18481 goto exception; 18482 JS_FreeValue(ctx, sp[-1]); 18483 sp[-1] = ret_val; 18484 break; 18485 } 18486 BREAK; 18487 18488 CASE(OP_to_propkey2): 18489 /* must be tested first */ 18490 if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { 18491 JS_ThrowTypeError(ctx, "value has no property"); 18492 goto exception; 18493 } 18494 switch (JS_VALUE_GET_TAG(sp[-1])) { 18495 case JS_TAG_INT: 18496 case JS_TAG_STRING: 18497 case JS_TAG_SYMBOL: 18498 break; 18499 default: 18500 ret_val = JS_ToPropertyKey(ctx, sp[-1]); 18501 if (JS_IsException(ret_val)) 18502 goto exception; 18503 JS_FreeValue(ctx, sp[-1]); 18504 sp[-1] = ret_val; 18505 break; 18506 } 18507 BREAK; 18508 #if 0 18509 CASE(OP_to_string): 18510 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) { 18511 ret_val = JS_ToString(ctx, sp[-1]); 18512 if (JS_IsException(ret_val)) 18513 goto exception; 18514 JS_FreeValue(ctx, sp[-1]); 18515 sp[-1] = ret_val; 18516 } 18517 BREAK; 18518 #endif 18519 CASE(OP_with_get_var): 18520 CASE(OP_with_put_var): 18521 CASE(OP_with_delete_var): 18522 CASE(OP_with_make_ref): 18523 CASE(OP_with_get_ref): 18524 CASE(OP_with_get_ref_undef): 18525 { 18526 JSAtom atom; 18527 int32_t diff; 18528 JSValue obj, val; 18529 int ret, is_with; 18530 atom = get_u32(pc); 18531 diff = get_u32(pc + 4); 18532 is_with = pc[8]; 18533 pc += 9; 18534 18535 obj = sp[-1]; 18536 ret = JS_HasProperty(ctx, obj, atom); 18537 if (unlikely(ret < 0)) 18538 goto exception; 18539 if (ret) { 18540 if (is_with) { 18541 ret = js_has_unscopable(ctx, obj, atom); 18542 if (unlikely(ret < 0)) 18543 goto exception; 18544 if (ret) 18545 goto no_with; 18546 } 18547 switch (opcode) { 18548 case OP_with_get_var: 18549 val = JS_GetProperty(ctx, obj, atom); 18550 if (unlikely(JS_IsException(val))) 18551 goto exception; 18552 set_value(ctx, &sp[-1], val); 18553 break; 18554 case OP_with_put_var: 18555 /* XXX: check if strict mode */ 18556 ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, 18557 JS_PROP_THROW_STRICT); 18558 JS_FreeValue(ctx, sp[-1]); 18559 sp -= 2; 18560 if (unlikely(ret < 0)) 18561 goto exception; 18562 break; 18563 case OP_with_delete_var: 18564 ret = JS_DeleteProperty(ctx, obj, atom, 0); 18565 if (unlikely(ret < 0)) 18566 goto exception; 18567 JS_FreeValue(ctx, sp[-1]); 18568 sp[-1] = JS_NewBool(ctx, ret); 18569 break; 18570 case OP_with_make_ref: 18571 /* produce a pair object/propname on the stack */ 18572 *sp++ = JS_AtomToValue(ctx, atom); 18573 break; 18574 case OP_with_get_ref: 18575 /* produce a pair object/method on the stack */ 18576 val = JS_GetProperty(ctx, obj, atom); 18577 if (unlikely(JS_IsException(val))) 18578 goto exception; 18579 *sp++ = val; 18580 break; 18581 case OP_with_get_ref_undef: 18582 /* produce a pair undefined/function on the stack */ 18583 val = JS_GetProperty(ctx, obj, atom); 18584 if (unlikely(JS_IsException(val))) 18585 goto exception; 18586 JS_FreeValue(ctx, sp[-1]); 18587 sp[-1] = JS_UNDEFINED; 18588 *sp++ = val; 18589 break; 18590 } 18591 pc += diff - 5; 18592 } else { 18593 no_with: 18594 /* if not jumping, drop the object argument */ 18595 JS_FreeValue(ctx, sp[-1]); 18596 sp--; 18597 } 18598 } 18599 BREAK; 18600 18601 CASE(OP_await): 18602 ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT); 18603 goto done_generator; 18604 CASE(OP_yield): 18605 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD); 18606 goto done_generator; 18607 CASE(OP_yield_star): 18608 CASE(OP_async_yield_star): 18609 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR); 18610 goto done_generator; 18611 CASE(OP_return_async): 18612 ret_val = JS_UNDEFINED; 18613 goto done_generator; 18614 CASE(OP_initial_yield): 18615 ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD); 18616 goto done_generator; 18617 18618 CASE(OP_nop): 18619 BREAK; 18620 CASE(OP_is_undefined_or_null): 18621 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || 18622 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { 18623 goto set_true; 18624 } else { 18625 goto free_and_set_false; 18626 } 18627 #if SHORT_OPCODES 18628 CASE(OP_is_undefined): 18629 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { 18630 goto set_true; 18631 } else { 18632 goto free_and_set_false; 18633 } 18634 CASE(OP_is_null): 18635 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { 18636 goto set_true; 18637 } else { 18638 goto free_and_set_false; 18639 } 18640 /* XXX: could merge to a single opcode */ 18641 CASE(OP_typeof_is_undefined): 18642 /* different from OP_is_undefined because of isHTMLDDA */ 18643 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { 18644 goto free_and_set_true; 18645 } else { 18646 goto free_and_set_false; 18647 } 18648 CASE(OP_typeof_is_function): 18649 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { 18650 goto free_and_set_true; 18651 } else { 18652 goto free_and_set_false; 18653 } 18654 free_and_set_true: 18655 JS_FreeValue(ctx, sp[-1]); 18656 #endif 18657 set_true: 18658 sp[-1] = JS_TRUE; 18659 BREAK; 18660 free_and_set_false: 18661 JS_FreeValue(ctx, sp[-1]); 18662 sp[-1] = JS_FALSE; 18663 BREAK; 18664 CASE(OP_invalid): 18665 DEFAULT: 18666 JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", 18667 (int)(pc - b->byte_code_buf - 1), opcode); 18668 goto exception; 18669 } 18670 } 18671 exception: 18672 if (is_backtrace_needed(ctx, rt->current_exception)) { 18673 /* add the backtrace information now (it is not done 18674 before if the exception happens in a bytecode 18675 operation */ 18676 sf->cur_pc = pc; 18677 build_backtrace(ctx, rt->current_exception, NULL, 0, 0); 18678 } 18679 if (!JS_IsUncatchableError(ctx, rt->current_exception)) { 18680 while (sp > stack_buf) { 18681 JSValue val = *--sp; 18682 JS_FreeValue(ctx, val); 18683 if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { 18684 int pos = JS_VALUE_GET_INT(val); 18685 if (pos == 0) { 18686 /* enumerator: close it with a throw */ 18687 JS_FreeValue(ctx, sp[-1]); /* drop the next method */ 18688 sp--; 18689 JS_IteratorClose(ctx, sp[-1], TRUE); 18690 } else { 18691 *sp++ = rt->current_exception; 18692 rt->current_exception = JS_UNINITIALIZED; 18693 pc = b->byte_code_buf + pos; 18694 goto restart; 18695 } 18696 } 18697 } 18698 } 18699 ret_val = JS_EXCEPTION; 18700 /* the local variables are freed by the caller in the generator 18701 case. Hence the label 'done' should never be reached in a 18702 generator function. */ 18703 if (b->func_kind != JS_FUNC_NORMAL) { 18704 done_generator: 18705 sf->cur_pc = pc; 18706 sf->cur_sp = sp; 18707 } else { 18708 done: 18709 if (unlikely(!list_empty(&sf->var_ref_list))) { 18710 /* variable references reference the stack: must close them */ 18711 close_var_refs(rt, sf); 18712 } 18713 /* free the local variables and stack */ 18714 for(pval = local_buf; pval < sp; pval++) { 18715 JS_FreeValue(ctx, *pval); 18716 } 18717 } 18718 rt->current_stack_frame = sf->prev_frame; 18719 return ret_val; 18720 } 18721 18722 #ifdef PERF_TRAMPOLINE 18723 18724 #include <sys/mman.h> 18725 #include <fcntl.h> 18726 #include <unistd.h> 18727 18728 static FILE * 18729 perf_map_get_file(void) 18730 { 18731 static FILE *perf_map_file = NULL; 18732 if (perf_map_file) { 18733 return perf_map_file; 18734 } 18735 char filename[100]; 18736 pid_t pid = getpid(); 18737 // Location and file name of perf map is hard-coded in perf tool. 18738 // Use exclusive create flag wit nofollow to prevent symlink attacks. 18739 int flags = O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC; 18740 snprintf(filename, sizeof(filename) - 1, "/tmp/perf-%jd.map", 18741 (intmax_t)pid); 18742 int fd = open(filename, flags, 0600); 18743 if (fd == -1) { 18744 return NULL; 18745 } 18746 perf_map_file = fdopen(fd, "w"); 18747 if (!perf_map_file) { 18748 close(fd); 18749 return NULL; 18750 } 18751 return perf_map_file; 18752 } 18753 18754 static void 18755 perf_map_write_entry(JSContext *ctx, const void *code_addr, unsigned int code_size, JSFunctionBytecode *b) 18756 { 18757 FILE *method_file = perf_map_get_file(); 18758 const char *atom_entry = NULL; 18759 const char *atom_filename = NULL; 18760 const char *filename = NULL; 18761 int line = 0; 18762 if (b->has_debug) { 18763 line = b->debug.line_num; 18764 } 18765 if (b->func_name != JS_ATOM_NULL) { 18766 atom_entry = JS_AtomToCString(ctx, b->func_name); 18767 } 18768 if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { 18769 atom_filename = JS_AtomToCString(ctx, b->debug.filename); 18770 } 18771 if (NULL == atom_filename) { 18772 filename = "<unknown>"; 18773 } else { 18774 filename = atom_filename; 18775 } 18776 if (NULL == atom_entry) { 18777 fprintf(method_file, "%p %x js@%s:%u\n", code_addr, code_size, filename, line); 18778 } else { 18779 fprintf(method_file, "%p %x js::%s@%s:%u\n", code_addr, code_size, atom_entry, filename, line); 18780 } 18781 fflush(method_file); 18782 JS_FreeCString(ctx, atom_entry); 18783 JS_FreeCString(ctx, atom_filename); 18784 } 18785 18786 typedef struct { 18787 JSContext *caller_ctx; 18788 JSValueConst func_obj; 18789 JSValueConst this_obj; 18790 JSValueConst new_target; 18791 int argc; 18792 JSValue *argv; 18793 int flags; 18794 } CallInternalArgs; 18795 18796 typedef JSValue CallFn(CallInternalArgs *args); 18797 typedef JSValue TrampolineFn(CallInternalArgs *args, CallFn fn); 18798 18799 /** 18800 * For x86-64: 18801 * push %rbp; mov %rsp,%rbp; call *%rsi; pop %rbp; ret; 18802 */ 18803 char perf_trampoline_code[] = {0x55, 0x48, 0x89, 0xe5, 0xff, 0xd6, 0x5d, 0xc3}; 18804 18805 void *compile_trampoline() 18806 { 18807 size_t mem_size = 4096 * 16; 18808 char *memory = 18809 mmap(NULL, // address 18810 mem_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 18811 -1, // fd (not used here) 18812 0); // offset (not used here) 18813 memcpy(memory, perf_trampoline_code, 8); 18814 mprotect(memory, mem_size, PROT_READ | PROT_EXEC); 18815 return memory; 18816 } 18817 18818 static JSValue fallback_trampoline(CallInternalArgs *ci_args, CallFn fn) 18819 { 18820 JSValue value; 18821 value = fn(ci_args); 18822 return value; 18823 } 18824 18825 static JSValue JS_CallInternalStruct(CallInternalArgs *ci_args) 18826 { 18827 return __JS_CallInternal(ci_args->caller_ctx, ci_args->func_obj, 18828 ci_args->this_obj, ci_args->new_target, 18829 ci_args->argc, ci_args->argv, ci_args->flags); 18830 } 18831 18832 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 18833 JSValueConst this_obj, JSValueConst new_target, 18834 int argc, JSValue *argv, int flags) 18835 { 18836 CallInternalArgs ci_args; 18837 JSObject *p; 18838 JSFunctionBytecode *b = NULL; 18839 18840 ci_args.caller_ctx = caller_ctx; 18841 ci_args.func_obj = func_obj; 18842 ci_args.this_obj = this_obj; 18843 ci_args.new_target = new_target; 18844 ci_args.argc = argc; 18845 ci_args.argv = argv; 18846 ci_args.flags = flags; 18847 18848 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { 18849 if (flags & JS_CALL_FLAG_GENERATOR) { 18850 JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); 18851 JSStackFrame *sf; 18852 /* func_obj get contains a pointer to JSFuncAsyncState */ 18853 /* the stack frame is already allocated */ 18854 sf = &s->frame; 18855 p = JS_VALUE_GET_OBJ(sf->cur_func); 18856 b = p->u.func.function_bytecode; 18857 } 18858 } else { 18859 p = JS_VALUE_GET_OBJ(func_obj); 18860 if (p->class_id == JS_CLASS_BYTECODE_FUNCTION) { 18861 b = p->u.func.function_bytecode; 18862 } 18863 } 18864 18865 if (b) { 18866 TrampolineFn *fn; 18867 if (!b->perf_trampoline) { 18868 b->perf_trampoline = compile_trampoline(); 18869 if (b->perf_trampoline) { 18870 perf_map_write_entry(caller_ctx, b->perf_trampoline, 8, b); 18871 } 18872 } 18873 fn = b->perf_trampoline; 18874 if (fn) { 18875 return fn(&ci_args, JS_CallInternalStruct); 18876 } 18877 } 18878 18879 return fallback_trampoline(&ci_args, JS_CallInternalStruct); 18880 //return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags); 18881 } 18882 18883 #else 18884 18885 static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 18886 JSValueConst this_obj, JSValueConst new_target, 18887 int argc, JSValue *argv, int flags) 18888 { 18889 return __JS_CallInternal(caller_ctx, func_obj, this_obj, new_target, argc, argv, flags); 18890 } 18891 18892 #endif /* PERF_TRAMPOLINE */ 18893 18894 JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, 18895 int argc, JSValueConst *argv) 18896 { 18897 return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, 18898 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); 18899 } 18900 18901 static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, 18902 int argc, JSValueConst *argv) 18903 { 18904 JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, 18905 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); 18906 JS_FreeValue(ctx, func_obj); 18907 return res; 18908 } 18909 18910 /* warning: the refcount of the context is not incremented. Return 18911 NULL in case of exception (case of revoked proxy only) */ 18912 static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj) 18913 { 18914 JSObject *p; 18915 JSContext *realm; 18916 18917 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) 18918 return ctx; 18919 p = JS_VALUE_GET_OBJ(func_obj); 18920 switch(p->class_id) { 18921 case JS_CLASS_C_FUNCTION: 18922 realm = p->u.cfunc.realm; 18923 break; 18924 case JS_CLASS_BYTECODE_FUNCTION: 18925 case JS_CLASS_GENERATOR_FUNCTION: 18926 case JS_CLASS_ASYNC_FUNCTION: 18927 case JS_CLASS_ASYNC_GENERATOR_FUNCTION: 18928 { 18929 JSFunctionBytecode *b; 18930 b = p->u.func.function_bytecode; 18931 realm = b->realm; 18932 } 18933 break; 18934 case JS_CLASS_PROXY: 18935 { 18936 JSProxyData *s = p->u.opaque; 18937 if (!s) 18938 return ctx; 18939 if (s->is_revoked) { 18940 JS_ThrowTypeErrorRevokedProxy(ctx); 18941 return NULL; 18942 } else { 18943 realm = JS_GetFunctionRealm(ctx, s->target); 18944 } 18945 } 18946 break; 18947 case JS_CLASS_BOUND_FUNCTION: 18948 { 18949 JSBoundFunction *bf = p->u.bound_function; 18950 realm = JS_GetFunctionRealm(ctx, bf->func_obj); 18951 } 18952 break; 18953 default: 18954 realm = ctx; 18955 break; 18956 } 18957 return realm; 18958 } 18959 18960 static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor, 18961 int class_id) 18962 { 18963 JSValue proto, obj; 18964 JSContext *realm; 18965 18966 if (JS_IsUndefined(ctor)) { 18967 proto = JS_DupValue(ctx, ctx->class_proto[class_id]); 18968 } else { 18969 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); 18970 if (JS_IsException(proto)) 18971 return proto; 18972 if (!JS_IsObject(proto)) { 18973 JS_FreeValue(ctx, proto); 18974 realm = JS_GetFunctionRealm(ctx, ctor); 18975 if (!realm) 18976 return JS_EXCEPTION; 18977 proto = JS_DupValue(ctx, realm->class_proto[class_id]); 18978 } 18979 } 18980 obj = JS_NewObjectProtoClass(ctx, proto, class_id); 18981 JS_FreeValue(ctx, proto); 18982 return obj; 18983 } 18984 18985 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ 18986 static JSValue JS_CallConstructorInternal(JSContext *ctx, 18987 JSValueConst func_obj, 18988 JSValueConst new_target, 18989 int argc, JSValue *argv, int flags) 18990 { 18991 JSObject *p; 18992 JSFunctionBytecode *b; 18993 18994 if (js_poll_interrupts(ctx)) 18995 return JS_EXCEPTION; 18996 flags |= JS_CALL_FLAG_CONSTRUCTOR; 18997 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) 18998 goto not_a_function; 18999 p = JS_VALUE_GET_OBJ(func_obj); 19000 if (unlikely(!p->is_constructor)) 19001 return JS_ThrowTypeError(ctx, "not a constructor"); 19002 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { 19003 JSClassCall *call_func; 19004 call_func = ctx->rt->class_array[p->class_id].call; 19005 if (!call_func) { 19006 not_a_function: 19007 return JS_ThrowTypeError(ctx, "not a function"); 19008 } 19009 return call_func(ctx, func_obj, new_target, argc, 19010 (JSValueConst *)argv, flags); 19011 } 19012 19013 b = p->u.func.function_bytecode; 19014 if (b->is_derived_class_constructor) { 19015 return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); 19016 } else { 19017 JSValue obj, ret; 19018 /* legacy constructor behavior */ 19019 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); 19020 if (JS_IsException(obj)) 19021 return JS_EXCEPTION; 19022 ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); 19023 if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || 19024 JS_IsException(ret)) { 19025 JS_FreeValue(ctx, obj); 19026 return ret; 19027 } else { 19028 JS_FreeValue(ctx, ret); 19029 return obj; 19030 } 19031 } 19032 } 19033 19034 JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, 19035 JSValueConst new_target, 19036 int argc, JSValueConst *argv) 19037 { 19038 return JS_CallConstructorInternal(ctx, func_obj, new_target, 19039 argc, (JSValue *)argv, 19040 JS_CALL_FLAG_COPY_ARGV); 19041 } 19042 19043 JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, 19044 int argc, JSValueConst *argv) 19045 { 19046 return JS_CallConstructorInternal(ctx, func_obj, func_obj, 19047 argc, (JSValue *)argv, 19048 JS_CALL_FLAG_COPY_ARGV); 19049 } 19050 19051 JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom, 19052 int argc, JSValueConst *argv) 19053 { 19054 JSValue func_obj; 19055 func_obj = JS_GetProperty(ctx, this_val, atom); 19056 if (JS_IsException(func_obj)) 19057 return func_obj; 19058 return JS_CallFree(ctx, func_obj, this_val, argc, argv); 19059 } 19060 19061 static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, 19062 int argc, JSValueConst *argv) 19063 { 19064 JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); 19065 JS_FreeValue(ctx, this_val); 19066 return res; 19067 } 19068 19069 /* JSAsyncFunctionState (used by generator and async functions) */ 19070 static JSAsyncFunctionState *async_func_init(JSContext *ctx, 19071 JSValueConst func_obj, JSValueConst this_obj, 19072 int argc, JSValueConst *argv) 19073 { 19074 JSAsyncFunctionState *s; 19075 JSObject *p; 19076 JSFunctionBytecode *b; 19077 JSStackFrame *sf; 19078 int local_count, i, arg_buf_len, n; 19079 19080 s = js_mallocz(ctx, sizeof(*s)); 19081 if (!s) 19082 return NULL; 19083 s->header.ref_count = 1; 19084 add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); 19085 19086 sf = &s->frame; 19087 init_list_head(&sf->var_ref_list); 19088 p = JS_VALUE_GET_OBJ(func_obj); 19089 b = p->u.func.function_bytecode; 19090 sf->js_mode = b->js_mode | JS_MODE_ASYNC; 19091 sf->cur_pc = b->byte_code_buf; 19092 arg_buf_len = max_int(b->arg_count, argc); 19093 local_count = arg_buf_len + b->var_count + b->stack_size; 19094 sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); 19095 if (!sf->arg_buf) { 19096 js_free(ctx, s); 19097 return NULL; 19098 } 19099 sf->cur_func = JS_DupValue(ctx, func_obj); 19100 s->this_val = JS_DupValue(ctx, this_obj); 19101 s->argc = argc; 19102 sf->arg_count = arg_buf_len; 19103 sf->var_buf = sf->arg_buf + arg_buf_len; 19104 sf->cur_sp = sf->var_buf + b->var_count; 19105 for(i = 0; i < argc; i++) 19106 sf->arg_buf[i] = JS_DupValue(ctx, argv[i]); 19107 n = arg_buf_len + b->var_count; 19108 for(i = argc; i < n; i++) 19109 sf->arg_buf[i] = JS_UNDEFINED; 19110 s->resolving_funcs[0] = JS_UNDEFINED; 19111 s->resolving_funcs[1] = JS_UNDEFINED; 19112 s->is_completed = FALSE; 19113 return s; 19114 } 19115 19116 static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) 19117 { 19118 JSStackFrame *sf = &s->frame; 19119 JSValue *sp; 19120 19121 if (sf->arg_buf) { 19122 /* cannot free the function if it is running */ 19123 assert(sf->cur_sp != NULL); 19124 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { 19125 JS_FreeValueRT(rt, *sp); 19126 } 19127 js_free_rt(rt, sf->arg_buf); 19128 sf->arg_buf = NULL; 19129 } 19130 JS_FreeValueRT(rt, sf->cur_func); 19131 JS_FreeValueRT(rt, s->this_val); 19132 } 19133 19134 static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) 19135 { 19136 JSRuntime *rt = ctx->rt; 19137 JSStackFrame *sf = &s->frame; 19138 JSValue func_obj, ret; 19139 19140 assert(!s->is_completed); 19141 if (js_check_stack_overflow(ctx->rt, 0)) { 19142 ret = JS_ThrowStackOverflow(ctx); 19143 } else { 19144 /* the tag does not matter provided it is not an object */ 19145 func_obj = JS_MKPTR(JS_TAG_INT, s); 19146 ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, 19147 s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); 19148 } 19149 if (JS_IsException(ret) || JS_IsUndefined(ret)) { 19150 if (JS_IsUndefined(ret)) { 19151 ret = sf->cur_sp[-1]; 19152 sf->cur_sp[-1] = JS_UNDEFINED; 19153 } 19154 /* end of execution */ 19155 s->is_completed = TRUE; 19156 19157 /* close the closure variables. */ 19158 close_var_refs(rt, sf); 19159 19160 async_func_free_frame(rt, s); 19161 } 19162 return ret; 19163 } 19164 19165 static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) 19166 { 19167 /* cannot close the closure variables here because it would 19168 potentially modify the object graph */ 19169 if (!s->is_completed) { 19170 async_func_free_frame(rt, s); 19171 } 19172 19173 JS_FreeValueRT(rt, s->resolving_funcs[0]); 19174 JS_FreeValueRT(rt, s->resolving_funcs[1]); 19175 19176 remove_gc_object(&s->header); 19177 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) { 19178 list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list); 19179 } else { 19180 js_free_rt(rt, s); 19181 } 19182 } 19183 19184 static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) 19185 { 19186 if (--s->header.ref_count == 0) { 19187 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { 19188 list_del(&s->header.link); 19189 list_add(&s->header.link, &rt->gc_zero_ref_count_list); 19190 if (rt->gc_phase == JS_GC_PHASE_NONE) { 19191 free_zero_refcount(rt); 19192 } 19193 } 19194 } 19195 } 19196 19197 /* Generators */ 19198 19199 typedef enum JSGeneratorStateEnum { 19200 JS_GENERATOR_STATE_SUSPENDED_START, 19201 JS_GENERATOR_STATE_SUSPENDED_YIELD, 19202 JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, 19203 JS_GENERATOR_STATE_EXECUTING, 19204 JS_GENERATOR_STATE_COMPLETED, 19205 } JSGeneratorStateEnum; 19206 19207 typedef struct JSGeneratorData { 19208 JSGeneratorStateEnum state; 19209 JSAsyncFunctionState *func_state; 19210 } JSGeneratorData; 19211 19212 static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) 19213 { 19214 if (s->state == JS_GENERATOR_STATE_COMPLETED) 19215 return; 19216 if (s->func_state) { 19217 async_func_free(rt, s->func_state); 19218 s->func_state = NULL; 19219 } 19220 s->state = JS_GENERATOR_STATE_COMPLETED; 19221 } 19222 19223 static void js_generator_finalizer(JSRuntime *rt, JSValue obj) 19224 { 19225 JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); 19226 19227 if (s) { 19228 free_generator_stack_rt(rt, s); 19229 js_free_rt(rt, s); 19230 } 19231 } 19232 19233 static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) 19234 { 19235 free_generator_stack_rt(ctx->rt, s); 19236 } 19237 19238 static void js_generator_mark(JSRuntime *rt, JSValueConst val, 19239 JS_MarkFunc *mark_func) 19240 { 19241 JSObject *p = JS_VALUE_GET_OBJ(val); 19242 JSGeneratorData *s = p->u.generator_data; 19243 19244 if (!s || !s->func_state) 19245 return; 19246 mark_func(rt, &s->func_state->header); 19247 } 19248 19249 /* XXX: use enum */ 19250 #define GEN_MAGIC_NEXT 0 19251 #define GEN_MAGIC_RETURN 1 19252 #define GEN_MAGIC_THROW 2 19253 19254 static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, 19255 int argc, JSValueConst *argv, 19256 BOOL *pdone, int magic) 19257 { 19258 JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); 19259 JSStackFrame *sf; 19260 JSValue ret, func_ret; 19261 19262 *pdone = TRUE; 19263 if (!s) 19264 return JS_ThrowTypeError(ctx, "not a generator"); 19265 switch(s->state) { 19266 default: 19267 case JS_GENERATOR_STATE_SUSPENDED_START: 19268 sf = &s->func_state->frame; 19269 if (magic == GEN_MAGIC_NEXT) { 19270 goto exec_no_arg; 19271 } else { 19272 free_generator_stack(ctx, s); 19273 goto done; 19274 } 19275 break; 19276 case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: 19277 case JS_GENERATOR_STATE_SUSPENDED_YIELD: 19278 sf = &s->func_state->frame; 19279 /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ 19280 ret = JS_DupValue(ctx, argv[0]); 19281 if (magic == GEN_MAGIC_THROW && 19282 s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { 19283 JS_Throw(ctx, ret); 19284 s->func_state->throw_flag = TRUE; 19285 } else { 19286 sf->cur_sp[-1] = ret; 19287 sf->cur_sp[0] = JS_NewInt32(ctx, magic); 19288 sf->cur_sp++; 19289 exec_no_arg: 19290 s->func_state->throw_flag = FALSE; 19291 } 19292 s->state = JS_GENERATOR_STATE_EXECUTING; 19293 func_ret = async_func_resume(ctx, s->func_state); 19294 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; 19295 if (s->func_state->is_completed) { 19296 /* finalize the execution in case of exception or normal return */ 19297 free_generator_stack(ctx, s); 19298 return func_ret; 19299 } else { 19300 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); 19301 /* get the returned yield value at the top of the stack */ 19302 ret = sf->cur_sp[-1]; 19303 sf->cur_sp[-1] = JS_UNDEFINED; 19304 if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { 19305 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; 19306 /* return (value, done) object */ 19307 *pdone = 2; 19308 } else { 19309 *pdone = FALSE; 19310 } 19311 } 19312 break; 19313 case JS_GENERATOR_STATE_COMPLETED: 19314 done: 19315 /* execution is finished */ 19316 switch(magic) { 19317 default: 19318 case GEN_MAGIC_NEXT: 19319 ret = JS_UNDEFINED; 19320 break; 19321 case GEN_MAGIC_RETURN: 19322 ret = JS_DupValue(ctx, argv[0]); 19323 break; 19324 case GEN_MAGIC_THROW: 19325 ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0])); 19326 break; 19327 } 19328 break; 19329 case JS_GENERATOR_STATE_EXECUTING: 19330 ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); 19331 break; 19332 } 19333 return ret; 19334 } 19335 19336 static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, 19337 JSValueConst this_obj, 19338 int argc, JSValueConst *argv, 19339 int flags) 19340 { 19341 JSValue obj, func_ret; 19342 JSGeneratorData *s; 19343 19344 s = js_mallocz(ctx, sizeof(*s)); 19345 if (!s) 19346 return JS_EXCEPTION; 19347 s->state = JS_GENERATOR_STATE_SUSPENDED_START; 19348 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); 19349 if (!s->func_state) { 19350 s->state = JS_GENERATOR_STATE_COMPLETED; 19351 goto fail; 19352 } 19353 19354 /* execute the function up to 'OP_initial_yield' */ 19355 func_ret = async_func_resume(ctx, s->func_state); 19356 if (JS_IsException(func_ret)) 19357 goto fail; 19358 JS_FreeValue(ctx, func_ret); 19359 19360 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); 19361 if (JS_IsException(obj)) 19362 goto fail; 19363 JS_SetOpaque(obj, s); 19364 return obj; 19365 fail: 19366 free_generator_stack_rt(ctx->rt, s); 19367 js_free(ctx, s); 19368 return JS_EXCEPTION; 19369 } 19370 19371 /* AsyncFunction */ 19372 19373 static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) 19374 { 19375 JSObject *p = JS_VALUE_GET_OBJ(val); 19376 JSAsyncFunctionState *s = p->u.async_function_data; 19377 if (s) { 19378 async_func_free(rt, s); 19379 } 19380 } 19381 19382 static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, 19383 JS_MarkFunc *mark_func) 19384 { 19385 JSObject *p = JS_VALUE_GET_OBJ(val); 19386 JSAsyncFunctionState *s = p->u.async_function_data; 19387 if (s) { 19388 mark_func(rt, &s->header); 19389 } 19390 } 19391 19392 static int js_async_function_resolve_create(JSContext *ctx, 19393 JSAsyncFunctionState *s, 19394 JSValue *resolving_funcs) 19395 { 19396 int i; 19397 JSObject *p; 19398 19399 for(i = 0; i < 2; i++) { 19400 resolving_funcs[i] = 19401 JS_NewObjectProtoClass(ctx, ctx->function_proto, 19402 JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); 19403 if (JS_IsException(resolving_funcs[i])) { 19404 if (i == 1) 19405 JS_FreeValue(ctx, resolving_funcs[0]); 19406 return -1; 19407 } 19408 p = JS_VALUE_GET_OBJ(resolving_funcs[i]); 19409 s->header.ref_count++; 19410 p->u.async_function_data = s; 19411 } 19412 return 0; 19413 } 19414 19415 static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s) 19416 { 19417 JSValue func_ret, ret2; 19418 19419 func_ret = async_func_resume(ctx, s); 19420 if (s->is_completed) { 19421 if (JS_IsException(func_ret)) { 19422 JSValue error; 19423 fail: 19424 error = JS_GetException(ctx); 19425 ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, 19426 1, (JSValueConst *)&error); 19427 JS_FreeValue(ctx, error); 19428 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ 19429 } else { 19430 /* normal return */ 19431 ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, 19432 1, (JSValueConst *)&func_ret); 19433 JS_FreeValue(ctx, func_ret); 19434 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ 19435 } 19436 } else { 19437 JSValue value, promise, resolving_funcs[2], resolving_funcs1[2]; 19438 int i, res; 19439 19440 value = s->frame.cur_sp[-1]; 19441 s->frame.cur_sp[-1] = JS_UNDEFINED; 19442 19443 /* await */ 19444 JS_FreeValue(ctx, func_ret); /* not used */ 19445 promise = js_promise_resolve(ctx, ctx->promise_ctor, 19446 1, (JSValueConst *)&value, 0); 19447 JS_FreeValue(ctx, value); 19448 if (JS_IsException(promise)) 19449 goto fail; 19450 if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { 19451 JS_FreeValue(ctx, promise); 19452 goto fail; 19453 } 19454 19455 /* Note: no need to create 'thrownawayCapability' as in 19456 the spec */ 19457 for(i = 0; i < 2; i++) 19458 resolving_funcs1[i] = JS_UNDEFINED; 19459 res = perform_promise_then(ctx, promise, 19460 (JSValueConst *)resolving_funcs, 19461 (JSValueConst *)resolving_funcs1); 19462 JS_FreeValue(ctx, promise); 19463 for(i = 0; i < 2; i++) 19464 JS_FreeValue(ctx, resolving_funcs[i]); 19465 if (res) 19466 goto fail; 19467 } 19468 } 19469 19470 static JSValue js_async_function_resolve_call(JSContext *ctx, 19471 JSValueConst func_obj, 19472 JSValueConst this_obj, 19473 int argc, JSValueConst *argv, 19474 int flags) 19475 { 19476 JSObject *p = JS_VALUE_GET_OBJ(func_obj); 19477 JSAsyncFunctionState *s = p->u.async_function_data; 19478 BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; 19479 JSValueConst arg; 19480 19481 if (argc > 0) 19482 arg = argv[0]; 19483 else 19484 arg = JS_UNDEFINED; 19485 s->throw_flag = is_reject; 19486 if (is_reject) { 19487 JS_Throw(ctx, JS_DupValue(ctx, arg)); 19488 } else { 19489 /* return value of await */ 19490 s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); 19491 } 19492 js_async_function_resume(ctx, s); 19493 return JS_UNDEFINED; 19494 } 19495 19496 static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, 19497 JSValueConst this_obj, 19498 int argc, JSValueConst *argv, int flags) 19499 { 19500 JSValue promise; 19501 JSAsyncFunctionState *s; 19502 19503 s = async_func_init(ctx, func_obj, this_obj, argc, argv); 19504 if (!s) 19505 return JS_EXCEPTION; 19506 19507 promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); 19508 if (JS_IsException(promise)) { 19509 async_func_free(ctx->rt, s); 19510 return JS_EXCEPTION; 19511 } 19512 19513 js_async_function_resume(ctx, s); 19514 19515 async_func_free(ctx->rt, s); 19516 19517 return promise; 19518 } 19519 19520 /* AsyncGenerator */ 19521 19522 typedef enum JSAsyncGeneratorStateEnum { 19523 JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, 19524 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, 19525 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, 19526 JS_ASYNC_GENERATOR_STATE_EXECUTING, 19527 JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, 19528 JS_ASYNC_GENERATOR_STATE_COMPLETED, 19529 } JSAsyncGeneratorStateEnum; 19530 19531 typedef struct JSAsyncGeneratorRequest { 19532 struct list_head link; 19533 /* completion */ 19534 int completion_type; /* GEN_MAGIC_x */ 19535 JSValue result; 19536 /* promise capability */ 19537 JSValue promise; 19538 JSValue resolving_funcs[2]; 19539 } JSAsyncGeneratorRequest; 19540 19541 typedef struct JSAsyncGeneratorData { 19542 JSObject *generator; /* back pointer to the object (const) */ 19543 JSAsyncGeneratorStateEnum state; 19544 /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ 19545 JSAsyncFunctionState *func_state; 19546 struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ 19547 } JSAsyncGeneratorData; 19548 19549 static void js_async_generator_free(JSRuntime *rt, 19550 JSAsyncGeneratorData *s) 19551 { 19552 struct list_head *el, *el1; 19553 JSAsyncGeneratorRequest *req; 19554 19555 list_for_each_safe(el, el1, &s->queue) { 19556 req = list_entry(el, JSAsyncGeneratorRequest, link); 19557 JS_FreeValueRT(rt, req->result); 19558 JS_FreeValueRT(rt, req->promise); 19559 JS_FreeValueRT(rt, req->resolving_funcs[0]); 19560 JS_FreeValueRT(rt, req->resolving_funcs[1]); 19561 js_free_rt(rt, req); 19562 } 19563 if (s->func_state) 19564 async_func_free(rt, s->func_state); 19565 js_free_rt(rt, s); 19566 } 19567 19568 static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) 19569 { 19570 JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); 19571 19572 if (s) { 19573 js_async_generator_free(rt, s); 19574 } 19575 } 19576 19577 static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, 19578 JS_MarkFunc *mark_func) 19579 { 19580 JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); 19581 struct list_head *el; 19582 JSAsyncGeneratorRequest *req; 19583 if (s) { 19584 list_for_each(el, &s->queue) { 19585 req = list_entry(el, JSAsyncGeneratorRequest, link); 19586 JS_MarkValue(rt, req->result, mark_func); 19587 JS_MarkValue(rt, req->promise, mark_func); 19588 JS_MarkValue(rt, req->resolving_funcs[0], mark_func); 19589 JS_MarkValue(rt, req->resolving_funcs[1], mark_func); 19590 } 19591 if (s->func_state) { 19592 mark_func(rt, &s->func_state->header); 19593 } 19594 } 19595 } 19596 19597 static JSValue js_async_generator_resolve_function(JSContext *ctx, 19598 JSValueConst this_obj, 19599 int argc, JSValueConst *argv, 19600 int magic, JSValue *func_data); 19601 19602 static int js_async_generator_resolve_function_create(JSContext *ctx, 19603 JSValueConst generator, 19604 JSValue *resolving_funcs, 19605 BOOL is_resume_next) 19606 { 19607 int i; 19608 JSValue func; 19609 19610 for(i = 0; i < 2; i++) { 19611 func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, 19612 i + is_resume_next * 2, 1, &generator); 19613 if (JS_IsException(func)) { 19614 if (i == 1) 19615 JS_FreeValue(ctx, resolving_funcs[0]); 19616 return -1; 19617 } 19618 resolving_funcs[i] = func; 19619 } 19620 return 0; 19621 } 19622 19623 static int js_async_generator_await(JSContext *ctx, 19624 JSAsyncGeneratorData *s, 19625 JSValueConst value) 19626 { 19627 JSValue promise, resolving_funcs[2], resolving_funcs1[2]; 19628 int i, res; 19629 19630 promise = js_promise_resolve(ctx, ctx->promise_ctor, 19631 1, &value, 0); 19632 if (JS_IsException(promise)) 19633 goto fail; 19634 19635 if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), 19636 resolving_funcs, FALSE)) { 19637 JS_FreeValue(ctx, promise); 19638 goto fail; 19639 } 19640 19641 /* Note: no need to create 'thrownawayCapability' as in 19642 the spec */ 19643 for(i = 0; i < 2; i++) 19644 resolving_funcs1[i] = JS_UNDEFINED; 19645 res = perform_promise_then(ctx, promise, 19646 (JSValueConst *)resolving_funcs, 19647 (JSValueConst *)resolving_funcs1); 19648 JS_FreeValue(ctx, promise); 19649 for(i = 0; i < 2; i++) 19650 JS_FreeValue(ctx, resolving_funcs[i]); 19651 if (res) 19652 goto fail; 19653 return 0; 19654 fail: 19655 return -1; 19656 } 19657 19658 static void js_async_generator_resolve_or_reject(JSContext *ctx, 19659 JSAsyncGeneratorData *s, 19660 JSValueConst result, 19661 int is_reject) 19662 { 19663 JSAsyncGeneratorRequest *next; 19664 JSValue ret; 19665 19666 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); 19667 list_del(&next->link); 19668 ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, 19669 &result); 19670 JS_FreeValue(ctx, ret); 19671 JS_FreeValue(ctx, next->result); 19672 JS_FreeValue(ctx, next->promise); 19673 JS_FreeValue(ctx, next->resolving_funcs[0]); 19674 JS_FreeValue(ctx, next->resolving_funcs[1]); 19675 js_free(ctx, next); 19676 } 19677 19678 static void js_async_generator_resolve(JSContext *ctx, 19679 JSAsyncGeneratorData *s, 19680 JSValueConst value, 19681 BOOL done) 19682 { 19683 JSValue result; 19684 result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done); 19685 /* XXX: better exception handling ? */ 19686 js_async_generator_resolve_or_reject(ctx, s, result, 0); 19687 JS_FreeValue(ctx, result); 19688 } 19689 19690 static void js_async_generator_reject(JSContext *ctx, 19691 JSAsyncGeneratorData *s, 19692 JSValueConst exception) 19693 { 19694 js_async_generator_resolve_or_reject(ctx, s, exception, 1); 19695 } 19696 19697 static void js_async_generator_complete(JSContext *ctx, 19698 JSAsyncGeneratorData *s) 19699 { 19700 if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { 19701 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; 19702 async_func_free(ctx->rt, s->func_state); 19703 s->func_state = NULL; 19704 } 19705 } 19706 19707 static int js_async_generator_completed_return(JSContext *ctx, 19708 JSAsyncGeneratorData *s, 19709 JSValueConst value) 19710 { 19711 JSValue promise, resolving_funcs[2], resolving_funcs1[2]; 19712 int res; 19713 19714 // Can fail looking up JS_ATOM_constructor when is_reject==0. 19715 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, 19716 /*is_reject*/0); 19717 // A poisoned .constructor property is observable and the resulting 19718 // exception should be delivered to the catch handler. 19719 if (JS_IsException(promise)) { 19720 JSValue err = JS_GetException(ctx); 19721 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err, 19722 /*is_reject*/1); 19723 JS_FreeValue(ctx, err); 19724 if (JS_IsException(promise)) 19725 return -1; 19726 } 19727 if (js_async_generator_resolve_function_create(ctx, 19728 JS_MKPTR(JS_TAG_OBJECT, s->generator), 19729 resolving_funcs1, 19730 TRUE)) { 19731 JS_FreeValue(ctx, promise); 19732 return -1; 19733 } 19734 resolving_funcs[0] = JS_UNDEFINED; 19735 resolving_funcs[1] = JS_UNDEFINED; 19736 res = perform_promise_then(ctx, promise, 19737 (JSValueConst *)resolving_funcs1, 19738 (JSValueConst *)resolving_funcs); 19739 JS_FreeValue(ctx, resolving_funcs1[0]); 19740 JS_FreeValue(ctx, resolving_funcs1[1]); 19741 JS_FreeValue(ctx, promise); 19742 return res; 19743 } 19744 19745 static void js_async_generator_resume_next(JSContext *ctx, 19746 JSAsyncGeneratorData *s) 19747 { 19748 JSAsyncGeneratorRequest *next; 19749 JSValue func_ret, value; 19750 19751 for(;;) { 19752 if (list_empty(&s->queue)) 19753 break; 19754 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); 19755 switch(s->state) { 19756 case JS_ASYNC_GENERATOR_STATE_EXECUTING: 19757 /* only happens when restarting execution after await() */ 19758 goto resume_exec; 19759 case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: 19760 goto done; 19761 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: 19762 if (next->completion_type == GEN_MAGIC_NEXT) { 19763 goto exec_no_arg; 19764 } else { 19765 js_async_generator_complete(ctx, s); 19766 } 19767 break; 19768 case JS_ASYNC_GENERATOR_STATE_COMPLETED: 19769 if (next->completion_type == GEN_MAGIC_NEXT) { 19770 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); 19771 } else if (next->completion_type == GEN_MAGIC_RETURN) { 19772 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; 19773 js_async_generator_completed_return(ctx, s, next->result); 19774 } else { 19775 js_async_generator_reject(ctx, s, next->result); 19776 } 19777 goto done; 19778 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: 19779 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: 19780 value = JS_DupValue(ctx, next->result); 19781 if (next->completion_type == GEN_MAGIC_THROW && 19782 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { 19783 JS_Throw(ctx, value); 19784 s->func_state->throw_flag = TRUE; 19785 } else { 19786 /* 'yield' returns a value. 'yield *' also returns a value 19787 in case the 'throw' method is called */ 19788 s->func_state->frame.cur_sp[-1] = value; 19789 s->func_state->frame.cur_sp[0] = 19790 JS_NewInt32(ctx, next->completion_type); 19791 s->func_state->frame.cur_sp++; 19792 exec_no_arg: 19793 s->func_state->throw_flag = FALSE; 19794 } 19795 s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; 19796 resume_exec: 19797 func_ret = async_func_resume(ctx, s->func_state); 19798 if (s->func_state->is_completed) { 19799 if (JS_IsException(func_ret)) { 19800 value = JS_GetException(ctx); 19801 js_async_generator_complete(ctx, s); 19802 js_async_generator_reject(ctx, s, value); 19803 JS_FreeValue(ctx, value); 19804 } else { 19805 /* end of function */ 19806 js_async_generator_complete(ctx, s); 19807 js_async_generator_resolve(ctx, s, func_ret, TRUE); 19808 JS_FreeValue(ctx, func_ret); 19809 } 19810 } else { 19811 int func_ret_code, ret; 19812 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); 19813 func_ret_code = JS_VALUE_GET_INT(func_ret); 19814 value = s->func_state->frame.cur_sp[-1]; 19815 s->func_state->frame.cur_sp[-1] = JS_UNDEFINED; 19816 switch(func_ret_code) { 19817 case FUNC_RET_YIELD: 19818 case FUNC_RET_YIELD_STAR: 19819 if (func_ret_code == FUNC_RET_YIELD_STAR) 19820 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; 19821 else 19822 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; 19823 js_async_generator_resolve(ctx, s, value, FALSE); 19824 JS_FreeValue(ctx, value); 19825 break; 19826 case FUNC_RET_AWAIT: 19827 ret = js_async_generator_await(ctx, s, value); 19828 JS_FreeValue(ctx, value); 19829 if (ret < 0) { 19830 /* exception: throw it */ 19831 s->func_state->throw_flag = TRUE; 19832 goto resume_exec; 19833 } 19834 goto done; 19835 default: 19836 abort(); 19837 } 19838 } 19839 break; 19840 default: 19841 abort(); 19842 } 19843 } 19844 done: ; 19845 } 19846 19847 static JSValue js_async_generator_resolve_function(JSContext *ctx, 19848 JSValueConst this_obj, 19849 int argc, JSValueConst *argv, 19850 int magic, JSValue *func_data) 19851 { 19852 BOOL is_reject = magic & 1; 19853 JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); 19854 JSValueConst arg = argv[0]; 19855 19856 /* XXX: what if s == NULL */ 19857 19858 if (magic >= 2) { 19859 /* resume next case in AWAITING_RETURN state */ 19860 assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || 19861 s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); 19862 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; 19863 if (is_reject) { 19864 js_async_generator_reject(ctx, s, arg); 19865 } else { 19866 js_async_generator_resolve(ctx, s, arg, TRUE); 19867 } 19868 } else { 19869 /* restart function execution after await() */ 19870 assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); 19871 s->func_state->throw_flag = is_reject; 19872 if (is_reject) { 19873 JS_Throw(ctx, JS_DupValue(ctx, arg)); 19874 } else { 19875 /* return value of await */ 19876 s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg); 19877 } 19878 js_async_generator_resume_next(ctx, s); 19879 } 19880 return JS_UNDEFINED; 19881 } 19882 19883 /* magic = GEN_MAGIC_x */ 19884 static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, 19885 int argc, JSValueConst *argv, 19886 int magic) 19887 { 19888 JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); 19889 JSValue promise, resolving_funcs[2]; 19890 JSAsyncGeneratorRequest *req; 19891 19892 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 19893 if (JS_IsException(promise)) 19894 return JS_EXCEPTION; 19895 if (!s) { 19896 JSValue err, res2; 19897 JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); 19898 err = JS_GetException(ctx); 19899 res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 19900 1, (JSValueConst *)&err); 19901 JS_FreeValue(ctx, err); 19902 JS_FreeValue(ctx, res2); 19903 JS_FreeValue(ctx, resolving_funcs[0]); 19904 JS_FreeValue(ctx, resolving_funcs[1]); 19905 return promise; 19906 } 19907 req = js_mallocz(ctx, sizeof(*req)); 19908 if (!req) 19909 goto fail; 19910 req->completion_type = magic; 19911 req->result = JS_DupValue(ctx, argv[0]); 19912 req->promise = JS_DupValue(ctx, promise); 19913 req->resolving_funcs[0] = resolving_funcs[0]; 19914 req->resolving_funcs[1] = resolving_funcs[1]; 19915 list_add_tail(&req->link, &s->queue); 19916 if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { 19917 js_async_generator_resume_next(ctx, s); 19918 } 19919 return promise; 19920 fail: 19921 JS_FreeValue(ctx, resolving_funcs[0]); 19922 JS_FreeValue(ctx, resolving_funcs[1]); 19923 JS_FreeValue(ctx, promise); 19924 return JS_EXCEPTION; 19925 } 19926 19927 static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj, 19928 JSValueConst this_obj, 19929 int argc, JSValueConst *argv, 19930 int flags) 19931 { 19932 JSValue obj, func_ret; 19933 JSAsyncGeneratorData *s; 19934 19935 s = js_mallocz(ctx, sizeof(*s)); 19936 if (!s) 19937 return JS_EXCEPTION; 19938 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; 19939 init_list_head(&s->queue); 19940 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); 19941 if (!s->func_state) 19942 goto fail; 19943 /* execute the function up to 'OP_initial_yield' (no yield nor 19944 await are possible) */ 19945 func_ret = async_func_resume(ctx, s->func_state); 19946 if (JS_IsException(func_ret)) 19947 goto fail; 19948 JS_FreeValue(ctx, func_ret); 19949 19950 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); 19951 if (JS_IsException(obj)) 19952 goto fail; 19953 s->generator = JS_VALUE_GET_OBJ(obj); 19954 JS_SetOpaque(obj, s); 19955 return obj; 19956 fail: 19957 js_async_generator_free(ctx->rt, s); 19958 return JS_EXCEPTION; 19959 } 19960 19961 /* JS parser */ 19962 19963 enum { 19964 TOK_NUMBER = -128, 19965 TOK_STRING, 19966 TOK_TEMPLATE, 19967 TOK_IDENT, 19968 TOK_REGEXP, 19969 /* warning: order matters (see js_parse_assign_expr) */ 19970 TOK_MUL_ASSIGN, 19971 TOK_DIV_ASSIGN, 19972 TOK_MOD_ASSIGN, 19973 TOK_PLUS_ASSIGN, 19974 TOK_MINUS_ASSIGN, 19975 TOK_SHL_ASSIGN, 19976 TOK_SAR_ASSIGN, 19977 TOK_SHR_ASSIGN, 19978 TOK_AND_ASSIGN, 19979 TOK_XOR_ASSIGN, 19980 TOK_OR_ASSIGN, 19981 #ifdef CONFIG_BIGNUM 19982 TOK_MATH_POW_ASSIGN, 19983 #endif 19984 TOK_POW_ASSIGN, 19985 TOK_LAND_ASSIGN, 19986 TOK_LOR_ASSIGN, 19987 TOK_DOUBLE_QUESTION_MARK_ASSIGN, 19988 TOK_DEC, 19989 TOK_INC, 19990 TOK_SHL, 19991 TOK_SAR, 19992 TOK_SHR, 19993 TOK_LT, 19994 TOK_LTE, 19995 TOK_GT, 19996 TOK_GTE, 19997 TOK_EQ, 19998 TOK_STRICT_EQ, 19999 TOK_NEQ, 20000 TOK_STRICT_NEQ, 20001 TOK_LAND, 20002 TOK_LOR, 20003 #ifdef CONFIG_BIGNUM 20004 TOK_MATH_POW, 20005 #endif 20006 TOK_POW, 20007 TOK_ARROW, 20008 TOK_ELLIPSIS, 20009 TOK_DOUBLE_QUESTION_MARK, 20010 TOK_QUESTION_MARK_DOT, 20011 TOK_ERROR, 20012 TOK_PRIVATE_NAME, 20013 TOK_EOF, 20014 /* keywords: WARNING: same order as atoms */ 20015 TOK_NULL, /* must be first */ 20016 TOK_FALSE, 20017 TOK_TRUE, 20018 TOK_IF, 20019 TOK_ELSE, 20020 TOK_RETURN, 20021 TOK_VAR, 20022 TOK_THIS, 20023 TOK_DELETE, 20024 TOK_VOID, 20025 TOK_TYPEOF, 20026 TOK_NEW, 20027 TOK_IN, 20028 TOK_INSTANCEOF, 20029 TOK_DO, 20030 TOK_WHILE, 20031 TOK_FOR, 20032 TOK_BREAK, 20033 TOK_CONTINUE, 20034 TOK_SWITCH, 20035 TOK_CASE, 20036 TOK_DEFAULT, 20037 TOK_THROW, 20038 TOK_TRY, 20039 TOK_CATCH, 20040 TOK_FINALLY, 20041 TOK_FUNCTION, 20042 TOK_DEBUGGER, 20043 TOK_WITH, 20044 /* FutureReservedWord */ 20045 TOK_CLASS, 20046 TOK_CONST, 20047 TOK_ENUM, 20048 TOK_EXPORT, 20049 TOK_EXTENDS, 20050 TOK_IMPORT, 20051 TOK_SUPER, 20052 /* FutureReservedWords when parsing strict mode code */ 20053 TOK_IMPLEMENTS, 20054 TOK_INTERFACE, 20055 TOK_LET, 20056 TOK_PACKAGE, 20057 TOK_PRIVATE, 20058 TOK_PROTECTED, 20059 TOK_PUBLIC, 20060 TOK_STATIC, 20061 TOK_YIELD, 20062 TOK_AWAIT, /* must be last */ 20063 TOK_OF, /* only used for js_parse_skip_parens_token() */ 20064 }; 20065 20066 #define TOK_FIRST_KEYWORD TOK_NULL 20067 #define TOK_LAST_KEYWORD TOK_AWAIT 20068 20069 /* unicode code points */ 20070 #define CP_NBSP 0x00a0 20071 #define CP_BOM 0xfeff 20072 20073 #define CP_LS 0x2028 20074 #define CP_PS 0x2029 20075 20076 typedef struct BlockEnv { 20077 struct BlockEnv *prev; 20078 JSAtom label_name; /* JS_ATOM_NULL if none */ 20079 int label_break; /* -1 if none */ 20080 int label_cont; /* -1 if none */ 20081 int drop_count; /* number of stack elements to drop */ 20082 int label_finally; /* -1 if none */ 20083 int scope_level; 20084 int has_iterator; 20085 } BlockEnv; 20086 20087 typedef struct JSGlobalVar { 20088 int cpool_idx; /* if >= 0, index in the constant pool for hoisted 20089 function defintion*/ 20090 uint8_t force_init : 1; /* force initialization to undefined */ 20091 uint8_t is_lexical : 1; /* global let/const definition */ 20092 uint8_t is_const : 1; /* const definition */ 20093 int scope_level; /* scope of definition */ 20094 JSAtom var_name; /* variable name */ 20095 } JSGlobalVar; 20096 20097 typedef struct RelocEntry { 20098 struct RelocEntry *next; 20099 uint32_t addr; /* address to patch */ 20100 int size; /* address size: 1, 2 or 4 bytes */ 20101 } RelocEntry; 20102 20103 typedef struct JumpSlot { 20104 int op; 20105 int size; 20106 int pos; 20107 int label; 20108 } JumpSlot; 20109 20110 typedef struct LabelSlot { 20111 int ref_count; 20112 int pos; /* phase 1 address, -1 means not resolved yet */ 20113 int pos2; /* phase 2 address, -1 means not resolved yet */ 20114 int addr; /* phase 3 address, -1 means not resolved yet */ 20115 RelocEntry *first_reloc; 20116 } LabelSlot; 20117 20118 typedef struct LineNumberSlot { 20119 uint32_t pc; 20120 int line_num; 20121 } LineNumberSlot; 20122 20123 typedef enum JSParseFunctionEnum { 20124 JS_PARSE_FUNC_STATEMENT, 20125 JS_PARSE_FUNC_VAR, 20126 JS_PARSE_FUNC_EXPR, 20127 JS_PARSE_FUNC_ARROW, 20128 JS_PARSE_FUNC_GETTER, 20129 JS_PARSE_FUNC_SETTER, 20130 JS_PARSE_FUNC_METHOD, 20131 JS_PARSE_FUNC_CLASS_STATIC_INIT, 20132 JS_PARSE_FUNC_CLASS_CONSTRUCTOR, 20133 JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, 20134 } JSParseFunctionEnum; 20135 20136 typedef enum JSParseExportEnum { 20137 JS_PARSE_EXPORT_NONE, 20138 JS_PARSE_EXPORT_NAMED, 20139 JS_PARSE_EXPORT_DEFAULT, 20140 } JSParseExportEnum; 20141 20142 typedef struct JSFunctionDef { 20143 JSContext *ctx; 20144 struct JSFunctionDef *parent; 20145 int parent_cpool_idx; /* index in the constant pool of the parent 20146 or -1 if none */ 20147 int parent_scope_level; /* scope level in parent at point of definition */ 20148 struct list_head child_list; /* list of JSFunctionDef.link */ 20149 struct list_head link; 20150 20151 BOOL is_eval; /* TRUE if eval code */ 20152 int eval_type; /* only valid if is_eval = TRUE */ 20153 BOOL is_global_var; /* TRUE if variables are not defined locally: 20154 eval global, eval module or non strict eval */ 20155 BOOL is_func_expr; /* TRUE if function expression */ 20156 BOOL has_home_object; /* TRUE if the home object is available */ 20157 BOOL has_prototype; /* true if a prototype field is necessary */ 20158 BOOL has_simple_parameter_list; 20159 BOOL has_parameter_expressions; /* if true, an argument scope is created */ 20160 BOOL has_use_strict; /* to reject directive in special cases */ 20161 BOOL has_eval_call; /* true if the function contains a call to eval() */ 20162 BOOL has_arguments_binding; /* true if the 'arguments' binding is 20163 available in the function */ 20164 BOOL has_this_binding; /* true if the 'this' and new.target binding are 20165 available in the function */ 20166 BOOL new_target_allowed; /* true if the 'new.target' does not 20167 throw a syntax error */ 20168 BOOL super_call_allowed; /* true if super() is allowed */ 20169 BOOL super_allowed; /* true if super. or super[] is allowed */ 20170 BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ 20171 BOOL is_derived_class_constructor; 20172 BOOL in_function_body; 20173 BOOL backtrace_barrier; 20174 JSFunctionKindEnum func_kind : 8; 20175 JSParseFunctionEnum func_type : 8; 20176 uint8_t js_mode; /* bitmap of JS_MODE_x */ 20177 JSAtom func_name; /* JS_ATOM_NULL if no name */ 20178 20179 JSVarDef *vars; 20180 int var_size; /* allocated size for vars[] */ 20181 int var_count; 20182 JSVarDef *args; 20183 int arg_size; /* allocated size for args[] */ 20184 int arg_count; /* number of arguments */ 20185 int defined_arg_count; 20186 int var_object_idx; /* -1 if none */ 20187 int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ 20188 int arguments_var_idx; /* -1 if none */ 20189 int arguments_arg_idx; /* argument variable definition in argument scope, 20190 -1 if none */ 20191 int func_var_idx; /* variable containing the current function (-1 20192 if none, only used if is_func_expr is true) */ 20193 int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ 20194 int this_var_idx; /* variable containg the 'this' value, -1 if none */ 20195 int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ 20196 int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ 20197 int home_object_var_idx; 20198 BOOL need_home_object; 20199 20200 int scope_level; /* index into fd->scopes if the current lexical scope */ 20201 int scope_first; /* index into vd->vars of first lexically scoped variable */ 20202 int scope_size; /* allocated size of fd->scopes array */ 20203 int scope_count; /* number of entries used in the fd->scopes array */ 20204 JSVarScope *scopes; 20205 JSVarScope def_scope_array[4]; 20206 int body_scope; /* scope of the body of the function or eval */ 20207 20208 int global_var_count; 20209 int global_var_size; 20210 JSGlobalVar *global_vars; 20211 20212 DynBuf byte_code; 20213 int last_opcode_pos; /* -1 if no last opcode */ 20214 int last_opcode_line_num; 20215 BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ 20216 20217 LabelSlot *label_slots; 20218 int label_size; /* allocated size for label_slots[] */ 20219 int label_count; 20220 BlockEnv *top_break; /* break/continue label stack */ 20221 20222 /* constant pool (strings, functions, numbers) */ 20223 JSValue *cpool; 20224 int cpool_count; 20225 int cpool_size; 20226 20227 /* list of variables in the closure */ 20228 int closure_var_count; 20229 int closure_var_size; 20230 JSClosureVar *closure_var; 20231 20232 JumpSlot *jump_slots; 20233 int jump_size; 20234 int jump_count; 20235 20236 LineNumberSlot *line_number_slots; 20237 int line_number_size; 20238 int line_number_count; 20239 int line_number_last; 20240 int line_number_last_pc; 20241 20242 /* pc2line table */ 20243 JSAtom filename; 20244 int line_num; 20245 DynBuf pc2line; 20246 20247 char *source; /* raw source, utf-8 encoded */ 20248 int source_len; 20249 20250 JSModuleDef *module; /* != NULL when parsing a module */ 20251 BOOL has_await; /* TRUE if await is used (used in module eval) */ 20252 } JSFunctionDef; 20253 20254 typedef struct JSToken { 20255 int val; 20256 int line_num; /* line number of token start */ 20257 const uint8_t *ptr; 20258 union { 20259 struct { 20260 JSValue str; 20261 int sep; 20262 } str; 20263 struct { 20264 JSValue val; 20265 #ifdef CONFIG_BIGNUM 20266 slimb_t exponent; /* may be != 0 only if val is a float */ 20267 #endif 20268 } num; 20269 struct { 20270 JSAtom atom; 20271 BOOL has_escape; 20272 BOOL is_reserved; 20273 } ident; 20274 struct { 20275 JSValue body; 20276 JSValue flags; 20277 } regexp; 20278 } u; 20279 } JSToken; 20280 20281 typedef struct JSParseState { 20282 JSContext *ctx; 20283 int last_line_num; /* line number of last token */ 20284 int line_num; /* line number of current offset */ 20285 const char *filename; 20286 JSToken token; 20287 BOOL got_lf; /* true if got line feed before the current token */ 20288 const uint8_t *last_ptr; 20289 const uint8_t *buf_ptr; 20290 const uint8_t *buf_end; 20291 20292 /* current function code */ 20293 JSFunctionDef *cur_func; 20294 BOOL is_module; /* parsing a module */ 20295 BOOL allow_html_comments; 20296 BOOL ext_json; /* true if accepting JSON superset */ 20297 } JSParseState; 20298 20299 typedef struct JSOpCode { 20300 #ifdef DUMP_BYTECODE 20301 const char *name; 20302 #endif 20303 uint8_t size; /* in bytes */ 20304 /* the opcodes remove n_pop items from the top of the stack, then 20305 pushes n_push items */ 20306 uint8_t n_pop; 20307 uint8_t n_push; 20308 uint8_t fmt; 20309 } JSOpCode; 20310 20311 static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { 20312 #define FMT(f) 20313 #ifdef DUMP_BYTECODE 20314 #define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, 20315 #else 20316 #define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, 20317 #endif 20318 #include "quickjs-opcode.h" 20319 #undef DEF 20320 #undef FMT 20321 }; 20322 20323 #if SHORT_OPCODES 20324 /* After the final compilation pass, short opcodes are used. Their 20325 opcodes overlap with the temporary opcodes which cannot appear in 20326 the final bytecode. Their description is after the temporary 20327 opcodes in opcode_info[]. */ 20328 #define short_opcode_info(op) \ 20329 opcode_info[(op) >= OP_TEMP_START ? \ 20330 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] 20331 #else 20332 #define short_opcode_info(op) opcode_info[op] 20333 #endif 20334 20335 static __exception int next_token(JSParseState *s); 20336 20337 static void free_token(JSParseState *s, JSToken *token) 20338 { 20339 switch(token->val) { 20340 case TOK_NUMBER: 20341 JS_FreeValue(s->ctx, token->u.num.val); 20342 break; 20343 case TOK_STRING: 20344 case TOK_TEMPLATE: 20345 JS_FreeValue(s->ctx, token->u.str.str); 20346 break; 20347 case TOK_REGEXP: 20348 JS_FreeValue(s->ctx, token->u.regexp.body); 20349 JS_FreeValue(s->ctx, token->u.regexp.flags); 20350 break; 20351 case TOK_IDENT: 20352 case TOK_PRIVATE_NAME: 20353 JS_FreeAtom(s->ctx, token->u.ident.atom); 20354 break; 20355 default: 20356 if (token->val >= TOK_FIRST_KEYWORD && 20357 token->val <= TOK_LAST_KEYWORD) { 20358 JS_FreeAtom(s->ctx, token->u.ident.atom); 20359 } 20360 break; 20361 } 20362 } 20363 20364 static void __attribute((unused)) dump_token(JSParseState *s, 20365 const JSToken *token) 20366 { 20367 switch(token->val) { 20368 case TOK_NUMBER: 20369 { 20370 double d; 20371 JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ 20372 printf("number: %.14g\n", d); 20373 } 20374 break; 20375 case TOK_IDENT: 20376 dump_atom: 20377 { 20378 char buf[ATOM_GET_STR_BUF_SIZE]; 20379 printf("ident: '%s'\n", 20380 JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); 20381 } 20382 break; 20383 case TOK_STRING: 20384 { 20385 const char *str; 20386 /* XXX: quote the string */ 20387 str = JS_ToCString(s->ctx, token->u.str.str); 20388 printf("string: '%s'\n", str); 20389 JS_FreeCString(s->ctx, str); 20390 } 20391 break; 20392 case TOK_TEMPLATE: 20393 { 20394 const char *str; 20395 str = JS_ToCString(s->ctx, token->u.str.str); 20396 printf("template: `%s`\n", str); 20397 JS_FreeCString(s->ctx, str); 20398 } 20399 break; 20400 case TOK_REGEXP: 20401 { 20402 const char *str, *str2; 20403 str = JS_ToCString(s->ctx, token->u.regexp.body); 20404 str2 = JS_ToCString(s->ctx, token->u.regexp.flags); 20405 printf("regexp: '%s' '%s'\n", str, str2); 20406 JS_FreeCString(s->ctx, str); 20407 JS_FreeCString(s->ctx, str2); 20408 } 20409 break; 20410 case TOK_EOF: 20411 printf("eof\n"); 20412 break; 20413 default: 20414 if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { 20415 goto dump_atom; 20416 } else if (s->token.val >= 256) { 20417 printf("token: %d\n", token->val); 20418 } else { 20419 printf("token: '%c'\n", token->val); 20420 } 20421 break; 20422 } 20423 } 20424 20425 int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) 20426 { 20427 JSContext *ctx = s->ctx; 20428 va_list ap; 20429 int backtrace_flags; 20430 20431 va_start(ap, fmt); 20432 JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); 20433 va_end(ap); 20434 backtrace_flags = 0; 20435 if (s->cur_func && s->cur_func->backtrace_barrier) 20436 backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; 20437 build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num, 20438 backtrace_flags); 20439 return -1; 20440 } 20441 20442 static int js_parse_expect(JSParseState *s, int tok) 20443 { 20444 if (s->token.val != tok) { 20445 /* XXX: dump token correctly in all cases */ 20446 return js_parse_error(s, "expecting '%c'", tok); 20447 } 20448 return next_token(s); 20449 } 20450 20451 static int js_parse_expect_semi(JSParseState *s) 20452 { 20453 if (s->token.val != ';') { 20454 /* automatic insertion of ';' */ 20455 if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { 20456 return 0; 20457 } 20458 return js_parse_error(s, "expecting '%c'", ';'); 20459 } 20460 return next_token(s); 20461 } 20462 20463 static int js_parse_error_reserved_identifier(JSParseState *s) 20464 { 20465 char buf1[ATOM_GET_STR_BUF_SIZE]; 20466 return js_parse_error(s, "'%s' is a reserved identifier", 20467 JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), 20468 s->token.u.ident.atom)); 20469 } 20470 20471 static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) 20472 { 20473 uint32_t c; 20474 StringBuffer b_s, *b = &b_s; 20475 20476 /* p points to the first byte of the template part */ 20477 if (string_buffer_init(s->ctx, b, 32)) 20478 goto fail; 20479 for(;;) { 20480 if (p >= s->buf_end) 20481 goto unexpected_eof; 20482 c = *p++; 20483 if (c == '`') { 20484 /* template end part */ 20485 break; 20486 } 20487 if (c == '$' && *p == '{') { 20488 /* template start or middle part */ 20489 p++; 20490 break; 20491 } 20492 if (c == '\\') { 20493 if (string_buffer_putc8(b, c)) 20494 goto fail; 20495 if (p >= s->buf_end) 20496 goto unexpected_eof; 20497 c = *p++; 20498 } 20499 /* newline sequences are normalized as single '\n' bytes */ 20500 if (c == '\r') { 20501 if (*p == '\n') 20502 p++; 20503 c = '\n'; 20504 } 20505 if (c == '\n') { 20506 s->line_num++; 20507 } else if (c >= 0x80) { 20508 const uint8_t *p_next; 20509 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 20510 if (c > 0x10FFFF) { 20511 js_parse_error(s, "invalid UTF-8 sequence"); 20512 goto fail; 20513 } 20514 p = p_next; 20515 } 20516 if (string_buffer_putc(b, c)) 20517 goto fail; 20518 } 20519 s->token.val = TOK_TEMPLATE; 20520 s->token.u.str.sep = c; 20521 s->token.u.str.str = string_buffer_end(b); 20522 s->buf_ptr = p; 20523 return 0; 20524 20525 unexpected_eof: 20526 js_parse_error(s, "unexpected end of string"); 20527 fail: 20528 string_buffer_free(b); 20529 return -1; 20530 } 20531 20532 static __exception int js_parse_string(JSParseState *s, int sep, 20533 BOOL do_throw, const uint8_t *p, 20534 JSToken *token, const uint8_t **pp) 20535 { 20536 int ret; 20537 uint32_t c; 20538 StringBuffer b_s, *b = &b_s; 20539 20540 /* string */ 20541 if (string_buffer_init(s->ctx, b, 32)) 20542 goto fail; 20543 for(;;) { 20544 if (p >= s->buf_end) 20545 goto invalid_char; 20546 c = *p; 20547 if (c < 0x20) { 20548 if (!s->cur_func) { 20549 if (do_throw) 20550 js_parse_error(s, "invalid character in a JSON string"); 20551 goto fail; 20552 } 20553 if (sep == '`') { 20554 if (c == '\r') { 20555 if (p[1] == '\n') 20556 p++; 20557 c = '\n'; 20558 } 20559 /* do not update s->line_num */ 20560 } else if (c == '\n' || c == '\r') 20561 goto invalid_char; 20562 } 20563 p++; 20564 if (c == sep) 20565 break; 20566 if (c == '$' && *p == '{' && sep == '`') { 20567 /* template start or middle part */ 20568 p++; 20569 break; 20570 } 20571 if (c == '\\') { 20572 c = *p; 20573 /* XXX: need a specific JSON case to avoid 20574 accepting invalid escapes */ 20575 switch(c) { 20576 case '\0': 20577 if (p >= s->buf_end) 20578 goto invalid_char; 20579 p++; 20580 break; 20581 case '\'': 20582 case '\"': 20583 case '\\': 20584 p++; 20585 break; 20586 case '\r': /* accept DOS and MAC newline sequences */ 20587 if (p[1] == '\n') { 20588 p++; 20589 } 20590 /* fall thru */ 20591 case '\n': 20592 /* ignore escaped newline sequence */ 20593 p++; 20594 if (sep != '`') 20595 s->line_num++; 20596 continue; 20597 default: 20598 if (c >= '0' && c <= '9') { 20599 if (!s->cur_func) 20600 goto invalid_escape; /* JSON case */ 20601 if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`') 20602 goto parse_escape; 20603 if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { 20604 p++; 20605 c = '\0'; 20606 } else { 20607 if (c >= '8' || sep == '`') { 20608 /* Note: according to ES2021, \8 and \9 are not 20609 accepted in strict mode or in templates. */ 20610 goto invalid_escape; 20611 } else { 20612 if (do_throw) 20613 js_parse_error(s, "octal escape sequences are not allowed in strict mode"); 20614 } 20615 goto fail; 20616 } 20617 } else if (c >= 0x80) { 20618 const uint8_t *p_next; 20619 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 20620 if (c > 0x10FFFF) { 20621 goto invalid_utf8; 20622 } 20623 p = p_next; 20624 /* LS or PS are skipped */ 20625 if (c == CP_LS || c == CP_PS) 20626 continue; 20627 } else { 20628 parse_escape: 20629 ret = lre_parse_escape(&p, TRUE); 20630 if (ret == -1) { 20631 invalid_escape: 20632 if (do_throw) 20633 js_parse_error(s, "malformed escape sequence in string literal"); 20634 goto fail; 20635 } else if (ret < 0) { 20636 /* ignore the '\' (could output a warning) */ 20637 p++; 20638 } else { 20639 c = ret; 20640 } 20641 } 20642 break; 20643 } 20644 } else if (c >= 0x80) { 20645 const uint8_t *p_next; 20646 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 20647 if (c > 0x10FFFF) 20648 goto invalid_utf8; 20649 p = p_next; 20650 } 20651 if (string_buffer_putc(b, c)) 20652 goto fail; 20653 } 20654 token->val = TOK_STRING; 20655 token->u.str.sep = c; 20656 token->u.str.str = string_buffer_end(b); 20657 *pp = p; 20658 return 0; 20659 20660 invalid_utf8: 20661 if (do_throw) 20662 js_parse_error(s, "invalid UTF-8 sequence"); 20663 goto fail; 20664 invalid_char: 20665 if (do_throw) 20666 js_parse_error(s, "unexpected end of string"); 20667 fail: 20668 string_buffer_free(b); 20669 return -1; 20670 } 20671 20672 static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { 20673 return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && 20674 !s->token.u.ident.has_escape; 20675 } 20676 20677 static __exception int js_parse_regexp(JSParseState *s) 20678 { 20679 const uint8_t *p; 20680 BOOL in_class; 20681 StringBuffer b_s, *b = &b_s; 20682 StringBuffer b2_s, *b2 = &b2_s; 20683 uint32_t c; 20684 20685 p = s->buf_ptr; 20686 p++; 20687 in_class = FALSE; 20688 if (string_buffer_init(s->ctx, b, 32)) 20689 return -1; 20690 if (string_buffer_init(s->ctx, b2, 1)) 20691 goto fail; 20692 for(;;) { 20693 if (p >= s->buf_end) { 20694 eof_error: 20695 js_parse_error(s, "unexpected end of regexp"); 20696 goto fail; 20697 } 20698 c = *p++; 20699 if (c == '\n' || c == '\r') { 20700 goto eol_error; 20701 } else if (c == '/') { 20702 if (!in_class) 20703 break; 20704 } else if (c == '[') { 20705 in_class = TRUE; 20706 } else if (c == ']') { 20707 /* XXX: incorrect as the first character in a class */ 20708 in_class = FALSE; 20709 } else if (c == '\\') { 20710 if (string_buffer_putc8(b, c)) 20711 goto fail; 20712 c = *p++; 20713 if (c == '\n' || c == '\r') 20714 goto eol_error; 20715 else if (c == '\0' && p >= s->buf_end) 20716 goto eof_error; 20717 else if (c >= 0x80) { 20718 const uint8_t *p_next; 20719 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 20720 if (c > 0x10FFFF) { 20721 goto invalid_utf8; 20722 } 20723 p = p_next; 20724 if (c == CP_LS || c == CP_PS) 20725 goto eol_error; 20726 } 20727 } else if (c >= 0x80) { 20728 const uint8_t *p_next; 20729 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next); 20730 if (c > 0x10FFFF) { 20731 invalid_utf8: 20732 js_parse_error(s, "invalid UTF-8 sequence"); 20733 goto fail; 20734 } 20735 p = p_next; 20736 /* LS or PS are considered as line terminator */ 20737 if (c == CP_LS || c == CP_PS) { 20738 eol_error: 20739 js_parse_error(s, "unexpected line terminator in regexp"); 20740 goto fail; 20741 } 20742 } 20743 if (string_buffer_putc(b, c)) 20744 goto fail; 20745 } 20746 20747 /* flags */ 20748 for(;;) { 20749 const uint8_t *p_next = p; 20750 c = *p_next++; 20751 if (c >= 0x80) { 20752 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next); 20753 if (c > 0x10FFFF) { 20754 goto invalid_utf8; 20755 } 20756 } 20757 if (!lre_js_is_ident_next(c)) 20758 break; 20759 if (string_buffer_putc(b2, c)) 20760 goto fail; 20761 p = p_next; 20762 } 20763 20764 s->token.val = TOK_REGEXP; 20765 s->token.u.regexp.body = string_buffer_end(b); 20766 s->token.u.regexp.flags = string_buffer_end(b2); 20767 s->buf_ptr = p; 20768 return 0; 20769 fail: 20770 string_buffer_free(b); 20771 string_buffer_free(b2); 20772 return -1; 20773 } 20774 20775 static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, 20776 char *static_buf) 20777 { 20778 char *buf, *new_buf; 20779 size_t size, new_size; 20780 20781 buf = *pbuf; 20782 size = *psize; 20783 if (size >= (SIZE_MAX / 3) * 2) 20784 new_size = SIZE_MAX; 20785 else 20786 new_size = size + (size >> 1); 20787 if (buf == static_buf) { 20788 new_buf = js_malloc(ctx, new_size); 20789 if (!new_buf) 20790 return -1; 20791 memcpy(new_buf, buf, size); 20792 } else { 20793 new_buf = js_realloc(ctx, buf, new_size); 20794 if (!new_buf) 20795 return -1; 20796 } 20797 *pbuf = new_buf; 20798 *psize = new_size; 20799 return 0; 20800 } 20801 20802 /* convert a TOK_IDENT to a keyword when needed */ 20803 static void update_token_ident(JSParseState *s) 20804 { 20805 if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || 20806 (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && 20807 (s->cur_func->js_mode & JS_MODE_STRICT)) || 20808 (s->token.u.ident.atom == JS_ATOM_yield && 20809 ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || 20810 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && 20811 !s->cur_func->in_function_body && s->cur_func->parent && 20812 (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || 20813 (s->token.u.ident.atom == JS_ATOM_await && 20814 (s->is_module || 20815 (s->cur_func->func_kind & JS_FUNC_ASYNC) || 20816 s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT || 20817 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && 20818 !s->cur_func->in_function_body && s->cur_func->parent && 20819 ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) || 20820 s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { 20821 if (s->token.u.ident.has_escape) { 20822 s->token.u.ident.is_reserved = TRUE; 20823 s->token.val = TOK_IDENT; 20824 } else { 20825 /* The keywords atoms are pre allocated */ 20826 s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; 20827 } 20828 } 20829 } 20830 20831 /* if the current token is an identifier or keyword, reparse it 20832 according to the current function type */ 20833 static void reparse_ident_token(JSParseState *s) 20834 { 20835 if (s->token.val == TOK_IDENT || 20836 (s->token.val >= TOK_FIRST_KEYWORD && 20837 s->token.val <= TOK_LAST_KEYWORD)) { 20838 s->token.val = TOK_IDENT; 20839 s->token.u.ident.is_reserved = FALSE; 20840 update_token_ident(s); 20841 } 20842 } 20843 20844 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ 20845 static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, 20846 BOOL *pident_has_escape, int c, BOOL is_private) 20847 { 20848 const uint8_t *p, *p1; 20849 char ident_buf[128], *buf; 20850 size_t ident_size, ident_pos; 20851 JSAtom atom; 20852 20853 p = *pp; 20854 buf = ident_buf; 20855 ident_size = sizeof(ident_buf); 20856 ident_pos = 0; 20857 if (is_private) 20858 buf[ident_pos++] = '#'; 20859 for(;;) { 20860 p1 = p; 20861 20862 if (c < 128) { 20863 buf[ident_pos++] = c; 20864 } else { 20865 ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c); 20866 } 20867 c = *p1++; 20868 if (c == '\\' && *p1 == 'u') { 20869 c = lre_parse_escape(&p1, TRUE); 20870 *pident_has_escape = TRUE; 20871 } else if (c >= 128) { 20872 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); 20873 } 20874 if (!lre_js_is_ident_next(c)) 20875 break; 20876 p = p1; 20877 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { 20878 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { 20879 atom = JS_ATOM_NULL; 20880 goto done; 20881 } 20882 } 20883 } 20884 atom = JS_NewAtomLen(s->ctx, buf, ident_pos); 20885 done: 20886 if (unlikely(buf != ident_buf)) 20887 js_free(s->ctx, buf); 20888 *pp = p; 20889 return atom; 20890 } 20891 20892 20893 static __exception int next_token(JSParseState *s) 20894 { 20895 const uint8_t *p; 20896 int c; 20897 BOOL ident_has_escape; 20898 JSAtom atom; 20899 20900 if (js_check_stack_overflow(s->ctx->rt, 0)) { 20901 return js_parse_error(s, "stack overflow"); 20902 } 20903 20904 free_token(s, &s->token); 20905 20906 p = s->last_ptr = s->buf_ptr; 20907 s->got_lf = FALSE; 20908 s->last_line_num = s->token.line_num; 20909 redo: 20910 s->token.line_num = s->line_num; 20911 s->token.ptr = p; 20912 c = *p; 20913 switch(c) { 20914 case 0: 20915 if (p >= s->buf_end) { 20916 s->token.val = TOK_EOF; 20917 } else { 20918 goto def_token; 20919 } 20920 break; 20921 case '`': 20922 if (js_parse_template_part(s, p + 1)) 20923 goto fail; 20924 p = s->buf_ptr; 20925 break; 20926 case '\'': 20927 case '\"': 20928 if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) 20929 goto fail; 20930 break; 20931 case '\r': /* accept DOS and MAC newline sequences */ 20932 if (p[1] == '\n') { 20933 p++; 20934 } 20935 /* fall thru */ 20936 case '\n': 20937 p++; 20938 line_terminator: 20939 s->got_lf = TRUE; 20940 s->line_num++; 20941 goto redo; 20942 case '\f': 20943 case '\v': 20944 case ' ': 20945 case '\t': 20946 p++; 20947 goto redo; 20948 case '/': 20949 if (p[1] == '*') { 20950 /* comment */ 20951 p += 2; 20952 for(;;) { 20953 if (*p == '\0' && p >= s->buf_end) { 20954 js_parse_error(s, "unexpected end of comment"); 20955 goto fail; 20956 } 20957 if (p[0] == '*' && p[1] == '/') { 20958 p += 2; 20959 break; 20960 } 20961 if (*p == '\n') { 20962 s->line_num++; 20963 s->got_lf = TRUE; /* considered as LF for ASI */ 20964 p++; 20965 } else if (*p == '\r') { 20966 s->got_lf = TRUE; /* considered as LF for ASI */ 20967 p++; 20968 } else if (*p >= 0x80) { 20969 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 20970 if (c == CP_LS || c == CP_PS) { 20971 s->got_lf = TRUE; /* considered as LF for ASI */ 20972 } else if (c == -1) { 20973 p++; /* skip invalid UTF-8 */ 20974 } 20975 } else { 20976 p++; 20977 } 20978 } 20979 goto redo; 20980 } else if (p[1] == '/') { 20981 /* line comment */ 20982 p += 2; 20983 skip_line_comment: 20984 for(;;) { 20985 if (*p == '\0' && p >= s->buf_end) 20986 break; 20987 if (*p == '\r' || *p == '\n') 20988 break; 20989 if (*p >= 0x80) { 20990 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 20991 /* LS or PS are considered as line terminator */ 20992 if (c == CP_LS || c == CP_PS) { 20993 break; 20994 } else if (c == -1) { 20995 p++; /* skip invalid UTF-8 */ 20996 } 20997 } else { 20998 p++; 20999 } 21000 } 21001 goto redo; 21002 } else if (p[1] == '=') { 21003 p += 2; 21004 s->token.val = TOK_DIV_ASSIGN; 21005 } else { 21006 p++; 21007 s->token.val = c; 21008 } 21009 break; 21010 case '\\': 21011 if (p[1] == 'u') { 21012 const uint8_t *p1 = p + 1; 21013 int c1 = lre_parse_escape(&p1, TRUE); 21014 if (c1 >= 0 && lre_js_is_ident_first(c1)) { 21015 c = c1; 21016 p = p1; 21017 ident_has_escape = TRUE; 21018 goto has_ident; 21019 } else { 21020 /* XXX: syntax error? */ 21021 } 21022 } 21023 goto def_token; 21024 case 'a': case 'b': case 'c': case 'd': 21025 case 'e': case 'f': case 'g': case 'h': 21026 case 'i': case 'j': case 'k': case 'l': 21027 case 'm': case 'n': case 'o': case 'p': 21028 case 'q': case 'r': case 's': case 't': 21029 case 'u': case 'v': case 'w': case 'x': 21030 case 'y': case 'z': 21031 case 'A': case 'B': case 'C': case 'D': 21032 case 'E': case 'F': case 'G': case 'H': 21033 case 'I': case 'J': case 'K': case 'L': 21034 case 'M': case 'N': case 'O': case 'P': 21035 case 'Q': case 'R': case 'S': case 'T': 21036 case 'U': case 'V': case 'W': case 'X': 21037 case 'Y': case 'Z': 21038 case '_': 21039 case '$': 21040 /* identifier */ 21041 p++; 21042 ident_has_escape = FALSE; 21043 has_ident: 21044 atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); 21045 if (atom == JS_ATOM_NULL) 21046 goto fail; 21047 s->token.u.ident.atom = atom; 21048 s->token.u.ident.has_escape = ident_has_escape; 21049 s->token.u.ident.is_reserved = FALSE; 21050 s->token.val = TOK_IDENT; 21051 update_token_ident(s); 21052 break; 21053 case '#': 21054 /* private name */ 21055 { 21056 const uint8_t *p1; 21057 p++; 21058 p1 = p; 21059 c = *p1++; 21060 if (c == '\\' && *p1 == 'u') { 21061 c = lre_parse_escape(&p1, TRUE); 21062 } else if (c >= 128) { 21063 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); 21064 } 21065 if (!lre_js_is_ident_first(c)) { 21066 js_parse_error(s, "invalid first character of private name"); 21067 goto fail; 21068 } 21069 p = p1; 21070 ident_has_escape = FALSE; /* not used */ 21071 atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); 21072 if (atom == JS_ATOM_NULL) 21073 goto fail; 21074 s->token.u.ident.atom = atom; 21075 s->token.val = TOK_PRIVATE_NAME; 21076 } 21077 break; 21078 case '.': 21079 if (p[1] == '.' && p[2] == '.') { 21080 p += 3; 21081 s->token.val = TOK_ELLIPSIS; 21082 break; 21083 } 21084 if (p[1] >= '0' && p[1] <= '9') { 21085 goto parse_number; 21086 } else { 21087 goto def_token; 21088 } 21089 break; 21090 case '0': 21091 /* in strict mode, octal literals are not accepted */ 21092 if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) { 21093 js_parse_error(s, "octal literals are deprecated in strict mode"); 21094 goto fail; 21095 } 21096 goto parse_number; 21097 case '1': case '2': case '3': case '4': 21098 case '5': case '6': case '7': case '8': 21099 case '9': 21100 /* number */ 21101 parse_number: 21102 { 21103 JSValue ret; 21104 const uint8_t *p1; 21105 int flags, radix; 21106 flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | 21107 ATOD_ACCEPT_UNDERSCORES; 21108 flags |= ATOD_ACCEPT_SUFFIX; 21109 #ifdef CONFIG_BIGNUM 21110 if (s->cur_func->js_mode & JS_MODE_MATH) { 21111 flags |= ATOD_MODE_BIGINT; 21112 if (s->cur_func->js_mode & JS_MODE_MATH) 21113 flags |= ATOD_TYPE_BIG_FLOAT; 21114 } 21115 #endif 21116 radix = 0; 21117 #ifdef CONFIG_BIGNUM 21118 s->token.u.num.exponent = 0; 21119 ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix, 21120 flags, &s->token.u.num.exponent); 21121 #else 21122 ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, 21123 flags); 21124 #endif 21125 if (JS_IsException(ret)) 21126 goto fail; 21127 /* reject `10instanceof Number` */ 21128 if (JS_VALUE_IS_NAN(ret) || 21129 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) { 21130 JS_FreeValue(s->ctx, ret); 21131 js_parse_error(s, "invalid number literal"); 21132 goto fail; 21133 } 21134 s->token.val = TOK_NUMBER; 21135 s->token.u.num.val = ret; 21136 } 21137 break; 21138 case '*': 21139 if (p[1] == '=') { 21140 p += 2; 21141 s->token.val = TOK_MUL_ASSIGN; 21142 } else if (p[1] == '*') { 21143 if (p[2] == '=') { 21144 p += 3; 21145 s->token.val = TOK_POW_ASSIGN; 21146 } else { 21147 p += 2; 21148 s->token.val = TOK_POW; 21149 } 21150 } else { 21151 goto def_token; 21152 } 21153 break; 21154 case '%': 21155 if (p[1] == '=') { 21156 p += 2; 21157 s->token.val = TOK_MOD_ASSIGN; 21158 } else { 21159 goto def_token; 21160 } 21161 break; 21162 case '+': 21163 if (p[1] == '=') { 21164 p += 2; 21165 s->token.val = TOK_PLUS_ASSIGN; 21166 } else if (p[1] == '+') { 21167 p += 2; 21168 s->token.val = TOK_INC; 21169 } else { 21170 goto def_token; 21171 } 21172 break; 21173 case '-': 21174 if (p[1] == '=') { 21175 p += 2; 21176 s->token.val = TOK_MINUS_ASSIGN; 21177 } else if (p[1] == '-') { 21178 if (s->allow_html_comments && 21179 p[2] == '>' && s->last_line_num != s->line_num) { 21180 /* Annex B: `-->` at beginning of line is an html comment end. 21181 It extends to the end of the line. 21182 */ 21183 goto skip_line_comment; 21184 } 21185 p += 2; 21186 s->token.val = TOK_DEC; 21187 } else { 21188 goto def_token; 21189 } 21190 break; 21191 case '<': 21192 if (p[1] == '=') { 21193 p += 2; 21194 s->token.val = TOK_LTE; 21195 } else if (p[1] == '<') { 21196 if (p[2] == '=') { 21197 p += 3; 21198 s->token.val = TOK_SHL_ASSIGN; 21199 } else { 21200 p += 2; 21201 s->token.val = TOK_SHL; 21202 } 21203 } else if (s->allow_html_comments && 21204 p[1] == '!' && p[2] == '-' && p[3] == '-') { 21205 /* Annex B: handle `<!--` single line html comments */ 21206 goto skip_line_comment; 21207 } else { 21208 goto def_token; 21209 } 21210 break; 21211 case '>': 21212 if (p[1] == '=') { 21213 p += 2; 21214 s->token.val = TOK_GTE; 21215 } else if (p[1] == '>') { 21216 if (p[2] == '>') { 21217 if (p[3] == '=') { 21218 p += 4; 21219 s->token.val = TOK_SHR_ASSIGN; 21220 } else { 21221 p += 3; 21222 s->token.val = TOK_SHR; 21223 } 21224 } else if (p[2] == '=') { 21225 p += 3; 21226 s->token.val = TOK_SAR_ASSIGN; 21227 } else { 21228 p += 2; 21229 s->token.val = TOK_SAR; 21230 } 21231 } else { 21232 goto def_token; 21233 } 21234 break; 21235 case '=': 21236 if (p[1] == '=') { 21237 if (p[2] == '=') { 21238 p += 3; 21239 s->token.val = TOK_STRICT_EQ; 21240 } else { 21241 p += 2; 21242 s->token.val = TOK_EQ; 21243 } 21244 } else if (p[1] == '>') { 21245 p += 2; 21246 s->token.val = TOK_ARROW; 21247 } else { 21248 goto def_token; 21249 } 21250 break; 21251 case '!': 21252 if (p[1] == '=') { 21253 if (p[2] == '=') { 21254 p += 3; 21255 s->token.val = TOK_STRICT_NEQ; 21256 } else { 21257 p += 2; 21258 s->token.val = TOK_NEQ; 21259 } 21260 } else { 21261 goto def_token; 21262 } 21263 break; 21264 case '&': 21265 if (p[1] == '=') { 21266 p += 2; 21267 s->token.val = TOK_AND_ASSIGN; 21268 } else if (p[1] == '&') { 21269 if (p[2] == '=') { 21270 p += 3; 21271 s->token.val = TOK_LAND_ASSIGN; 21272 } else { 21273 p += 2; 21274 s->token.val = TOK_LAND; 21275 } 21276 } else { 21277 goto def_token; 21278 } 21279 break; 21280 #ifdef CONFIG_BIGNUM 21281 /* in math mode, '^' is the power operator. '^^' is always the 21282 xor operator and '**' is always the power operator */ 21283 case '^': 21284 if (p[1] == '=') { 21285 p += 2; 21286 if (s->cur_func->js_mode & JS_MODE_MATH) 21287 s->token.val = TOK_MATH_POW_ASSIGN; 21288 else 21289 s->token.val = TOK_XOR_ASSIGN; 21290 } else if (p[1] == '^') { 21291 if (p[2] == '=') { 21292 p += 3; 21293 s->token.val = TOK_XOR_ASSIGN; 21294 } else { 21295 p += 2; 21296 s->token.val = '^'; 21297 } 21298 } else { 21299 p++; 21300 if (s->cur_func->js_mode & JS_MODE_MATH) 21301 s->token.val = TOK_MATH_POW; 21302 else 21303 s->token.val = '^'; 21304 } 21305 break; 21306 #else 21307 case '^': 21308 if (p[1] == '=') { 21309 p += 2; 21310 s->token.val = TOK_XOR_ASSIGN; 21311 } else { 21312 goto def_token; 21313 } 21314 break; 21315 #endif 21316 case '|': 21317 if (p[1] == '=') { 21318 p += 2; 21319 s->token.val = TOK_OR_ASSIGN; 21320 } else if (p[1] == '|') { 21321 if (p[2] == '=') { 21322 p += 3; 21323 s->token.val = TOK_LOR_ASSIGN; 21324 } else { 21325 p += 2; 21326 s->token.val = TOK_LOR; 21327 } 21328 } else { 21329 goto def_token; 21330 } 21331 break; 21332 case '?': 21333 if (p[1] == '?') { 21334 if (p[2] == '=') { 21335 p += 3; 21336 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN; 21337 } else { 21338 p += 2; 21339 s->token.val = TOK_DOUBLE_QUESTION_MARK; 21340 } 21341 } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) { 21342 p += 2; 21343 s->token.val = TOK_QUESTION_MARK_DOT; 21344 } else { 21345 goto def_token; 21346 } 21347 break; 21348 default: 21349 if (c >= 128) { 21350 /* unicode value */ 21351 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 21352 switch(c) { 21353 case CP_PS: 21354 case CP_LS: 21355 /* XXX: should avoid incrementing line_number, but 21356 needed to handle HTML comments */ 21357 goto line_terminator; 21358 default: 21359 if (lre_is_space(c)) { 21360 goto redo; 21361 } else if (lre_js_is_ident_first(c)) { 21362 ident_has_escape = FALSE; 21363 goto has_ident; 21364 } else { 21365 js_parse_error(s, "unexpected character"); 21366 goto fail; 21367 } 21368 } 21369 } 21370 def_token: 21371 s->token.val = c; 21372 p++; 21373 break; 21374 } 21375 s->buf_ptr = p; 21376 21377 // dump_token(s, &s->token); 21378 return 0; 21379 21380 fail: 21381 s->token.val = TOK_ERROR; 21382 return -1; 21383 } 21384 21385 /* 'c' is the first character. Return JS_ATOM_NULL in case of error */ 21386 static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) 21387 { 21388 const uint8_t *p; 21389 char ident_buf[128], *buf; 21390 size_t ident_size, ident_pos; 21391 JSAtom atom; 21392 21393 p = *pp; 21394 buf = ident_buf; 21395 ident_size = sizeof(ident_buf); 21396 ident_pos = 0; 21397 for(;;) { 21398 buf[ident_pos++] = c; 21399 c = *p; 21400 if (c >= 128 || !lre_is_id_continue_byte(c)) 21401 break; 21402 p++; 21403 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { 21404 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) { 21405 atom = JS_ATOM_NULL; 21406 goto done; 21407 } 21408 } 21409 } 21410 atom = JS_NewAtomLen(s->ctx, buf, ident_pos); 21411 done: 21412 if (unlikely(buf != ident_buf)) 21413 js_free(s->ctx, buf); 21414 *pp = p; 21415 return atom; 21416 } 21417 21418 static __exception int json_next_token(JSParseState *s) 21419 { 21420 const uint8_t *p; 21421 int c; 21422 JSAtom atom; 21423 21424 if (js_check_stack_overflow(s->ctx->rt, 0)) { 21425 return js_parse_error(s, "stack overflow"); 21426 } 21427 21428 free_token(s, &s->token); 21429 21430 p = s->last_ptr = s->buf_ptr; 21431 s->last_line_num = s->token.line_num; 21432 redo: 21433 s->token.line_num = s->line_num; 21434 s->token.ptr = p; 21435 c = *p; 21436 switch(c) { 21437 case 0: 21438 if (p >= s->buf_end) { 21439 s->token.val = TOK_EOF; 21440 } else { 21441 goto def_token; 21442 } 21443 break; 21444 case '\'': 21445 if (!s->ext_json) { 21446 /* JSON does not accept single quoted strings */ 21447 goto def_token; 21448 } 21449 /* fall through */ 21450 case '\"': 21451 if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) 21452 goto fail; 21453 break; 21454 case '\r': /* accept DOS and MAC newline sequences */ 21455 if (p[1] == '\n') { 21456 p++; 21457 } 21458 /* fall thru */ 21459 case '\n': 21460 p++; 21461 s->line_num++; 21462 goto redo; 21463 case '\f': 21464 case '\v': 21465 if (!s->ext_json) { 21466 /* JSONWhitespace does not match <VT>, nor <FF> */ 21467 goto def_token; 21468 } 21469 /* fall through */ 21470 case ' ': 21471 case '\t': 21472 p++; 21473 goto redo; 21474 case '/': 21475 if (!s->ext_json) { 21476 /* JSON does not accept comments */ 21477 goto def_token; 21478 } 21479 if (p[1] == '*') { 21480 /* comment */ 21481 p += 2; 21482 for(;;) { 21483 if (*p == '\0' && p >= s->buf_end) { 21484 js_parse_error(s, "unexpected end of comment"); 21485 goto fail; 21486 } 21487 if (p[0] == '*' && p[1] == '/') { 21488 p += 2; 21489 break; 21490 } 21491 if (*p == '\n') { 21492 s->line_num++; 21493 p++; 21494 } else if (*p == '\r') { 21495 p++; 21496 } else if (*p >= 0x80) { 21497 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 21498 if (c == -1) { 21499 p++; /* skip invalid UTF-8 */ 21500 } 21501 } else { 21502 p++; 21503 } 21504 } 21505 goto redo; 21506 } else if (p[1] == '/') { 21507 /* line comment */ 21508 p += 2; 21509 for(;;) { 21510 if (*p == '\0' && p >= s->buf_end) 21511 break; 21512 if (*p == '\r' || *p == '\n') 21513 break; 21514 if (*p >= 0x80) { 21515 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 21516 /* LS or PS are considered as line terminator */ 21517 if (c == CP_LS || c == CP_PS) { 21518 break; 21519 } else if (c == -1) { 21520 p++; /* skip invalid UTF-8 */ 21521 } 21522 } else { 21523 p++; 21524 } 21525 } 21526 goto redo; 21527 } else { 21528 goto def_token; 21529 } 21530 break; 21531 case 'a': case 'b': case 'c': case 'd': 21532 case 'e': case 'f': case 'g': case 'h': 21533 case 'i': case 'j': case 'k': case 'l': 21534 case 'm': case 'n': case 'o': case 'p': 21535 case 'q': case 'r': case 's': case 't': 21536 case 'u': case 'v': case 'w': case 'x': 21537 case 'y': case 'z': 21538 case 'A': case 'B': case 'C': case 'D': 21539 case 'E': case 'F': case 'G': case 'H': 21540 case 'I': case 'J': case 'K': case 'L': 21541 case 'M': case 'N': case 'O': case 'P': 21542 case 'Q': case 'R': case 'S': case 'T': 21543 case 'U': case 'V': case 'W': case 'X': 21544 case 'Y': case 'Z': 21545 case '_': 21546 case '$': 21547 /* identifier : only pure ascii characters are accepted */ 21548 p++; 21549 atom = json_parse_ident(s, &p, c); 21550 if (atom == JS_ATOM_NULL) 21551 goto fail; 21552 s->token.u.ident.atom = atom; 21553 s->token.u.ident.has_escape = FALSE; 21554 s->token.u.ident.is_reserved = FALSE; 21555 s->token.val = TOK_IDENT; 21556 break; 21557 case '+': 21558 if (!s->ext_json || !is_digit(p[1])) 21559 goto def_token; 21560 goto parse_number; 21561 case '0': 21562 if (is_digit(p[1])) 21563 goto def_token; 21564 goto parse_number; 21565 case '-': 21566 if (!is_digit(p[1])) 21567 goto def_token; 21568 goto parse_number; 21569 case '1': case '2': case '3': case '4': 21570 case '5': case '6': case '7': case '8': 21571 case '9': 21572 /* number */ 21573 parse_number: 21574 { 21575 JSValue ret; 21576 int flags, radix; 21577 if (!s->ext_json) { 21578 flags = 0; 21579 radix = 10; 21580 } else { 21581 flags = ATOD_ACCEPT_BIN_OCT; 21582 radix = 0; 21583 } 21584 ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix, 21585 flags); 21586 if (JS_IsException(ret)) 21587 goto fail; 21588 s->token.val = TOK_NUMBER; 21589 s->token.u.num.val = ret; 21590 } 21591 break; 21592 default: 21593 if (c >= 128) { 21594 js_parse_error(s, "unexpected character"); 21595 goto fail; 21596 } 21597 def_token: 21598 s->token.val = c; 21599 p++; 21600 break; 21601 } 21602 s->buf_ptr = p; 21603 21604 // dump_token(s, &s->token); 21605 return 0; 21606 21607 fail: 21608 s->token.val = TOK_ERROR; 21609 return -1; 21610 } 21611 21612 static int match_identifier(const uint8_t *p, const char *s) { 21613 uint32_t c; 21614 while (*s) { 21615 if ((uint8_t)*s++ != *p++) 21616 return 0; 21617 } 21618 c = *p; 21619 if (c >= 128) 21620 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 21621 return !lre_js_is_ident_next(c); 21622 } 21623 21624 /* simple_next_token() is used to check for the next token in simple cases. 21625 It is only used for ':' and '=>', 'let' or 'function' look-ahead. 21626 (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule() 21627 Whitespace and comments are skipped correctly. 21628 Then the next token is analyzed, only for specific words. 21629 Return values: 21630 - '\n' if !no_line_terminator 21631 - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION 21632 - TOK_IDENT is returned for other identifiers and keywords 21633 - otherwise the next character or unicode codepoint is returned. 21634 */ 21635 static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) 21636 { 21637 const uint8_t *p; 21638 uint32_t c; 21639 21640 /* skip spaces and comments */ 21641 p = *pp; 21642 for (;;) { 21643 switch(c = *p++) { 21644 case '\r': 21645 case '\n': 21646 if (no_line_terminator) 21647 return '\n'; 21648 continue; 21649 case ' ': 21650 case '\t': 21651 case '\v': 21652 case '\f': 21653 continue; 21654 case '/': 21655 if (*p == '/') { 21656 if (no_line_terminator) 21657 return '\n'; 21658 while (*p && *p != '\r' && *p != '\n') 21659 p++; 21660 continue; 21661 } 21662 if (*p == '*') { 21663 while (*++p) { 21664 if ((*p == '\r' || *p == '\n') && no_line_terminator) 21665 return '\n'; 21666 if (*p == '*' && p[1] == '/') { 21667 p += 2; 21668 break; 21669 } 21670 } 21671 continue; 21672 } 21673 break; 21674 case '=': 21675 if (*p == '>') 21676 return TOK_ARROW; 21677 break; 21678 case 'i': 21679 if (match_identifier(p, "n")) 21680 return TOK_IN; 21681 if (match_identifier(p, "mport")) { 21682 *pp = p + 5; 21683 return TOK_IMPORT; 21684 } 21685 return TOK_IDENT; 21686 case 'o': 21687 if (match_identifier(p, "f")) 21688 return TOK_OF; 21689 return TOK_IDENT; 21690 case 'e': 21691 if (match_identifier(p, "xport")) 21692 return TOK_EXPORT; 21693 return TOK_IDENT; 21694 case 'f': 21695 if (match_identifier(p, "unction")) 21696 return TOK_FUNCTION; 21697 return TOK_IDENT; 21698 case '\\': 21699 if (*p == 'u') { 21700 if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE))) 21701 return TOK_IDENT; 21702 } 21703 break; 21704 default: 21705 if (c >= 128) { 21706 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p); 21707 if (no_line_terminator && (c == CP_PS || c == CP_LS)) 21708 return '\n'; 21709 } 21710 if (lre_is_space(c)) 21711 continue; 21712 if (lre_js_is_ident_first(c)) 21713 return TOK_IDENT; 21714 break; 21715 } 21716 return c; 21717 } 21718 } 21719 21720 static int peek_token(JSParseState *s, BOOL no_line_terminator) 21721 { 21722 const uint8_t *p = s->buf_ptr; 21723 return simple_next_token(&p, no_line_terminator); 21724 } 21725 21726 static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) 21727 { 21728 const uint8_t *p = *pp; 21729 int c; 21730 21731 if (p[0] == '#' && p[1] == '!') { 21732 p += 2; 21733 while (p < buf_end) { 21734 if (*p == '\n' || *p == '\r') { 21735 break; 21736 } else if (*p >= 0x80) { 21737 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); 21738 if (c == CP_LS || c == CP_PS) { 21739 break; 21740 } else if (c == -1) { 21741 p++; /* skip invalid UTF-8 */ 21742 } 21743 } else { 21744 p++; 21745 } 21746 } 21747 *pp = p; 21748 } 21749 } 21750 21751 /* return true if 'input' contains the source of a module 21752 (heuristic). 'input' must be a zero terminated. 21753 21754 Heuristic: skip comments and expect 'import' keyword not followed 21755 by '(' or '.' or export keyword. 21756 */ 21757 BOOL JS_DetectModule(const char *input, size_t input_len) 21758 { 21759 const uint8_t *p = (const uint8_t *)input; 21760 int tok; 21761 21762 skip_shebang(&p, p + input_len); 21763 switch(simple_next_token(&p, FALSE)) { 21764 case TOK_IMPORT: 21765 tok = simple_next_token(&p, FALSE); 21766 return (tok != '.' && tok != '('); 21767 case TOK_EXPORT: 21768 return TRUE; 21769 default: 21770 return FALSE; 21771 } 21772 } 21773 21774 static inline int get_prev_opcode(JSFunctionDef *fd) { 21775 if (fd->last_opcode_pos < 0) 21776 return OP_invalid; 21777 else 21778 return fd->byte_code.buf[fd->last_opcode_pos]; 21779 } 21780 21781 static BOOL js_is_live_code(JSParseState *s) { 21782 switch (get_prev_opcode(s->cur_func)) { 21783 case OP_tail_call: 21784 case OP_tail_call_method: 21785 case OP_return: 21786 case OP_return_undef: 21787 case OP_return_async: 21788 case OP_throw: 21789 case OP_throw_error: 21790 case OP_goto: 21791 #if SHORT_OPCODES 21792 case OP_goto8: 21793 case OP_goto16: 21794 #endif 21795 case OP_ret: 21796 return FALSE; 21797 default: 21798 return TRUE; 21799 } 21800 } 21801 21802 static void emit_u8(JSParseState *s, uint8_t val) 21803 { 21804 dbuf_putc(&s->cur_func->byte_code, val); 21805 } 21806 21807 static void emit_u16(JSParseState *s, uint16_t val) 21808 { 21809 dbuf_put_u16(&s->cur_func->byte_code, val); 21810 } 21811 21812 static void emit_u32(JSParseState *s, uint32_t val) 21813 { 21814 dbuf_put_u32(&s->cur_func->byte_code, val); 21815 } 21816 21817 static void emit_op(JSParseState *s, uint8_t val) 21818 { 21819 JSFunctionDef *fd = s->cur_func; 21820 DynBuf *bc = &fd->byte_code; 21821 21822 /* Use the line number of the last token used, not the next token, 21823 nor the current offset in the source file. 21824 */ 21825 if (unlikely(fd->last_opcode_line_num != s->last_line_num)) { 21826 dbuf_putc(bc, OP_line_num); 21827 dbuf_put_u32(bc, s->last_line_num); 21828 fd->last_opcode_line_num = s->last_line_num; 21829 } 21830 fd->last_opcode_pos = bc->size; 21831 dbuf_putc(bc, val); 21832 } 21833 21834 static void emit_atom(JSParseState *s, JSAtom name) 21835 { 21836 emit_u32(s, JS_DupAtom(s->ctx, name)); 21837 } 21838 21839 static int update_label(JSFunctionDef *s, int label, int delta) 21840 { 21841 LabelSlot *ls; 21842 21843 assert(label >= 0 && label < s->label_count); 21844 ls = &s->label_slots[label]; 21845 ls->ref_count += delta; 21846 assert(ls->ref_count >= 0); 21847 return ls->ref_count; 21848 } 21849 21850 static int new_label_fd(JSFunctionDef *fd, int label) 21851 { 21852 LabelSlot *ls; 21853 21854 if (label < 0) { 21855 if (js_resize_array(fd->ctx, (void *)&fd->label_slots, 21856 sizeof(fd->label_slots[0]), 21857 &fd->label_size, fd->label_count + 1)) 21858 return -1; 21859 label = fd->label_count++; 21860 ls = &fd->label_slots[label]; 21861 ls->ref_count = 0; 21862 ls->pos = -1; 21863 ls->pos2 = -1; 21864 ls->addr = -1; 21865 ls->first_reloc = NULL; 21866 } 21867 return label; 21868 } 21869 21870 static int new_label(JSParseState *s) 21871 { 21872 return new_label_fd(s->cur_func, -1); 21873 } 21874 21875 /* don't update the last opcode and don't emit line number info */ 21876 static void emit_label_raw(JSParseState *s, int label) 21877 { 21878 emit_u8(s, OP_label); 21879 emit_u32(s, label); 21880 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; 21881 } 21882 21883 /* return the label ID offset */ 21884 static int emit_label(JSParseState *s, int label) 21885 { 21886 if (label >= 0) { 21887 emit_op(s, OP_label); 21888 emit_u32(s, label); 21889 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; 21890 return s->cur_func->byte_code.size - 4; 21891 } else { 21892 return -1; 21893 } 21894 } 21895 21896 /* return label or -1 if dead code */ 21897 static int emit_goto(JSParseState *s, int opcode, int label) 21898 { 21899 if (js_is_live_code(s)) { 21900 if (label < 0) 21901 label = new_label(s); 21902 emit_op(s, opcode); 21903 emit_u32(s, label); 21904 s->cur_func->label_slots[label].ref_count++; 21905 return label; 21906 } 21907 return -1; 21908 } 21909 21910 /* return the constant pool index. 'val' is not duplicated. */ 21911 static int cpool_add(JSParseState *s, JSValue val) 21912 { 21913 JSFunctionDef *fd = s->cur_func; 21914 21915 if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]), 21916 &fd->cpool_size, fd->cpool_count + 1)) 21917 return -1; 21918 fd->cpool[fd->cpool_count++] = val; 21919 return fd->cpool_count - 1; 21920 } 21921 21922 static __exception int emit_push_const(JSParseState *s, JSValueConst val, 21923 BOOL as_atom) 21924 { 21925 int idx; 21926 21927 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) { 21928 JSAtom atom; 21929 /* warning: JS_NewAtomStr frees the string value */ 21930 JS_DupValue(s->ctx, val); 21931 atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); 21932 if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { 21933 emit_op(s, OP_push_atom_value); 21934 emit_u32(s, atom); 21935 return 0; 21936 } 21937 } 21938 21939 idx = cpool_add(s, JS_DupValue(s->ctx, val)); 21940 if (idx < 0) 21941 return -1; 21942 emit_op(s, OP_push_const); 21943 emit_u32(s, idx); 21944 return 0; 21945 } 21946 21947 /* return the variable index or -1 if not found, 21948 add ARGUMENT_VAR_OFFSET for argument variables */ 21949 static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 21950 { 21951 int i; 21952 for(i = fd->arg_count; i-- > 0;) { 21953 if (fd->args[i].var_name == name) 21954 return i | ARGUMENT_VAR_OFFSET; 21955 } 21956 return -1; 21957 } 21958 21959 static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 21960 { 21961 int i; 21962 for(i = fd->var_count; i-- > 0;) { 21963 if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0) 21964 return i; 21965 } 21966 return find_arg(ctx, fd, name); 21967 } 21968 21969 /* find a variable declaration in a given scope */ 21970 static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd, 21971 JSAtom name, int scope_level) 21972 { 21973 int scope_idx; 21974 for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0; 21975 scope_idx = fd->vars[scope_idx].scope_next) { 21976 if (fd->vars[scope_idx].scope_level != scope_level) 21977 break; 21978 if (fd->vars[scope_idx].var_name == name) 21979 return scope_idx; 21980 } 21981 return -1; 21982 } 21983 21984 /* return true if scope == parent_scope or if scope is a child of 21985 parent_scope */ 21986 static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd, 21987 int scope, int parent_scope) 21988 { 21989 while (scope >= 0) { 21990 if (scope == parent_scope) 21991 return TRUE; 21992 scope = fd->scopes[scope].parent; 21993 } 21994 return FALSE; 21995 } 21996 21997 /* find a 'var' declaration in the same scope or a child scope */ 21998 static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd, 21999 JSAtom name, int scope_level) 22000 { 22001 int i; 22002 for(i = 0; i < fd->var_count; i++) { 22003 JSVarDef *vd = &fd->vars[i]; 22004 if (vd->var_name == name && vd->scope_level == 0) { 22005 if (is_child_scope(ctx, fd, vd->scope_next, 22006 scope_level)) 22007 return i; 22008 } 22009 } 22010 return -1; 22011 } 22012 22013 22014 static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name) 22015 { 22016 int i; 22017 for(i = 0; i < fd->global_var_count; i++) { 22018 JSGlobalVar *hf = &fd->global_vars[i]; 22019 if (hf->var_name == name) 22020 return hf; 22021 } 22022 return NULL; 22023 22024 } 22025 22026 static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name) 22027 { 22028 JSGlobalVar *hf = find_global_var(fd, name); 22029 if (hf && hf->is_lexical) 22030 return hf; 22031 else 22032 return NULL; 22033 } 22034 22035 static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name, 22036 int scope_idx, BOOL check_catch_var) 22037 { 22038 while (scope_idx >= 0) { 22039 JSVarDef *vd = &fd->vars[scope_idx]; 22040 if (vd->var_name == name && 22041 (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH && 22042 check_catch_var))) 22043 return scope_idx; 22044 scope_idx = vd->scope_next; 22045 } 22046 22047 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) { 22048 if (find_lexical_global_var(fd, name)) 22049 return GLOBAL_VAR_OFFSET; 22050 } 22051 return -1; 22052 } 22053 22054 static int push_scope(JSParseState *s) { 22055 if (s->cur_func) { 22056 JSFunctionDef *fd = s->cur_func; 22057 int scope = fd->scope_count; 22058 /* XXX: should check for scope overflow */ 22059 if ((fd->scope_count + 1) > fd->scope_size) { 22060 int new_size; 22061 size_t slack; 22062 JSVarScope *new_buf; 22063 /* XXX: potential arithmetic overflow */ 22064 new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2); 22065 if (fd->scopes == fd->def_scope_array) { 22066 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack); 22067 if (!new_buf) 22068 return -1; 22069 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes)); 22070 } else { 22071 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack); 22072 if (!new_buf) 22073 return -1; 22074 } 22075 new_size += slack / sizeof(*new_buf); 22076 fd->scopes = new_buf; 22077 fd->scope_size = new_size; 22078 } 22079 fd->scope_count++; 22080 fd->scopes[scope].parent = fd->scope_level; 22081 fd->scopes[scope].first = fd->scope_first; 22082 emit_op(s, OP_enter_scope); 22083 emit_u16(s, scope); 22084 return fd->scope_level = scope; 22085 } 22086 return 0; 22087 } 22088 22089 static int get_first_lexical_var(JSFunctionDef *fd, int scope) 22090 { 22091 while (scope >= 0) { 22092 int scope_idx = fd->scopes[scope].first; 22093 if (scope_idx >= 0) 22094 return scope_idx; 22095 scope = fd->scopes[scope].parent; 22096 } 22097 return -1; 22098 } 22099 22100 static void pop_scope(JSParseState *s) { 22101 if (s->cur_func) { 22102 /* disable scoped variables */ 22103 JSFunctionDef *fd = s->cur_func; 22104 int scope = fd->scope_level; 22105 emit_op(s, OP_leave_scope); 22106 emit_u16(s, scope); 22107 fd->scope_level = fd->scopes[scope].parent; 22108 fd->scope_first = get_first_lexical_var(fd, fd->scope_level); 22109 } 22110 } 22111 22112 static void close_scopes(JSParseState *s, int scope, int scope_stop) 22113 { 22114 while (scope > scope_stop) { 22115 emit_op(s, OP_leave_scope); 22116 emit_u16(s, scope); 22117 scope = s->cur_func->scopes[scope].parent; 22118 } 22119 } 22120 22121 /* return the variable index or -1 if error */ 22122 static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 22123 { 22124 JSVarDef *vd; 22125 22126 /* the local variable indexes are currently stored on 16 bits */ 22127 if (fd->var_count >= JS_MAX_LOCAL_VARS) { 22128 JS_ThrowInternalError(ctx, "too many local variables"); 22129 return -1; 22130 } 22131 if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]), 22132 &fd->var_size, fd->var_count + 1)) 22133 return -1; 22134 vd = &fd->vars[fd->var_count++]; 22135 memset(vd, 0, sizeof(*vd)); 22136 vd->var_name = JS_DupAtom(ctx, name); 22137 vd->func_pool_idx = -1; 22138 return fd->var_count - 1; 22139 } 22140 22141 static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name, 22142 JSVarKindEnum var_kind) 22143 { 22144 int idx = add_var(ctx, fd, name); 22145 if (idx >= 0) { 22146 JSVarDef *vd = &fd->vars[idx]; 22147 vd->var_kind = var_kind; 22148 vd->scope_level = fd->scope_level; 22149 vd->scope_next = fd->scope_first; 22150 fd->scopes[fd->scope_level].first = idx; 22151 fd->scope_first = idx; 22152 } 22153 return idx; 22154 } 22155 22156 static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 22157 { 22158 int idx = fd->func_var_idx; 22159 if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) { 22160 fd->func_var_idx = idx; 22161 fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME; 22162 if (fd->js_mode & JS_MODE_STRICT) 22163 fd->vars[idx].is_const = TRUE; 22164 } 22165 return idx; 22166 } 22167 22168 static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd) 22169 { 22170 int idx = fd->arguments_var_idx; 22171 if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) { 22172 fd->arguments_var_idx = idx; 22173 } 22174 return idx; 22175 } 22176 22177 /* add an argument definition in the argument scope. Only needed when 22178 "eval()" may be called in the argument scope. Return 0 if OK. */ 22179 static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd) 22180 { 22181 int idx; 22182 if (fd->arguments_arg_idx < 0) { 22183 idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX); 22184 if (idx < 0) { 22185 /* XXX: the scope links are not fully updated. May be an 22186 issue if there are child scopes of the argument 22187 scope */ 22188 idx = add_var(ctx, fd, JS_ATOM_arguments); 22189 if (idx < 0) 22190 return -1; 22191 fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first; 22192 fd->scopes[ARG_SCOPE_INDEX].first = idx; 22193 fd->vars[idx].scope_level = ARG_SCOPE_INDEX; 22194 fd->vars[idx].is_lexical = TRUE; 22195 22196 fd->arguments_arg_idx = idx; 22197 } 22198 } 22199 return 0; 22200 } 22201 22202 static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name) 22203 { 22204 JSVarDef *vd; 22205 22206 /* the local variable indexes are currently stored on 16 bits */ 22207 if (fd->arg_count >= JS_MAX_LOCAL_VARS) { 22208 JS_ThrowInternalError(ctx, "too many arguments"); 22209 return -1; 22210 } 22211 if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]), 22212 &fd->arg_size, fd->arg_count + 1)) 22213 return -1; 22214 vd = &fd->args[fd->arg_count++]; 22215 memset(vd, 0, sizeof(*vd)); 22216 vd->var_name = JS_DupAtom(ctx, name); 22217 vd->func_pool_idx = -1; 22218 return fd->arg_count - 1; 22219 } 22220 22221 /* add a global variable definition */ 22222 static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s, 22223 JSAtom name) 22224 { 22225 JSGlobalVar *hf; 22226 22227 if (js_resize_array(ctx, (void **)&s->global_vars, 22228 sizeof(s->global_vars[0]), 22229 &s->global_var_size, s->global_var_count + 1)) 22230 return NULL; 22231 hf = &s->global_vars[s->global_var_count++]; 22232 hf->cpool_idx = -1; 22233 hf->force_init = FALSE; 22234 hf->is_lexical = FALSE; 22235 hf->is_const = FALSE; 22236 hf->scope_level = s->scope_level; 22237 hf->var_name = JS_DupAtom(ctx, name); 22238 return hf; 22239 } 22240 22241 typedef enum { 22242 JS_VAR_DEF_WITH, 22243 JS_VAR_DEF_LET, 22244 JS_VAR_DEF_CONST, 22245 JS_VAR_DEF_FUNCTION_DECL, /* function declaration */ 22246 JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */ 22247 JS_VAR_DEF_CATCH, 22248 JS_VAR_DEF_VAR, 22249 } JSVarDefEnum; 22250 22251 static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, 22252 JSVarDefEnum var_def_type) 22253 { 22254 JSContext *ctx = s->ctx; 22255 JSVarDef *vd; 22256 int idx; 22257 22258 switch (var_def_type) { 22259 case JS_VAR_DEF_WITH: 22260 idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL); 22261 break; 22262 22263 case JS_VAR_DEF_LET: 22264 case JS_VAR_DEF_CONST: 22265 case JS_VAR_DEF_FUNCTION_DECL: 22266 case JS_VAR_DEF_NEW_FUNCTION_DECL: 22267 idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE); 22268 if (idx >= 0) { 22269 if (idx < GLOBAL_VAR_OFFSET) { 22270 if (fd->vars[idx].scope_level == fd->scope_level) { 22271 /* same scope: in non strict mode, functions 22272 can be redefined (annex B.3.3.4). */ 22273 if (!(!(fd->js_mode & JS_MODE_STRICT) && 22274 var_def_type == JS_VAR_DEF_FUNCTION_DECL && 22275 fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) { 22276 goto redef_lex_error; 22277 } 22278 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) { 22279 goto redef_lex_error; 22280 } 22281 } else { 22282 if (fd->scope_level == fd->body_scope) { 22283 redef_lex_error: 22284 /* redefining a scoped var in the same scope: error */ 22285 return js_parse_error(s, "invalid redefinition of lexical identifier"); 22286 } 22287 } 22288 } 22289 if (var_def_type != JS_VAR_DEF_FUNCTION_DECL && 22290 var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL && 22291 fd->scope_level == fd->body_scope && 22292 find_arg(ctx, fd, name) >= 0) { 22293 /* lexical variable redefines a parameter name */ 22294 return js_parse_error(s, "invalid redefinition of parameter name"); 22295 } 22296 22297 if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) { 22298 return js_parse_error(s, "invalid redefinition of a variable"); 22299 } 22300 22301 if (fd->is_global_var) { 22302 JSGlobalVar *hf; 22303 hf = find_global_var(fd, name); 22304 if (hf && is_child_scope(ctx, fd, hf->scope_level, 22305 fd->scope_level)) { 22306 return js_parse_error(s, "invalid redefinition of global identifier"); 22307 } 22308 } 22309 22310 if (fd->is_eval && 22311 (fd->eval_type == JS_EVAL_TYPE_GLOBAL || 22312 fd->eval_type == JS_EVAL_TYPE_MODULE) && 22313 fd->scope_level == fd->body_scope) { 22314 JSGlobalVar *hf; 22315 hf = add_global_var(s->ctx, fd, name); 22316 if (!hf) 22317 return -1; 22318 hf->is_lexical = TRUE; 22319 hf->is_const = (var_def_type == JS_VAR_DEF_CONST); 22320 idx = GLOBAL_VAR_OFFSET; 22321 } else { 22322 JSVarKindEnum var_kind; 22323 if (var_def_type == JS_VAR_DEF_FUNCTION_DECL) 22324 var_kind = JS_VAR_FUNCTION_DECL; 22325 else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL) 22326 var_kind = JS_VAR_NEW_FUNCTION_DECL; 22327 else 22328 var_kind = JS_VAR_NORMAL; 22329 idx = add_scope_var(ctx, fd, name, var_kind); 22330 if (idx >= 0) { 22331 vd = &fd->vars[idx]; 22332 vd->is_lexical = 1; 22333 vd->is_const = (var_def_type == JS_VAR_DEF_CONST); 22334 } 22335 } 22336 break; 22337 22338 case JS_VAR_DEF_CATCH: 22339 idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH); 22340 break; 22341 22342 case JS_VAR_DEF_VAR: 22343 if (find_lexical_decl(ctx, fd, name, fd->scope_first, 22344 FALSE) >= 0) { 22345 invalid_lexical_redefinition: 22346 /* error to redefine a var that inside a lexical scope */ 22347 return js_parse_error(s, "invalid redefinition of lexical identifier"); 22348 } 22349 if (fd->is_global_var) { 22350 JSGlobalVar *hf; 22351 hf = find_global_var(fd, name); 22352 if (hf && hf->is_lexical && hf->scope_level == fd->scope_level && 22353 fd->eval_type == JS_EVAL_TYPE_MODULE) { 22354 goto invalid_lexical_redefinition; 22355 } 22356 hf = add_global_var(s->ctx, fd, name); 22357 if (!hf) 22358 return -1; 22359 idx = GLOBAL_VAR_OFFSET; 22360 } else { 22361 /* if the variable already exists, don't add it again */ 22362 idx = find_var(ctx, fd, name); 22363 if (idx >= 0) 22364 break; 22365 idx = add_var(ctx, fd, name); 22366 if (idx >= 0) { 22367 if (name == JS_ATOM_arguments && fd->has_arguments_binding) 22368 fd->arguments_var_idx = idx; 22369 fd->vars[idx].scope_next = fd->scope_level; 22370 } 22371 } 22372 break; 22373 default: 22374 abort(); 22375 } 22376 return idx; 22377 } 22378 22379 /* add a private field variable in the current scope */ 22380 static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, 22381 JSAtom name, JSVarKindEnum var_kind, BOOL is_static) 22382 { 22383 JSContext *ctx = s->ctx; 22384 JSVarDef *vd; 22385 int idx; 22386 22387 idx = add_scope_var(ctx, fd, name, var_kind); 22388 if (idx < 0) 22389 return idx; 22390 vd = &fd->vars[idx]; 22391 vd->is_lexical = 1; 22392 vd->is_const = 1; 22393 vd->is_static_private = is_static; 22394 return idx; 22395 } 22396 22397 static __exception int js_parse_expr(JSParseState *s); 22398 static __exception int js_parse_function_decl(JSParseState *s, 22399 JSParseFunctionEnum func_type, 22400 JSFunctionKindEnum func_kind, 22401 JSAtom func_name, const uint8_t *ptr, 22402 int start_line); 22403 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); 22404 static __exception int js_parse_function_decl2(JSParseState *s, 22405 JSParseFunctionEnum func_type, 22406 JSFunctionKindEnum func_kind, 22407 JSAtom func_name, 22408 const uint8_t *ptr, 22409 int function_line_num, 22410 JSParseExportEnum export_flag, 22411 JSFunctionDef **pfd); 22412 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); 22413 static __exception int js_parse_assign_expr(JSParseState *s); 22414 static __exception int js_parse_unary(JSParseState *s, int parse_flags); 22415 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, 22416 JSAtom label_name, 22417 int label_break, int label_cont, 22418 int drop_count); 22419 static void pop_break_entry(JSFunctionDef *fd); 22420 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, 22421 JSAtom local_name, JSAtom export_name, 22422 JSExportTypeEnum export_type); 22423 22424 /* Note: all the fields are already sealed except length */ 22425 static int seal_template_obj(JSContext *ctx, JSValueConst obj) 22426 { 22427 JSObject *p; 22428 JSShapeProperty *prs; 22429 22430 p = JS_VALUE_GET_OBJ(obj); 22431 prs = find_own_property1(p, JS_ATOM_length); 22432 if (prs) { 22433 if (js_update_property_flags(ctx, p, &prs, 22434 prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE))) 22435 return -1; 22436 } 22437 p->extensible = FALSE; 22438 return 0; 22439 } 22440 22441 static __exception int js_parse_template(JSParseState *s, int call, int *argc) 22442 { 22443 JSContext *ctx = s->ctx; 22444 JSValue raw_array, template_object; 22445 JSToken cooked; 22446 int depth, ret; 22447 22448 raw_array = JS_UNDEFINED; /* avoid warning */ 22449 template_object = JS_UNDEFINED; /* avoid warning */ 22450 if (call) { 22451 /* Create a template object: an array of cooked strings */ 22452 /* Create an array of raw strings and store it to the raw property */ 22453 template_object = JS_NewArray(ctx); 22454 if (JS_IsException(template_object)) 22455 return -1; 22456 // pool_idx = s->cur_func->cpool_count; 22457 ret = emit_push_const(s, template_object, 0); 22458 JS_FreeValue(ctx, template_object); 22459 if (ret) 22460 return -1; 22461 raw_array = JS_NewArray(ctx); 22462 if (JS_IsException(raw_array)) 22463 return -1; 22464 if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw, 22465 raw_array, JS_PROP_THROW) < 0) { 22466 return -1; 22467 } 22468 } 22469 22470 depth = 0; 22471 while (s->token.val == TOK_TEMPLATE) { 22472 const uint8_t *p = s->token.ptr + 1; 22473 cooked = s->token; 22474 if (call) { 22475 if (JS_DefinePropertyValueUint32(ctx, raw_array, depth, 22476 JS_DupValue(ctx, s->token.u.str.str), 22477 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { 22478 return -1; 22479 } 22480 /* re-parse the string with escape sequences but do not throw a 22481 syntax error if it contains invalid sequences 22482 */ 22483 if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) { 22484 cooked.u.str.str = JS_UNDEFINED; 22485 } 22486 if (JS_DefinePropertyValueUint32(ctx, template_object, depth, 22487 cooked.u.str.str, 22488 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) { 22489 return -1; 22490 } 22491 } else { 22492 JSString *str; 22493 /* re-parse the string with escape sequences and throw a 22494 syntax error if it contains invalid sequences 22495 */ 22496 JS_FreeValue(ctx, s->token.u.str.str); 22497 s->token.u.str.str = JS_UNDEFINED; 22498 if (js_parse_string(s, '`', TRUE, p, &cooked, &p)) 22499 return -1; 22500 str = JS_VALUE_GET_STRING(cooked.u.str.str); 22501 if (str->len != 0 || depth == 0) { 22502 ret = emit_push_const(s, cooked.u.str.str, 1); 22503 JS_FreeValue(s->ctx, cooked.u.str.str); 22504 if (ret) 22505 return -1; 22506 if (depth == 0) { 22507 if (s->token.u.str.sep == '`') 22508 goto done1; 22509 emit_op(s, OP_get_field2); 22510 emit_atom(s, JS_ATOM_concat); 22511 } 22512 depth++; 22513 } else { 22514 JS_FreeValue(s->ctx, cooked.u.str.str); 22515 } 22516 } 22517 if (s->token.u.str.sep == '`') 22518 goto done; 22519 if (next_token(s)) 22520 return -1; 22521 if (js_parse_expr(s)) 22522 return -1; 22523 depth++; 22524 if (s->token.val != '}') { 22525 return js_parse_error(s, "expected '}' after template expression"); 22526 } 22527 /* XXX: should convert to string at this stage? */ 22528 free_token(s, &s->token); 22529 /* Resume TOK_TEMPLATE parsing (s->token.line_num and 22530 * s->token.ptr are OK) */ 22531 s->got_lf = FALSE; 22532 s->last_line_num = s->token.line_num; 22533 if (js_parse_template_part(s, s->buf_ptr)) 22534 return -1; 22535 } 22536 return js_parse_expect(s, TOK_TEMPLATE); 22537 22538 done: 22539 if (call) { 22540 /* Seal the objects */ 22541 seal_template_obj(ctx, raw_array); 22542 seal_template_obj(ctx, template_object); 22543 *argc = depth + 1; 22544 } else { 22545 emit_op(s, OP_call_method); 22546 emit_u16(s, depth - 1); 22547 } 22548 done1: 22549 return next_token(s); 22550 } 22551 22552 22553 #define PROP_TYPE_IDENT 0 22554 #define PROP_TYPE_VAR 1 22555 #define PROP_TYPE_GET 2 22556 #define PROP_TYPE_SET 3 22557 #define PROP_TYPE_STAR 4 22558 #define PROP_TYPE_ASYNC 5 22559 #define PROP_TYPE_ASYNC_STAR 6 22560 22561 #define PROP_TYPE_PRIVATE (1 << 4) 22562 22563 static BOOL token_is_ident(int tok) 22564 { 22565 /* Accept keywords and reserved words as property names */ 22566 return (tok == TOK_IDENT || 22567 (tok >= TOK_FIRST_KEYWORD && 22568 tok <= TOK_LAST_KEYWORD)); 22569 } 22570 22571 /* if the property is an expression, name = JS_ATOM_NULL */ 22572 static int __exception js_parse_property_name(JSParseState *s, 22573 JSAtom *pname, 22574 BOOL allow_method, BOOL allow_var, 22575 BOOL allow_private) 22576 { 22577 int is_private = 0; 22578 BOOL is_non_reserved_ident; 22579 JSAtom name; 22580 int prop_type; 22581 22582 prop_type = PROP_TYPE_IDENT; 22583 if (allow_method) { 22584 if (token_is_pseudo_keyword(s, JS_ATOM_get) 22585 || token_is_pseudo_keyword(s, JS_ATOM_set)) { 22586 /* get x(), set x() */ 22587 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 22588 if (next_token(s)) 22589 goto fail1; 22590 if (s->token.val == ':' || s->token.val == ',' || 22591 s->token.val == '}' || s->token.val == '(' || 22592 s->token.val == '=') { 22593 is_non_reserved_ident = TRUE; 22594 goto ident_found; 22595 } 22596 prop_type = PROP_TYPE_GET + (name == JS_ATOM_set); 22597 JS_FreeAtom(s->ctx, name); 22598 } else if (s->token.val == '*') { 22599 if (next_token(s)) 22600 goto fail; 22601 prop_type = PROP_TYPE_STAR; 22602 } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && 22603 peek_token(s, TRUE) != '\n') { 22604 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 22605 if (next_token(s)) 22606 goto fail1; 22607 if (s->token.val == ':' || s->token.val == ',' || 22608 s->token.val == '}' || s->token.val == '(' || 22609 s->token.val == '=') { 22610 is_non_reserved_ident = TRUE; 22611 goto ident_found; 22612 } 22613 JS_FreeAtom(s->ctx, name); 22614 if (s->token.val == '*') { 22615 if (next_token(s)) 22616 goto fail; 22617 prop_type = PROP_TYPE_ASYNC_STAR; 22618 } else { 22619 prop_type = PROP_TYPE_ASYNC; 22620 } 22621 } 22622 } 22623 22624 if (token_is_ident(s->token.val)) { 22625 /* variable can only be a non-reserved identifier */ 22626 is_non_reserved_ident = 22627 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved); 22628 /* keywords and reserved words have a valid atom */ 22629 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 22630 if (next_token(s)) 22631 goto fail1; 22632 ident_found: 22633 if (is_non_reserved_ident && 22634 prop_type == PROP_TYPE_IDENT && allow_var) { 22635 if (!(s->token.val == ':' || 22636 (s->token.val == '(' && allow_method))) { 22637 prop_type = PROP_TYPE_VAR; 22638 } 22639 } 22640 } else if (s->token.val == TOK_STRING) { 22641 name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 22642 if (name == JS_ATOM_NULL) 22643 goto fail; 22644 if (next_token(s)) 22645 goto fail1; 22646 } else if (s->token.val == TOK_NUMBER) { 22647 JSValue val; 22648 val = s->token.u.num.val; 22649 #ifdef CONFIG_BIGNUM 22650 if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { 22651 JSBigFloat *p = JS_VALUE_GET_PTR(val); 22652 val = s->ctx->rt->bigfloat_ops. 22653 mul_pow10_to_float64(s->ctx, &p->num, 22654 s->token.u.num.exponent); 22655 if (JS_IsException(val)) 22656 goto fail; 22657 name = JS_ValueToAtom(s->ctx, val); 22658 JS_FreeValue(s->ctx, val); 22659 } else 22660 #endif 22661 { 22662 name = JS_ValueToAtom(s->ctx, val); 22663 } 22664 if (name == JS_ATOM_NULL) 22665 goto fail; 22666 if (next_token(s)) 22667 goto fail1; 22668 } else if (s->token.val == '[') { 22669 if (next_token(s)) 22670 goto fail; 22671 if (js_parse_expr(s)) 22672 goto fail; 22673 if (js_parse_expect(s, ']')) 22674 goto fail; 22675 name = JS_ATOM_NULL; 22676 } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) { 22677 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 22678 if (next_token(s)) 22679 goto fail1; 22680 is_private = PROP_TYPE_PRIVATE; 22681 } else { 22682 goto invalid_prop; 22683 } 22684 if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR && 22685 s->token.val != '(') { 22686 JS_FreeAtom(s->ctx, name); 22687 invalid_prop: 22688 js_parse_error(s, "invalid property name"); 22689 goto fail; 22690 } 22691 *pname = name; 22692 return prop_type | is_private; 22693 fail1: 22694 JS_FreeAtom(s->ctx, name); 22695 fail: 22696 *pname = JS_ATOM_NULL; 22697 return -1; 22698 } 22699 22700 typedef struct JSParsePos { 22701 int last_line_num; 22702 int line_num; 22703 BOOL got_lf; 22704 const uint8_t *ptr; 22705 } JSParsePos; 22706 22707 static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) 22708 { 22709 sp->last_line_num = s->last_line_num; 22710 sp->line_num = s->token.line_num; 22711 sp->ptr = s->token.ptr; 22712 sp->got_lf = s->got_lf; 22713 return 0; 22714 } 22715 22716 static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) 22717 { 22718 s->token.line_num = sp->last_line_num; 22719 s->line_num = sp->line_num; 22720 s->buf_ptr = sp->ptr; 22721 s->got_lf = sp->got_lf; 22722 return next_token(s); 22723 } 22724 22725 /* return TRUE if a regexp literal is allowed after this token */ 22726 static BOOL is_regexp_allowed(int tok) 22727 { 22728 switch (tok) { 22729 case TOK_NUMBER: 22730 case TOK_STRING: 22731 case TOK_REGEXP: 22732 case TOK_DEC: 22733 case TOK_INC: 22734 case TOK_NULL: 22735 case TOK_FALSE: 22736 case TOK_TRUE: 22737 case TOK_THIS: 22738 case ')': 22739 case ']': 22740 case '}': /* XXX: regexp may occur after */ 22741 case TOK_IDENT: 22742 return FALSE; 22743 default: 22744 return TRUE; 22745 } 22746 } 22747 22748 #define SKIP_HAS_SEMI (1 << 0) 22749 #define SKIP_HAS_ELLIPSIS (1 << 1) 22750 #define SKIP_HAS_ASSIGNMENT (1 << 2) 22751 22752 /* XXX: improve speed with early bailout */ 22753 /* XXX: no longer works if regexps are present. Could use previous 22754 regexp parsing heuristics to handle most cases */ 22755 static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator) 22756 { 22757 char state[256]; 22758 size_t level = 0; 22759 JSParsePos pos; 22760 int last_tok, tok = TOK_EOF; 22761 int c, tok_len, bits = 0; 22762 22763 /* protect from underflow */ 22764 state[level++] = 0; 22765 22766 js_parse_get_pos(s, &pos); 22767 last_tok = 0; 22768 for (;;) { 22769 switch(s->token.val) { 22770 case '(': 22771 case '[': 22772 case '{': 22773 if (level >= sizeof(state)) 22774 goto done; 22775 state[level++] = s->token.val; 22776 break; 22777 case ')': 22778 if (state[--level] != '(') 22779 goto done; 22780 break; 22781 case ']': 22782 if (state[--level] != '[') 22783 goto done; 22784 break; 22785 case '}': 22786 c = state[--level]; 22787 if (c == '`') { 22788 /* continue the parsing of the template */ 22789 free_token(s, &s->token); 22790 /* Resume TOK_TEMPLATE parsing (s->token.line_num and 22791 * s->token.ptr are OK) */ 22792 s->got_lf = FALSE; 22793 s->last_line_num = s->token.line_num; 22794 if (js_parse_template_part(s, s->buf_ptr)) 22795 goto done; 22796 goto handle_template; 22797 } else if (c != '{') { 22798 goto done; 22799 } 22800 break; 22801 case TOK_TEMPLATE: 22802 handle_template: 22803 if (s->token.u.str.sep != '`') { 22804 /* '${' inside the template : closing '}' and continue 22805 parsing the template */ 22806 if (level >= sizeof(state)) 22807 goto done; 22808 state[level++] = '`'; 22809 } 22810 break; 22811 case TOK_EOF: 22812 goto done; 22813 case ';': 22814 if (level == 2) { 22815 bits |= SKIP_HAS_SEMI; 22816 } 22817 break; 22818 case TOK_ELLIPSIS: 22819 if (level == 2) { 22820 bits |= SKIP_HAS_ELLIPSIS; 22821 } 22822 break; 22823 case '=': 22824 bits |= SKIP_HAS_ASSIGNMENT; 22825 break; 22826 22827 case TOK_DIV_ASSIGN: 22828 tok_len = 2; 22829 goto parse_regexp; 22830 case '/': 22831 tok_len = 1; 22832 parse_regexp: 22833 if (is_regexp_allowed(last_tok)) { 22834 s->buf_ptr -= tok_len; 22835 if (js_parse_regexp(s)) { 22836 /* XXX: should clear the exception */ 22837 goto done; 22838 } 22839 } 22840 break; 22841 } 22842 /* last_tok is only used to recognize regexps */ 22843 if (s->token.val == TOK_IDENT && 22844 (token_is_pseudo_keyword(s, JS_ATOM_of) || 22845 token_is_pseudo_keyword(s, JS_ATOM_yield))) { 22846 last_tok = TOK_OF; 22847 } else { 22848 last_tok = s->token.val; 22849 } 22850 if (next_token(s)) { 22851 /* XXX: should clear the exception generated by next_token() */ 22852 break; 22853 } 22854 if (level <= 1) { 22855 tok = s->token.val; 22856 if (token_is_pseudo_keyword(s, JS_ATOM_of)) 22857 tok = TOK_OF; 22858 if (no_line_terminator && s->last_line_num != s->token.line_num) 22859 tok = '\n'; 22860 break; 22861 } 22862 } 22863 done: 22864 if (pbits) { 22865 *pbits = bits; 22866 } 22867 if (js_parse_seek_token(s, &pos)) 22868 return -1; 22869 return tok; 22870 } 22871 22872 static void set_object_name(JSParseState *s, JSAtom name) 22873 { 22874 JSFunctionDef *fd = s->cur_func; 22875 int opcode; 22876 22877 opcode = get_prev_opcode(fd); 22878 if (opcode == OP_set_name) { 22879 /* XXX: should free atom after OP_set_name? */ 22880 fd->byte_code.size = fd->last_opcode_pos; 22881 fd->last_opcode_pos = -1; 22882 emit_op(s, OP_set_name); 22883 emit_atom(s, name); 22884 } else if (opcode == OP_set_class_name) { 22885 int define_class_pos; 22886 JSAtom atom; 22887 define_class_pos = fd->last_opcode_pos + 1 - 22888 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 22889 assert(fd->byte_code.buf[define_class_pos] == OP_define_class); 22890 /* for consistency we free the previous atom which is 22891 JS_ATOM_empty_string */ 22892 atom = get_u32(fd->byte_code.buf + define_class_pos + 1); 22893 JS_FreeAtom(s->ctx, atom); 22894 put_u32(fd->byte_code.buf + define_class_pos + 1, 22895 JS_DupAtom(s->ctx, name)); 22896 fd->last_opcode_pos = -1; 22897 } 22898 } 22899 22900 static void set_object_name_computed(JSParseState *s) 22901 { 22902 JSFunctionDef *fd = s->cur_func; 22903 int opcode; 22904 22905 opcode = get_prev_opcode(fd); 22906 if (opcode == OP_set_name) { 22907 /* XXX: should free atom after OP_set_name? */ 22908 fd->byte_code.size = fd->last_opcode_pos; 22909 fd->last_opcode_pos = -1; 22910 emit_op(s, OP_set_name_computed); 22911 } else if (opcode == OP_set_class_name) { 22912 int define_class_pos; 22913 define_class_pos = fd->last_opcode_pos + 1 - 22914 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 22915 assert(fd->byte_code.buf[define_class_pos] == OP_define_class); 22916 fd->byte_code.buf[define_class_pos] = OP_define_class_computed; 22917 fd->last_opcode_pos = -1; 22918 } 22919 } 22920 22921 static __exception int js_parse_object_literal(JSParseState *s) 22922 { 22923 JSAtom name = JS_ATOM_NULL; 22924 const uint8_t *start_ptr; 22925 int start_line, prop_type; 22926 BOOL has_proto; 22927 22928 if (next_token(s)) 22929 goto fail; 22930 /* XXX: add an initial length that will be patched back */ 22931 emit_op(s, OP_object); 22932 has_proto = FALSE; 22933 while (s->token.val != '}') { 22934 /* specific case for getter/setter */ 22935 start_ptr = s->token.ptr; 22936 start_line = s->token.line_num; 22937 22938 if (s->token.val == TOK_ELLIPSIS) { 22939 if (next_token(s)) 22940 return -1; 22941 if (js_parse_assign_expr(s)) 22942 return -1; 22943 emit_op(s, OP_null); /* dummy excludeList */ 22944 emit_op(s, OP_copy_data_properties); 22945 emit_u8(s, 2 | (1 << 2) | (0 << 5)); 22946 emit_op(s, OP_drop); /* pop excludeList */ 22947 emit_op(s, OP_drop); /* pop src object */ 22948 goto next; 22949 } 22950 22951 prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE); 22952 if (prop_type < 0) 22953 goto fail; 22954 22955 if (prop_type == PROP_TYPE_VAR) { 22956 /* shortcut for x: x */ 22957 emit_op(s, OP_scope_get_var); 22958 emit_atom(s, name); 22959 emit_u16(s, s->cur_func->scope_level); 22960 emit_op(s, OP_define_field); 22961 emit_atom(s, name); 22962 } else if (s->token.val == '(') { 22963 BOOL is_getset = (prop_type == PROP_TYPE_GET || 22964 prop_type == PROP_TYPE_SET); 22965 JSParseFunctionEnum func_type; 22966 JSFunctionKindEnum func_kind; 22967 int op_flags; 22968 22969 func_kind = JS_FUNC_NORMAL; 22970 if (is_getset) { 22971 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET; 22972 } else { 22973 func_type = JS_PARSE_FUNC_METHOD; 22974 if (prop_type == PROP_TYPE_STAR) 22975 func_kind = JS_FUNC_GENERATOR; 22976 else if (prop_type == PROP_TYPE_ASYNC) 22977 func_kind = JS_FUNC_ASYNC; 22978 else if (prop_type == PROP_TYPE_ASYNC_STAR) 22979 func_kind = JS_FUNC_ASYNC_GENERATOR; 22980 } 22981 if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL, 22982 start_ptr, start_line)) 22983 goto fail; 22984 if (name == JS_ATOM_NULL) { 22985 emit_op(s, OP_define_method_computed); 22986 } else { 22987 emit_op(s, OP_define_method); 22988 emit_atom(s, name); 22989 } 22990 if (is_getset) { 22991 op_flags = OP_DEFINE_METHOD_GETTER + 22992 prop_type - PROP_TYPE_GET; 22993 } else { 22994 op_flags = OP_DEFINE_METHOD_METHOD; 22995 } 22996 emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE); 22997 } else { 22998 if (js_parse_expect(s, ':')) 22999 goto fail; 23000 if (js_parse_assign_expr(s)) 23001 goto fail; 23002 if (name == JS_ATOM_NULL) { 23003 set_object_name_computed(s); 23004 emit_op(s, OP_define_array_el); 23005 emit_op(s, OP_drop); 23006 } else if (name == JS_ATOM___proto__) { 23007 if (has_proto) { 23008 js_parse_error(s, "duplicate __proto__ property name"); 23009 goto fail; 23010 } 23011 emit_op(s, OP_set_proto); 23012 has_proto = TRUE; 23013 } else { 23014 set_object_name(s, name); 23015 emit_op(s, OP_define_field); 23016 emit_atom(s, name); 23017 } 23018 } 23019 JS_FreeAtom(s->ctx, name); 23020 next: 23021 name = JS_ATOM_NULL; 23022 if (s->token.val != ',') 23023 break; 23024 if (next_token(s)) 23025 goto fail; 23026 } 23027 if (js_parse_expect(s, '}')) 23028 goto fail; 23029 return 0; 23030 fail: 23031 JS_FreeAtom(s->ctx, name); 23032 return -1; 23033 } 23034 23035 /* allow the 'in' binary operator */ 23036 #define PF_IN_ACCEPTED (1 << 0) 23037 /* allow function calls parsing in js_parse_postfix_expr() */ 23038 #define PF_POSTFIX_CALL (1 << 1) 23039 /* allow the exponentiation operator in js_parse_unary() */ 23040 #define PF_POW_ALLOWED (1 << 2) 23041 /* forbid the exponentiation operator in js_parse_unary() */ 23042 #define PF_POW_FORBIDDEN (1 << 3) 23043 23044 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); 23045 23046 static __exception int js_parse_left_hand_side_expr(JSParseState *s) 23047 { 23048 return js_parse_postfix_expr(s, PF_POSTFIX_CALL); 23049 } 23050 23051 /* XXX: could generate specific bytecode */ 23052 static __exception int js_parse_class_default_ctor(JSParseState *s, 23053 BOOL has_super, 23054 JSFunctionDef **pfd) 23055 { 23056 JSParsePos pos; 23057 const char *str; 23058 int ret, line_num; 23059 JSParseFunctionEnum func_type; 23060 const uint8_t *saved_buf_end; 23061 23062 js_parse_get_pos(s, &pos); 23063 if (has_super) { 23064 /* spec change: no argument evaluation */ 23065 str = "(){super(...arguments);}"; 23066 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; 23067 } else { 23068 str = "(){}"; 23069 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; 23070 } 23071 line_num = s->token.line_num; 23072 saved_buf_end = s->buf_end; 23073 s->buf_ptr = (uint8_t *)str; 23074 s->buf_end = (uint8_t *)(str + strlen(str)); 23075 ret = next_token(s); 23076 if (!ret) { 23077 ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL, 23078 JS_ATOM_NULL, (uint8_t *)str, 23079 line_num, JS_PARSE_EXPORT_NONE, pfd); 23080 } 23081 s->buf_end = saved_buf_end; 23082 ret |= js_parse_seek_token(s, &pos); 23083 return ret; 23084 } 23085 23086 /* find field in the current scope */ 23087 static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd, 23088 JSAtom name, int scope_level) 23089 { 23090 int idx; 23091 idx = fd->scopes[scope_level].first; 23092 while (idx != -1) { 23093 if (fd->vars[idx].scope_level != scope_level) 23094 break; 23095 if (fd->vars[idx].var_name == name) 23096 return idx; 23097 idx = fd->vars[idx].scope_next; 23098 } 23099 return -1; 23100 } 23101 23102 /* initialize the class fields, called by the constructor. Note: 23103 super() can be called in an arrow function, so <this> and 23104 <class_fields_init> can be variable references */ 23105 static void emit_class_field_init(JSParseState *s) 23106 { 23107 int label_next; 23108 23109 emit_op(s, OP_scope_get_var); 23110 emit_atom(s, JS_ATOM_class_fields_init); 23111 emit_u16(s, s->cur_func->scope_level); 23112 23113 /* no need to call the class field initializer if not defined */ 23114 emit_op(s, OP_dup); 23115 label_next = emit_goto(s, OP_if_false, -1); 23116 23117 emit_op(s, OP_scope_get_var); 23118 emit_atom(s, JS_ATOM_this); 23119 emit_u16(s, 0); 23120 23121 emit_op(s, OP_swap); 23122 23123 emit_op(s, OP_call_method); 23124 emit_u16(s, 0); 23125 23126 emit_label(s, label_next); 23127 emit_op(s, OP_drop); 23128 } 23129 23130 /* build a private setter function name from the private getter name */ 23131 static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) 23132 { 23133 return js_atom_concat_str(ctx, name, "<set>"); 23134 } 23135 23136 typedef struct { 23137 JSFunctionDef *fields_init_fd; 23138 int computed_fields_count; 23139 BOOL need_brand; 23140 int brand_push_pos; 23141 BOOL is_static; 23142 } ClassFieldsDef; 23143 23144 static __exception int emit_class_init_start(JSParseState *s, 23145 ClassFieldsDef *cf) 23146 { 23147 int label_add_brand; 23148 23149 cf->fields_init_fd = js_parse_function_class_fields_init(s); 23150 if (!cf->fields_init_fd) 23151 return -1; 23152 23153 s->cur_func = cf->fields_init_fd; 23154 23155 if (!cf->is_static) { 23156 /* add the brand to the newly created instance */ 23157 /* XXX: would be better to add the code only if needed, maybe in a 23158 later pass */ 23159 emit_op(s, OP_push_false); /* will be patched later */ 23160 cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; 23161 label_add_brand = emit_goto(s, OP_if_false, -1); 23162 23163 emit_op(s, OP_scope_get_var); 23164 emit_atom(s, JS_ATOM_this); 23165 emit_u16(s, 0); 23166 23167 emit_op(s, OP_scope_get_var); 23168 emit_atom(s, JS_ATOM_home_object); 23169 emit_u16(s, 0); 23170 23171 emit_op(s, OP_add_brand); 23172 23173 emit_label(s, label_add_brand); 23174 } 23175 s->cur_func = s->cur_func->parent; 23176 return 0; 23177 } 23178 23179 static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) 23180 { 23181 int cpool_idx; 23182 23183 s->cur_func = cf->fields_init_fd; 23184 emit_op(s, OP_return_undef); 23185 s->cur_func = s->cur_func->parent; 23186 23187 cpool_idx = cpool_add(s, JS_NULL); 23188 cf->fields_init_fd->parent_cpool_idx = cpool_idx; 23189 emit_op(s, OP_fclosure); 23190 emit_u32(s, cpool_idx); 23191 emit_op(s, OP_set_home_object); 23192 } 23193 23194 23195 static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, 23196 JSParseExportEnum export_flag) 23197 { 23198 JSContext *ctx = s->ctx; 23199 JSFunctionDef *fd = s->cur_func; 23200 JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1; 23201 JSAtom class_var_name = JS_ATOM_NULL; 23202 JSFunctionDef *method_fd, *ctor_fd; 23203 int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset; 23204 int class_flags = 0, i, define_class_offset; 23205 BOOL is_static, is_private; 23206 const uint8_t *class_start_ptr = s->token.ptr; 23207 const uint8_t *start_ptr; 23208 ClassFieldsDef class_fields[2]; 23209 23210 /* classes are parsed and executed in strict mode */ 23211 saved_js_mode = fd->js_mode; 23212 fd->js_mode |= JS_MODE_STRICT; 23213 if (next_token(s)) 23214 goto fail; 23215 if (s->token.val == TOK_IDENT) { 23216 if (s->token.u.ident.is_reserved) { 23217 js_parse_error_reserved_identifier(s); 23218 goto fail; 23219 } 23220 class_name = JS_DupAtom(ctx, s->token.u.ident.atom); 23221 if (next_token(s)) 23222 goto fail; 23223 } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) { 23224 js_parse_error(s, "class statement requires a name"); 23225 goto fail; 23226 } 23227 if (!is_class_expr) { 23228 if (class_name == JS_ATOM_NULL) 23229 class_var_name = JS_ATOM__default_; /* export default */ 23230 else 23231 class_var_name = class_name; 23232 class_var_name = JS_DupAtom(ctx, class_var_name); 23233 } 23234 23235 push_scope(s); 23236 23237 if (s->token.val == TOK_EXTENDS) { 23238 class_flags = JS_DEFINE_CLASS_HAS_HERITAGE; 23239 if (next_token(s)) 23240 goto fail; 23241 if (js_parse_left_hand_side_expr(s)) 23242 goto fail; 23243 } else { 23244 emit_op(s, OP_undefined); 23245 } 23246 23247 /* add a 'const' definition for the class name */ 23248 if (class_name != JS_ATOM_NULL) { 23249 class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST); 23250 if (class_name_var_idx < 0) 23251 goto fail; 23252 } 23253 23254 if (js_parse_expect(s, '{')) 23255 goto fail; 23256 23257 /* this scope contains the private fields */ 23258 push_scope(s); 23259 23260 emit_op(s, OP_push_const); 23261 ctor_cpool_offset = fd->byte_code.size; 23262 emit_u32(s, 0); /* will be patched at the end of the class parsing */ 23263 23264 if (class_name == JS_ATOM_NULL) { 23265 if (class_var_name != JS_ATOM_NULL) 23266 class_name1 = JS_ATOM_default; 23267 else 23268 class_name1 = JS_ATOM_empty_string; 23269 } else { 23270 class_name1 = class_name; 23271 } 23272 23273 emit_op(s, OP_define_class); 23274 emit_atom(s, class_name1); 23275 emit_u8(s, class_flags); 23276 define_class_offset = fd->last_opcode_pos; 23277 23278 for(i = 0; i < 2; i++) { 23279 ClassFieldsDef *cf = &class_fields[i]; 23280 cf->fields_init_fd = NULL; 23281 cf->computed_fields_count = 0; 23282 cf->need_brand = FALSE; 23283 cf->is_static = i; 23284 } 23285 23286 ctor_fd = NULL; 23287 while (s->token.val != '}') { 23288 if (s->token.val == ';') { 23289 if (next_token(s)) 23290 goto fail; 23291 continue; 23292 } 23293 is_static = FALSE; 23294 if (s->token.val == TOK_STATIC) { 23295 int next = peek_token(s, TRUE); 23296 if (!(next == ';' || next == '}' || next == '(' || next == '=')) 23297 is_static = TRUE; 23298 } 23299 prop_type = -1; 23300 if (is_static) { 23301 if (next_token(s)) 23302 goto fail; 23303 if (s->token.val == '{') { 23304 ClassFieldsDef *cf = &class_fields[is_static]; 23305 JSFunctionDef *init; 23306 if (!cf->fields_init_fd) { 23307 if (emit_class_init_start(s, cf)) 23308 goto fail; 23309 } 23310 s->cur_func = cf->fields_init_fd; 23311 /* XXX: could try to avoid creating a new function and 23312 reuse 'fields_init_fd' with a specific 'var' 23313 scope */ 23314 // stack is now: <empty> 23315 if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, 23316 JS_FUNC_NORMAL, JS_ATOM_NULL, 23317 s->token.ptr, s->token.line_num, 23318 JS_PARSE_EXPORT_NONE, &init) < 0) { 23319 goto fail; 23320 } 23321 // stack is now: fclosure 23322 push_scope(s); 23323 emit_op(s, OP_scope_get_var); 23324 emit_atom(s, JS_ATOM_this); 23325 emit_u16(s, 0); 23326 // stack is now: fclosure this 23327 emit_op(s, OP_swap); 23328 // stack is now: this fclosure 23329 emit_op(s, OP_call_method); 23330 emit_u16(s, 0); 23331 // stack is now: returnvalue 23332 emit_op(s, OP_drop); 23333 // stack is now: <empty> 23334 pop_scope(s); 23335 s->cur_func = s->cur_func->parent; 23336 continue; 23337 } 23338 /* allow "static" field name */ 23339 if (s->token.val == ';' || s->token.val == '=') { 23340 is_static = FALSE; 23341 name = JS_DupAtom(ctx, JS_ATOM_static); 23342 prop_type = PROP_TYPE_IDENT; 23343 } 23344 } 23345 if (is_static) 23346 emit_op(s, OP_swap); 23347 start_ptr = s->token.ptr; 23348 if (prop_type < 0) { 23349 prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE); 23350 if (prop_type < 0) 23351 goto fail; 23352 } 23353 is_private = prop_type & PROP_TYPE_PRIVATE; 23354 prop_type &= ~PROP_TYPE_PRIVATE; 23355 23356 if ((name == JS_ATOM_constructor && !is_static && 23357 prop_type != PROP_TYPE_IDENT) || 23358 (name == JS_ATOM_prototype && is_static) || 23359 name == JS_ATOM_hash_constructor) { 23360 js_parse_error(s, "invalid method name"); 23361 goto fail; 23362 } 23363 if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) { 23364 BOOL is_set = prop_type - PROP_TYPE_GET; 23365 JSFunctionDef *method_fd; 23366 23367 if (is_private) { 23368 int idx, var_kind, is_static1; 23369 idx = find_private_class_field(ctx, fd, name, fd->scope_level); 23370 if (idx >= 0) { 23371 var_kind = fd->vars[idx].var_kind; 23372 is_static1 = fd->vars[idx].is_static_private; 23373 if (var_kind == JS_VAR_PRIVATE_FIELD || 23374 var_kind == JS_VAR_PRIVATE_METHOD || 23375 var_kind == JS_VAR_PRIVATE_GETTER_SETTER || 23376 var_kind == (JS_VAR_PRIVATE_GETTER + is_set) || 23377 (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) && 23378 is_static != is_static1)) { 23379 goto private_field_already_defined; 23380 } 23381 fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; 23382 } else { 23383 if (add_private_class_field(s, fd, name, 23384 JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0) 23385 goto fail; 23386 } 23387 class_fields[is_static].need_brand = TRUE; 23388 } 23389 23390 if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, 23391 JS_FUNC_NORMAL, JS_ATOM_NULL, 23392 start_ptr, s->token.line_num, 23393 JS_PARSE_EXPORT_NONE, &method_fd)) 23394 goto fail; 23395 if (is_private) { 23396 method_fd->need_home_object = TRUE; /* needed for brand check */ 23397 emit_op(s, OP_set_home_object); 23398 /* XXX: missing function name */ 23399 emit_op(s, OP_scope_put_var_init); 23400 if (is_set) { 23401 JSAtom setter_name; 23402 int ret; 23403 23404 setter_name = get_private_setter_name(ctx, name); 23405 if (setter_name == JS_ATOM_NULL) 23406 goto fail; 23407 emit_atom(s, setter_name); 23408 ret = add_private_class_field(s, fd, setter_name, 23409 JS_VAR_PRIVATE_SETTER, is_static); 23410 JS_FreeAtom(ctx, setter_name); 23411 if (ret < 0) 23412 goto fail; 23413 } else { 23414 emit_atom(s, name); 23415 } 23416 emit_u16(s, s->cur_func->scope_level); 23417 } else { 23418 if (name == JS_ATOM_NULL) { 23419 emit_op(s, OP_define_method_computed); 23420 } else { 23421 emit_op(s, OP_define_method); 23422 emit_atom(s, name); 23423 } 23424 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set); 23425 } 23426 } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') { 23427 ClassFieldsDef *cf = &class_fields[is_static]; 23428 JSAtom field_var_name = JS_ATOM_NULL; 23429 23430 /* class field */ 23431 23432 /* XXX: spec: not consistent with method name checks */ 23433 if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) { 23434 js_parse_error(s, "invalid field name"); 23435 goto fail; 23436 } 23437 23438 if (is_private) { 23439 if (find_private_class_field(ctx, fd, name, 23440 fd->scope_level) >= 0) { 23441 goto private_field_already_defined; 23442 } 23443 if (add_private_class_field(s, fd, name, 23444 JS_VAR_PRIVATE_FIELD, is_static) < 0) 23445 goto fail; 23446 emit_op(s, OP_private_symbol); 23447 emit_atom(s, name); 23448 emit_op(s, OP_scope_put_var_init); 23449 emit_atom(s, name); 23450 emit_u16(s, s->cur_func->scope_level); 23451 } 23452 23453 if (!cf->fields_init_fd) { 23454 if (emit_class_init_start(s, cf)) 23455 goto fail; 23456 } 23457 if (name == JS_ATOM_NULL ) { 23458 /* save the computed field name into a variable */ 23459 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count); 23460 if (field_var_name == JS_ATOM_NULL) 23461 goto fail; 23462 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) { 23463 JS_FreeAtom(ctx, field_var_name); 23464 goto fail; 23465 } 23466 emit_op(s, OP_to_propkey); 23467 emit_op(s, OP_scope_put_var_init); 23468 emit_atom(s, field_var_name); 23469 emit_u16(s, s->cur_func->scope_level); 23470 } 23471 s->cur_func = cf->fields_init_fd; 23472 emit_op(s, OP_scope_get_var); 23473 emit_atom(s, JS_ATOM_this); 23474 emit_u16(s, 0); 23475 23476 if (name == JS_ATOM_NULL) { 23477 emit_op(s, OP_scope_get_var); 23478 emit_atom(s, field_var_name); 23479 emit_u16(s, s->cur_func->scope_level); 23480 cf->computed_fields_count++; 23481 JS_FreeAtom(ctx, field_var_name); 23482 } else if (is_private) { 23483 emit_op(s, OP_scope_get_var); 23484 emit_atom(s, name); 23485 emit_u16(s, s->cur_func->scope_level); 23486 } 23487 23488 if (s->token.val == '=') { 23489 if (next_token(s)) 23490 goto fail; 23491 if (js_parse_assign_expr(s)) 23492 goto fail; 23493 } else { 23494 emit_op(s, OP_undefined); 23495 } 23496 if (is_private) { 23497 set_object_name_computed(s); 23498 emit_op(s, OP_define_private_field); 23499 } else if (name == JS_ATOM_NULL) { 23500 set_object_name_computed(s); 23501 emit_op(s, OP_define_array_el); 23502 emit_op(s, OP_drop); 23503 } else { 23504 set_object_name(s, name); 23505 emit_op(s, OP_define_field); 23506 emit_atom(s, name); 23507 } 23508 s->cur_func = s->cur_func->parent; 23509 if (js_parse_expect_semi(s)) 23510 goto fail; 23511 } else { 23512 JSParseFunctionEnum func_type; 23513 JSFunctionKindEnum func_kind; 23514 23515 func_type = JS_PARSE_FUNC_METHOD; 23516 func_kind = JS_FUNC_NORMAL; 23517 if (prop_type == PROP_TYPE_STAR) { 23518 func_kind = JS_FUNC_GENERATOR; 23519 } else if (prop_type == PROP_TYPE_ASYNC) { 23520 func_kind = JS_FUNC_ASYNC; 23521 } else if (prop_type == PROP_TYPE_ASYNC_STAR) { 23522 func_kind = JS_FUNC_ASYNC_GENERATOR; 23523 } else if (name == JS_ATOM_constructor && !is_static) { 23524 if (ctor_fd) { 23525 js_parse_error(s, "property constructor appears more than once"); 23526 goto fail; 23527 } 23528 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) 23529 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR; 23530 else 23531 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; 23532 } 23533 if (is_private) { 23534 class_fields[is_static].need_brand = TRUE; 23535 } 23536 if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd)) 23537 goto fail; 23538 if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR || 23539 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { 23540 ctor_fd = method_fd; 23541 } else if (is_private) { 23542 method_fd->need_home_object = TRUE; /* needed for brand check */ 23543 if (find_private_class_field(ctx, fd, name, 23544 fd->scope_level) >= 0) { 23545 private_field_already_defined: 23546 js_parse_error(s, "private class field is already defined"); 23547 goto fail; 23548 } 23549 if (add_private_class_field(s, fd, name, 23550 JS_VAR_PRIVATE_METHOD, is_static) < 0) 23551 goto fail; 23552 emit_op(s, OP_set_home_object); 23553 emit_op(s, OP_set_name); 23554 emit_atom(s, name); 23555 emit_op(s, OP_scope_put_var_init); 23556 emit_atom(s, name); 23557 emit_u16(s, s->cur_func->scope_level); 23558 } else { 23559 if (name == JS_ATOM_NULL) { 23560 emit_op(s, OP_define_method_computed); 23561 } else { 23562 emit_op(s, OP_define_method); 23563 emit_atom(s, name); 23564 } 23565 emit_u8(s, OP_DEFINE_METHOD_METHOD); 23566 } 23567 } 23568 if (is_static) 23569 emit_op(s, OP_swap); 23570 JS_FreeAtom(ctx, name); 23571 name = JS_ATOM_NULL; 23572 } 23573 23574 if (s->token.val != '}') { 23575 js_parse_error(s, "expecting '%c'", '}'); 23576 goto fail; 23577 } 23578 23579 if (!ctor_fd) { 23580 if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd)) 23581 goto fail; 23582 } 23583 /* patch the constant pool index for the constructor */ 23584 put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx); 23585 23586 /* store the class source code in the constructor. */ 23587 if (!(fd->js_mode & JS_MODE_STRIP)) { 23588 js_free(ctx, ctor_fd->source); 23589 ctor_fd->source_len = s->buf_ptr - class_start_ptr; 23590 ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr, 23591 ctor_fd->source_len); 23592 if (!ctor_fd->source) 23593 goto fail; 23594 } 23595 23596 /* consume the '}' */ 23597 if (next_token(s)) 23598 goto fail; 23599 23600 { 23601 ClassFieldsDef *cf = &class_fields[0]; 23602 int var_idx; 23603 23604 if (cf->need_brand) { 23605 /* add a private brand to the prototype */ 23606 emit_op(s, OP_dup); 23607 emit_op(s, OP_null); 23608 emit_op(s, OP_swap); 23609 emit_op(s, OP_add_brand); 23610 23611 /* define the brand field in 'this' of the initializer */ 23612 if (!cf->fields_init_fd) { 23613 if (emit_class_init_start(s, cf)) 23614 goto fail; 23615 } 23616 /* patch the start of the function to enable the 23617 OP_add_brand_instance code */ 23618 cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; 23619 } 23620 23621 /* store the function to initialize the fields to that it can be 23622 referenced by the constructor */ 23623 var_idx = define_var(s, fd, JS_ATOM_class_fields_init, 23624 JS_VAR_DEF_CONST); 23625 if (var_idx < 0) 23626 goto fail; 23627 if (cf->fields_init_fd) { 23628 emit_class_init_end(s, cf); 23629 } else { 23630 emit_op(s, OP_undefined); 23631 } 23632 emit_op(s, OP_scope_put_var_init); 23633 emit_atom(s, JS_ATOM_class_fields_init); 23634 emit_u16(s, s->cur_func->scope_level); 23635 } 23636 23637 /* drop the prototype */ 23638 emit_op(s, OP_drop); 23639 23640 if (class_fields[1].need_brand) { 23641 /* add a private brand to the class */ 23642 emit_op(s, OP_dup); 23643 emit_op(s, OP_dup); 23644 emit_op(s, OP_add_brand); 23645 } 23646 23647 if (class_name != JS_ATOM_NULL) { 23648 /* store the class name in the scoped class name variable (it 23649 is independent from the class statement variable 23650 definition) */ 23651 emit_op(s, OP_dup); 23652 emit_op(s, OP_scope_put_var_init); 23653 emit_atom(s, class_name); 23654 emit_u16(s, fd->scope_level); 23655 } 23656 23657 /* initialize the static fields */ 23658 if (class_fields[1].fields_init_fd != NULL) { 23659 ClassFieldsDef *cf = &class_fields[1]; 23660 emit_op(s, OP_dup); 23661 emit_class_init_end(s, cf); 23662 emit_op(s, OP_call_method); 23663 emit_u16(s, 0); 23664 emit_op(s, OP_drop); 23665 } 23666 23667 pop_scope(s); 23668 pop_scope(s); 23669 23670 /* the class statements have a block level scope */ 23671 if (class_var_name != JS_ATOM_NULL) { 23672 if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0) 23673 goto fail; 23674 emit_op(s, OP_scope_put_var_init); 23675 emit_atom(s, class_var_name); 23676 emit_u16(s, fd->scope_level); 23677 } else { 23678 if (class_name == JS_ATOM_NULL) { 23679 /* cannot use OP_set_name because the name of the class 23680 must be defined before the static initializers are 23681 executed */ 23682 emit_op(s, OP_set_class_name); 23683 emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset); 23684 } 23685 } 23686 23687 if (export_flag != JS_PARSE_EXPORT_NONE) { 23688 if (!add_export_entry(s, fd->module, 23689 class_var_name, 23690 export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default, 23691 JS_EXPORT_TYPE_LOCAL)) 23692 goto fail; 23693 } 23694 23695 JS_FreeAtom(ctx, class_name); 23696 JS_FreeAtom(ctx, class_var_name); 23697 fd->js_mode = saved_js_mode; 23698 return 0; 23699 fail: 23700 JS_FreeAtom(ctx, name); 23701 JS_FreeAtom(ctx, class_name); 23702 JS_FreeAtom(ctx, class_var_name); 23703 fd->js_mode = saved_js_mode; 23704 return -1; 23705 } 23706 23707 static __exception int js_parse_array_literal(JSParseState *s) 23708 { 23709 uint32_t idx; 23710 BOOL need_length; 23711 23712 if (next_token(s)) 23713 return -1; 23714 /* small regular arrays are created on the stack */ 23715 idx = 0; 23716 while (s->token.val != ']' && idx < 32) { 23717 if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS) 23718 break; 23719 if (js_parse_assign_expr(s)) 23720 return -1; 23721 idx++; 23722 /* accept trailing comma */ 23723 if (s->token.val == ',') { 23724 if (next_token(s)) 23725 return -1; 23726 } else 23727 if (s->token.val != ']') 23728 goto done; 23729 } 23730 emit_op(s, OP_array_from); 23731 emit_u16(s, idx); 23732 23733 /* larger arrays and holes are handled with explicit indices */ 23734 need_length = FALSE; 23735 while (s->token.val != ']' && idx < 0x7fffffff) { 23736 if (s->token.val == TOK_ELLIPSIS) 23737 break; 23738 need_length = TRUE; 23739 if (s->token.val != ',') { 23740 if (js_parse_assign_expr(s)) 23741 return -1; 23742 emit_op(s, OP_define_field); 23743 emit_u32(s, __JS_AtomFromUInt32(idx)); 23744 need_length = FALSE; 23745 } 23746 idx++; 23747 /* accept trailing comma */ 23748 if (s->token.val == ',') { 23749 if (next_token(s)) 23750 return -1; 23751 } 23752 } 23753 if (s->token.val == ']') { 23754 if (need_length) { 23755 /* Set the length: Cannot use OP_define_field because 23756 length is not configurable */ 23757 emit_op(s, OP_dup); 23758 emit_op(s, OP_push_i32); 23759 emit_u32(s, idx); 23760 emit_op(s, OP_put_field); 23761 emit_atom(s, JS_ATOM_length); 23762 } 23763 goto done; 23764 } 23765 23766 /* huge arrays and spread elements require a dynamic index on the stack */ 23767 emit_op(s, OP_push_i32); 23768 emit_u32(s, idx); 23769 23770 /* stack has array, index */ 23771 while (s->token.val != ']') { 23772 if (s->token.val == TOK_ELLIPSIS) { 23773 if (next_token(s)) 23774 return -1; 23775 if (js_parse_assign_expr(s)) 23776 return -1; 23777 #if 1 23778 emit_op(s, OP_append); 23779 #else 23780 int label_next, label_done; 23781 label_next = new_label(s); 23782 label_done = new_label(s); 23783 /* enumerate object */ 23784 emit_op(s, OP_for_of_start); 23785 emit_op(s, OP_rot5l); 23786 emit_op(s, OP_rot5l); 23787 emit_label(s, label_next); 23788 /* on stack: enum_rec array idx */ 23789 emit_op(s, OP_for_of_next); 23790 emit_u8(s, 2); 23791 emit_goto(s, OP_if_true, label_done); 23792 /* append element */ 23793 /* enum_rec array idx val -> enum_rec array new_idx */ 23794 emit_op(s, OP_define_array_el); 23795 emit_op(s, OP_inc); 23796 emit_goto(s, OP_goto, label_next); 23797 emit_label(s, label_done); 23798 /* close enumeration */ 23799 emit_op(s, OP_drop); /* drop undef val */ 23800 emit_op(s, OP_nip1); /* drop enum_rec */ 23801 emit_op(s, OP_nip1); 23802 emit_op(s, OP_nip1); 23803 #endif 23804 } else { 23805 need_length = TRUE; 23806 if (s->token.val != ',') { 23807 if (js_parse_assign_expr(s)) 23808 return -1; 23809 /* a idx val */ 23810 emit_op(s, OP_define_array_el); 23811 need_length = FALSE; 23812 } 23813 emit_op(s, OP_inc); 23814 } 23815 if (s->token.val != ',') 23816 break; 23817 if (next_token(s)) 23818 return -1; 23819 } 23820 if (need_length) { 23821 /* Set the length: cannot use OP_define_field because 23822 length is not configurable */ 23823 emit_op(s, OP_dup1); /* array length - array array length */ 23824 emit_op(s, OP_put_field); 23825 emit_atom(s, JS_ATOM_length); 23826 } else { 23827 emit_op(s, OP_drop); /* array length - array */ 23828 } 23829 done: 23830 return js_parse_expect(s, ']'); 23831 } 23832 23833 /* XXX: remove */ 23834 static BOOL has_with_scope(JSFunctionDef *s, int scope_level) 23835 { 23836 /* check if scope chain contains a with statement */ 23837 while (s) { 23838 int scope_idx = s->scopes[scope_level].first; 23839 while (scope_idx >= 0) { 23840 JSVarDef *vd = &s->vars[scope_idx]; 23841 23842 if (vd->var_name == JS_ATOM__with_) 23843 return TRUE; 23844 scope_idx = vd->scope_next; 23845 } 23846 /* check parent scopes */ 23847 scope_level = s->parent_scope_level; 23848 s = s->parent; 23849 } 23850 return FALSE; 23851 } 23852 23853 static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, 23854 JSAtom *pname, int *plabel, int *pdepth, BOOL keep, 23855 int tok) 23856 { 23857 JSFunctionDef *fd; 23858 int opcode, scope, label, depth; 23859 JSAtom name; 23860 23861 /* we check the last opcode to get the lvalue type */ 23862 fd = s->cur_func; 23863 scope = 0; 23864 name = JS_ATOM_NULL; 23865 label = -1; 23866 depth = 0; 23867 switch(opcode = get_prev_opcode(fd)) { 23868 case OP_scope_get_var: 23869 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 23870 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 23871 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) && 23872 (fd->js_mode & JS_MODE_STRICT)) { 23873 return js_parse_error(s, "invalid lvalue in strict mode"); 23874 } 23875 if (name == JS_ATOM_this || name == JS_ATOM_new_target) 23876 goto invalid_lvalue; 23877 depth = 2; /* will generate OP_get_ref_value */ 23878 break; 23879 case OP_get_field: 23880 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 23881 depth = 1; 23882 break; 23883 case OP_scope_get_private_field: 23884 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 23885 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 23886 depth = 1; 23887 break; 23888 case OP_get_array_el: 23889 depth = 2; 23890 break; 23891 case OP_get_super_value: 23892 depth = 3; 23893 break; 23894 default: 23895 invalid_lvalue: 23896 if (tok == TOK_FOR) { 23897 return js_parse_error(s, "invalid for in/of left hand-side"); 23898 } else if (tok == TOK_INC || tok == TOK_DEC) { 23899 return js_parse_error(s, "invalid increment/decrement operand"); 23900 } else if (tok == '[' || tok == '{') { 23901 return js_parse_error(s, "invalid destructuring target"); 23902 } else { 23903 return js_parse_error(s, "invalid assignment left-hand side"); 23904 } 23905 } 23906 /* remove the last opcode */ 23907 fd->byte_code.size = fd->last_opcode_pos; 23908 fd->last_opcode_pos = -1; 23909 23910 if (keep) { 23911 /* get the value but keep the object/fields on the stack */ 23912 switch(opcode) { 23913 case OP_scope_get_var: 23914 label = new_label(s); 23915 emit_op(s, OP_scope_make_ref); 23916 emit_atom(s, name); 23917 emit_u32(s, label); 23918 emit_u16(s, scope); 23919 update_label(fd, label, 1); 23920 emit_op(s, OP_get_ref_value); 23921 opcode = OP_get_ref_value; 23922 break; 23923 case OP_get_field: 23924 emit_op(s, OP_get_field2); 23925 emit_atom(s, name); 23926 break; 23927 case OP_scope_get_private_field: 23928 emit_op(s, OP_scope_get_private_field2); 23929 emit_atom(s, name); 23930 emit_u16(s, scope); 23931 break; 23932 case OP_get_array_el: 23933 /* XXX: replace by a single opcode ? */ 23934 emit_op(s, OP_to_propkey2); 23935 emit_op(s, OP_dup2); 23936 emit_op(s, OP_get_array_el); 23937 break; 23938 case OP_get_super_value: 23939 emit_op(s, OP_to_propkey); 23940 emit_op(s, OP_dup3); 23941 emit_op(s, OP_get_super_value); 23942 break; 23943 default: 23944 abort(); 23945 } 23946 } else { 23947 switch(opcode) { 23948 case OP_scope_get_var: 23949 label = new_label(s); 23950 emit_op(s, OP_scope_make_ref); 23951 emit_atom(s, name); 23952 emit_u32(s, label); 23953 emit_u16(s, scope); 23954 update_label(fd, label, 1); 23955 opcode = OP_get_ref_value; 23956 break; 23957 case OP_get_array_el: 23958 emit_op(s, OP_to_propkey2); 23959 break; 23960 case OP_get_super_value: 23961 emit_op(s, OP_to_propkey); 23962 break; 23963 } 23964 } 23965 23966 *popcode = opcode; 23967 *pscope = scope; 23968 /* name has refcount for OP_get_field and OP_get_ref_value, 23969 and JS_ATOM_NULL for other opcodes */ 23970 *pname = name; 23971 *plabel = label; 23972 if (pdepth) 23973 *pdepth = depth; 23974 return 0; 23975 } 23976 23977 typedef enum { 23978 PUT_LVALUE_NOKEEP, /* [depth] v -> */ 23979 PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently 23980 just disable optimizations) */ 23981 PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */ 23982 PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */ 23983 PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */ 23984 } PutLValueEnum; 23985 23986 /* name has a live reference. 'is_let' is only used with opcode = 23987 OP_scope_get_var which is never generated by get_lvalue(). */ 23988 static void put_lvalue(JSParseState *s, int opcode, int scope, 23989 JSAtom name, int label, PutLValueEnum special, 23990 BOOL is_let) 23991 { 23992 switch(opcode) { 23993 case OP_get_field: 23994 case OP_scope_get_private_field: 23995 /* depth = 1 */ 23996 switch(special) { 23997 case PUT_LVALUE_NOKEEP: 23998 case PUT_LVALUE_NOKEEP_DEPTH: 23999 break; 24000 case PUT_LVALUE_KEEP_TOP: 24001 emit_op(s, OP_insert2); /* obj v -> v obj v */ 24002 break; 24003 case PUT_LVALUE_KEEP_SECOND: 24004 emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */ 24005 break; 24006 case PUT_LVALUE_NOKEEP_BOTTOM: 24007 emit_op(s, OP_swap); 24008 break; 24009 default: 24010 abort(); 24011 } 24012 break; 24013 case OP_get_array_el: 24014 case OP_get_ref_value: 24015 /* depth = 2 */ 24016 if (opcode == OP_get_ref_value) { 24017 JS_FreeAtom(s->ctx, name); 24018 emit_label(s, label); 24019 } 24020 switch(special) { 24021 case PUT_LVALUE_NOKEEP: 24022 emit_op(s, OP_nop); /* will trigger optimization */ 24023 break; 24024 case PUT_LVALUE_NOKEEP_DEPTH: 24025 break; 24026 case PUT_LVALUE_KEEP_TOP: 24027 emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */ 24028 break; 24029 case PUT_LVALUE_KEEP_SECOND: 24030 emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */ 24031 break; 24032 case PUT_LVALUE_NOKEEP_BOTTOM: 24033 emit_op(s, OP_rot3l); 24034 break; 24035 default: 24036 abort(); 24037 } 24038 break; 24039 case OP_get_super_value: 24040 /* depth = 3 */ 24041 switch(special) { 24042 case PUT_LVALUE_NOKEEP: 24043 case PUT_LVALUE_NOKEEP_DEPTH: 24044 break; 24045 case PUT_LVALUE_KEEP_TOP: 24046 emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */ 24047 break; 24048 case PUT_LVALUE_KEEP_SECOND: 24049 emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */ 24050 break; 24051 case PUT_LVALUE_NOKEEP_BOTTOM: 24052 emit_op(s, OP_rot4l); 24053 break; 24054 default: 24055 abort(); 24056 } 24057 break; 24058 default: 24059 break; 24060 } 24061 24062 switch(opcode) { 24063 case OP_scope_get_var: /* val -- */ 24064 assert(special == PUT_LVALUE_NOKEEP || 24065 special == PUT_LVALUE_NOKEEP_DEPTH); 24066 emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var); 24067 emit_u32(s, name); /* has refcount */ 24068 emit_u16(s, scope); 24069 break; 24070 case OP_get_field: 24071 emit_op(s, OP_put_field); 24072 emit_u32(s, name); /* name has refcount */ 24073 break; 24074 case OP_scope_get_private_field: 24075 emit_op(s, OP_scope_put_private_field); 24076 emit_u32(s, name); /* name has refcount */ 24077 emit_u16(s, scope); 24078 break; 24079 case OP_get_array_el: 24080 emit_op(s, OP_put_array_el); 24081 break; 24082 case OP_get_ref_value: 24083 emit_op(s, OP_put_ref_value); 24084 break; 24085 case OP_get_super_value: 24086 emit_op(s, OP_put_super_value); 24087 break; 24088 default: 24089 abort(); 24090 } 24091 } 24092 24093 static __exception int js_parse_expr_paren(JSParseState *s) 24094 { 24095 if (js_parse_expect(s, '(')) 24096 return -1; 24097 if (js_parse_expr(s)) 24098 return -1; 24099 if (js_parse_expect(s, ')')) 24100 return -1; 24101 return 0; 24102 } 24103 24104 static int js_unsupported_keyword(JSParseState *s, JSAtom atom) 24105 { 24106 char buf[ATOM_GET_STR_BUF_SIZE]; 24107 return js_parse_error(s, "unsupported keyword: %s", 24108 JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); 24109 } 24110 24111 static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) 24112 { 24113 JSFunctionDef *fd = s->cur_func; 24114 JSVarDefEnum var_def_type; 24115 24116 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { 24117 return js_parse_error(s, "yield is a reserved identifier"); 24118 } 24119 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) 24120 && (fd->js_mode & JS_MODE_STRICT)) { 24121 return js_parse_error(s, "invalid variable name in strict mode"); 24122 } 24123 if ((name == JS_ATOM_let || name == JS_ATOM_undefined) 24124 && (tok == TOK_LET || tok == TOK_CONST)) { 24125 return js_parse_error(s, "invalid lexical variable name"); 24126 } 24127 switch(tok) { 24128 case TOK_LET: 24129 var_def_type = JS_VAR_DEF_LET; 24130 break; 24131 case TOK_CONST: 24132 var_def_type = JS_VAR_DEF_CONST; 24133 break; 24134 case TOK_VAR: 24135 var_def_type = JS_VAR_DEF_VAR; 24136 break; 24137 case TOK_CATCH: 24138 var_def_type = JS_VAR_DEF_CATCH; 24139 break; 24140 default: 24141 abort(); 24142 } 24143 if (define_var(s, fd, name, var_def_type) < 0) 24144 return -1; 24145 return 0; 24146 } 24147 24148 static void js_emit_spread_code(JSParseState *s, int depth) 24149 { 24150 int label_rest_next, label_rest_done; 24151 24152 /* XXX: could check if enum object is an actual array and optimize 24153 slice extraction. enumeration record and target array are in a 24154 different order from OP_append case. */ 24155 /* enum_rec xxx -- enum_rec xxx array 0 */ 24156 emit_op(s, OP_array_from); 24157 emit_u16(s, 0); 24158 emit_op(s, OP_push_i32); 24159 emit_u32(s, 0); 24160 emit_label(s, label_rest_next = new_label(s)); 24161 emit_op(s, OP_for_of_next); 24162 emit_u8(s, 2 + depth); 24163 label_rest_done = emit_goto(s, OP_if_true, -1); 24164 /* array idx val -- array idx */ 24165 emit_op(s, OP_define_array_el); 24166 emit_op(s, OP_inc); 24167 emit_goto(s, OP_goto, label_rest_next); 24168 emit_label(s, label_rest_done); 24169 /* enum_rec xxx array idx undef -- enum_rec xxx array */ 24170 emit_op(s, OP_drop); 24171 emit_op(s, OP_drop); 24172 } 24173 24174 static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name) 24175 { 24176 /* Check for duplicate parameter names */ 24177 JSFunctionDef *fd = s->cur_func; 24178 int i; 24179 for (i = 0; i < fd->arg_count; i++) { 24180 if (fd->args[i].var_name == name) 24181 goto duplicate; 24182 } 24183 for (i = 0; i < fd->var_count; i++) { 24184 if (fd->vars[i].var_name == name) 24185 goto duplicate; 24186 } 24187 return 0; 24188 24189 duplicate: 24190 return js_parse_error(s, "duplicate parameter names not allowed in this context"); 24191 } 24192 24193 static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg) 24194 { 24195 JSAtom name; 24196 24197 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) 24198 || ((s->cur_func->js_mode & JS_MODE_STRICT) && 24199 (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) { 24200 js_parse_error(s, "invalid destructuring target"); 24201 return JS_ATOM_NULL; 24202 } 24203 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24204 if (is_arg && js_parse_check_duplicate_parameter(s, name)) 24205 goto fail; 24206 if (next_token(s)) 24207 goto fail; 24208 24209 return name; 24210 fail: 24211 JS_FreeAtom(s->ctx, name); 24212 return JS_ATOM_NULL; 24213 } 24214 24215 /* Return -1 if error, 0 if no initializer, 1 if an initializer is 24216 present at the top level. */ 24217 static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg, 24218 int hasval, int has_ellipsis, 24219 BOOL allow_initializer) 24220 { 24221 int label_parse, label_assign, label_done, label_lvalue, depth_lvalue; 24222 int start_addr, assign_addr; 24223 JSAtom prop_name, var_name; 24224 int opcode, scope, tok1, skip_bits; 24225 BOOL has_initializer; 24226 24227 if (has_ellipsis < 0) { 24228 /* pre-parse destructuration target for spread detection */ 24229 js_parse_skip_parens_token(s, &skip_bits, FALSE); 24230 has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS; 24231 } 24232 24233 label_parse = new_label(s); 24234 label_assign = new_label(s); 24235 24236 start_addr = s->cur_func->byte_code.size; 24237 if (hasval) { 24238 /* consume value from the stack */ 24239 emit_op(s, OP_dup); 24240 emit_op(s, OP_undefined); 24241 emit_op(s, OP_strict_eq); 24242 emit_goto(s, OP_if_true, label_parse); 24243 emit_label(s, label_assign); 24244 } else { 24245 emit_goto(s, OP_goto, label_parse); 24246 emit_label(s, label_assign); 24247 /* leave value on the stack */ 24248 emit_op(s, OP_dup); 24249 } 24250 assign_addr = s->cur_func->byte_code.size; 24251 if (s->token.val == '{') { 24252 if (next_token(s)) 24253 return -1; 24254 /* throw an exception if the value cannot be converted to an object */ 24255 emit_op(s, OP_to_object); 24256 if (has_ellipsis) { 24257 /* add excludeList on stack just below src object */ 24258 emit_op(s, OP_object); 24259 emit_op(s, OP_swap); 24260 } 24261 while (s->token.val != '}') { 24262 int prop_type; 24263 if (s->token.val == TOK_ELLIPSIS) { 24264 if (!has_ellipsis) { 24265 JS_ThrowInternalError(s->ctx, "unexpected ellipsis token"); 24266 return -1; 24267 } 24268 if (next_token(s)) 24269 return -1; 24270 if (tok) { 24271 var_name = js_parse_destructuring_var(s, tok, is_arg); 24272 if (var_name == JS_ATOM_NULL) 24273 return -1; 24274 opcode = OP_scope_get_var; 24275 scope = s->cur_func->scope_level; 24276 label_lvalue = -1; 24277 depth_lvalue = 0; 24278 } else { 24279 if (js_parse_left_hand_side_expr(s)) 24280 return -1; 24281 24282 if (get_lvalue(s, &opcode, &scope, &var_name, 24283 &label_lvalue, &depth_lvalue, FALSE, '{')) 24284 return -1; 24285 } 24286 if (s->token.val != '}') { 24287 js_parse_error(s, "assignment rest property must be last"); 24288 goto var_error; 24289 } 24290 emit_op(s, OP_object); /* target */ 24291 emit_op(s, OP_copy_data_properties); 24292 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5)); 24293 goto set_val; 24294 } 24295 prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE); 24296 if (prop_type < 0) 24297 return -1; 24298 var_name = JS_ATOM_NULL; 24299 opcode = OP_scope_get_var; 24300 scope = s->cur_func->scope_level; 24301 label_lvalue = -1; 24302 depth_lvalue = 0; 24303 if (prop_type == PROP_TYPE_IDENT) { 24304 if (next_token(s)) 24305 goto prop_error; 24306 if ((s->token.val == '[' || s->token.val == '{') 24307 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || 24308 tok1 == '=' || tok1 == '}')) { 24309 if (prop_name == JS_ATOM_NULL) { 24310 /* computed property name on stack */ 24311 if (has_ellipsis) { 24312 /* define the property in excludeList */ 24313 emit_op(s, OP_to_propkey); /* avoid calling ToString twice */ 24314 emit_op(s, OP_perm3); /* TOS: src excludeList prop */ 24315 emit_op(s, OP_null); /* TOS: src excludeList prop null */ 24316 emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */ 24317 emit_op(s, OP_perm3); /* TOS: excludeList src prop */ 24318 } 24319 /* get the computed property from the source object */ 24320 emit_op(s, OP_get_array_el2); 24321 } else { 24322 /* named property */ 24323 if (has_ellipsis) { 24324 /* define the property in excludeList */ 24325 emit_op(s, OP_swap); /* TOS: src excludeList */ 24326 emit_op(s, OP_null); /* TOS: src excludeList null */ 24327 emit_op(s, OP_define_field); /* TOS: src excludeList */ 24328 emit_atom(s, prop_name); 24329 emit_op(s, OP_swap); /* TOS: excludeList src */ 24330 } 24331 /* get the named property from the source object */ 24332 emit_op(s, OP_get_field2); 24333 emit_u32(s, prop_name); 24334 } 24335 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0) 24336 return -1; 24337 if (s->token.val == '}') 24338 break; 24339 /* accept a trailing comma before the '}' */ 24340 if (js_parse_expect(s, ',')) 24341 return -1; 24342 continue; 24343 } 24344 if (prop_name == JS_ATOM_NULL) { 24345 emit_op(s, OP_to_propkey2); 24346 if (has_ellipsis) { 24347 /* define the property in excludeList */ 24348 emit_op(s, OP_perm3); 24349 emit_op(s, OP_null); 24350 emit_op(s, OP_define_array_el); 24351 emit_op(s, OP_perm3); 24352 } 24353 /* source prop -- source source prop */ 24354 emit_op(s, OP_dup1); 24355 } else { 24356 if (has_ellipsis) { 24357 /* define the property in excludeList */ 24358 emit_op(s, OP_swap); 24359 emit_op(s, OP_null); 24360 emit_op(s, OP_define_field); 24361 emit_atom(s, prop_name); 24362 emit_op(s, OP_swap); 24363 } 24364 /* source -- source source */ 24365 emit_op(s, OP_dup); 24366 } 24367 if (tok) { 24368 var_name = js_parse_destructuring_var(s, tok, is_arg); 24369 if (var_name == JS_ATOM_NULL) 24370 goto prop_error; 24371 } else { 24372 if (js_parse_left_hand_side_expr(s)) 24373 goto prop_error; 24374 lvalue: 24375 if (get_lvalue(s, &opcode, &scope, &var_name, 24376 &label_lvalue, &depth_lvalue, FALSE, '{')) 24377 goto prop_error; 24378 /* swap ref and lvalue object if any */ 24379 if (prop_name == JS_ATOM_NULL) { 24380 switch(depth_lvalue) { 24381 case 1: 24382 /* source prop x -> x source prop */ 24383 emit_op(s, OP_rot3r); 24384 break; 24385 case 2: 24386 /* source prop x y -> x y source prop */ 24387 emit_op(s, OP_swap2); /* t p2 s p1 */ 24388 break; 24389 case 3: 24390 /* source prop x y z -> x y z source prop */ 24391 emit_op(s, OP_rot5l); 24392 emit_op(s, OP_rot5l); 24393 break; 24394 } 24395 } else { 24396 switch(depth_lvalue) { 24397 case 1: 24398 /* source x -> x source */ 24399 emit_op(s, OP_swap); 24400 break; 24401 case 2: 24402 /* source x y -> x y source */ 24403 emit_op(s, OP_rot3l); 24404 break; 24405 case 3: 24406 /* source x y z -> x y z source */ 24407 emit_op(s, OP_rot4l); 24408 break; 24409 } 24410 } 24411 } 24412 if (prop_name == JS_ATOM_NULL) { 24413 /* computed property name on stack */ 24414 /* XXX: should have OP_get_array_el2x with depth */ 24415 /* source prop -- val */ 24416 emit_op(s, OP_get_array_el); 24417 } else { 24418 /* named property */ 24419 /* XXX: should have OP_get_field2x with depth */ 24420 /* source -- val */ 24421 emit_op(s, OP_get_field); 24422 emit_u32(s, prop_name); 24423 } 24424 } else { 24425 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */ 24426 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name)) 24427 goto prop_error; 24428 if ((s->cur_func->js_mode & JS_MODE_STRICT) && 24429 (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) { 24430 js_parse_error(s, "invalid destructuring target"); 24431 goto prop_error; 24432 } 24433 if (has_ellipsis) { 24434 /* define the property in excludeList */ 24435 emit_op(s, OP_swap); 24436 emit_op(s, OP_null); 24437 emit_op(s, OP_define_field); 24438 emit_atom(s, prop_name); 24439 emit_op(s, OP_swap); 24440 } 24441 if (!tok || tok == TOK_VAR) { 24442 /* generate reference */ 24443 /* source -- source source */ 24444 emit_op(s, OP_dup); 24445 emit_op(s, OP_scope_get_var); 24446 emit_atom(s, prop_name); 24447 emit_u16(s, s->cur_func->scope_level); 24448 goto lvalue; 24449 } 24450 var_name = JS_DupAtom(s->ctx, prop_name); 24451 /* source -- source val */ 24452 emit_op(s, OP_get_field2); 24453 emit_u32(s, prop_name); 24454 } 24455 set_val: 24456 if (tok) { 24457 if (js_define_var(s, var_name, tok)) 24458 goto var_error; 24459 scope = s->cur_func->scope_level; 24460 } 24461 if (s->token.val == '=') { /* handle optional default value */ 24462 int label_hasval; 24463 emit_op(s, OP_dup); 24464 emit_op(s, OP_undefined); 24465 emit_op(s, OP_strict_eq); 24466 label_hasval = emit_goto(s, OP_if_false, -1); 24467 if (next_token(s)) 24468 goto var_error; 24469 emit_op(s, OP_drop); 24470 if (js_parse_assign_expr(s)) 24471 goto var_error; 24472 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) 24473 set_object_name(s, var_name); 24474 emit_label(s, label_hasval); 24475 } 24476 /* store value into lvalue object */ 24477 put_lvalue(s, opcode, scope, var_name, label_lvalue, 24478 PUT_LVALUE_NOKEEP_DEPTH, 24479 (tok == TOK_CONST || tok == TOK_LET)); 24480 if (s->token.val == '}') 24481 break; 24482 /* accept a trailing comma before the '}' */ 24483 if (js_parse_expect(s, ',')) 24484 return -1; 24485 } 24486 /* drop the source object */ 24487 emit_op(s, OP_drop); 24488 if (has_ellipsis) { 24489 emit_op(s, OP_drop); /* pop excludeList */ 24490 } 24491 if (next_token(s)) 24492 return -1; 24493 } else if (s->token.val == '[') { 24494 BOOL has_spread; 24495 int enum_depth; 24496 BlockEnv block_env; 24497 24498 if (next_token(s)) 24499 return -1; 24500 /* the block environment is only needed in generators in case 24501 'yield' triggers a 'return' */ 24502 push_break_entry(s->cur_func, &block_env, 24503 JS_ATOM_NULL, -1, -1, 2); 24504 block_env.has_iterator = TRUE; 24505 emit_op(s, OP_for_of_start); 24506 has_spread = FALSE; 24507 while (s->token.val != ']') { 24508 /* get the next value */ 24509 if (s->token.val == TOK_ELLIPSIS) { 24510 if (next_token(s)) 24511 return -1; 24512 if (s->token.val == ',' || s->token.val == ']') 24513 return js_parse_error(s, "missing binding pattern..."); 24514 has_spread = TRUE; 24515 } 24516 if (s->token.val == ',') { 24517 /* do nothing, skip the value, has_spread is false */ 24518 emit_op(s, OP_for_of_next); 24519 emit_u8(s, 0); 24520 emit_op(s, OP_drop); 24521 emit_op(s, OP_drop); 24522 } else if ((s->token.val == '[' || s->token.val == '{') 24523 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' || 24524 tok1 == '=' || tok1 == ']')) { 24525 if (has_spread) { 24526 if (tok1 == '=') 24527 return js_parse_error(s, "rest element cannot have a default value"); 24528 js_emit_spread_code(s, 0); 24529 } else { 24530 emit_op(s, OP_for_of_next); 24531 emit_u8(s, 0); 24532 emit_op(s, OP_drop); 24533 } 24534 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) 24535 return -1; 24536 } else { 24537 var_name = JS_ATOM_NULL; 24538 enum_depth = 0; 24539 if (tok) { 24540 var_name = js_parse_destructuring_var(s, tok, is_arg); 24541 if (var_name == JS_ATOM_NULL) 24542 goto var_error; 24543 if (js_define_var(s, var_name, tok)) 24544 goto var_error; 24545 opcode = OP_scope_get_var; 24546 scope = s->cur_func->scope_level; 24547 } else { 24548 if (js_parse_left_hand_side_expr(s)) 24549 return -1; 24550 if (get_lvalue(s, &opcode, &scope, &var_name, 24551 &label_lvalue, &enum_depth, FALSE, '[')) { 24552 return -1; 24553 } 24554 } 24555 if (has_spread) { 24556 js_emit_spread_code(s, enum_depth); 24557 } else { 24558 emit_op(s, OP_for_of_next); 24559 emit_u8(s, enum_depth); 24560 emit_op(s, OP_drop); 24561 } 24562 if (s->token.val == '=' && !has_spread) { 24563 /* handle optional default value */ 24564 int label_hasval; 24565 emit_op(s, OP_dup); 24566 emit_op(s, OP_undefined); 24567 emit_op(s, OP_strict_eq); 24568 label_hasval = emit_goto(s, OP_if_false, -1); 24569 if (next_token(s)) 24570 goto var_error; 24571 emit_op(s, OP_drop); 24572 if (js_parse_assign_expr(s)) 24573 goto var_error; 24574 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value) 24575 set_object_name(s, var_name); 24576 emit_label(s, label_hasval); 24577 } 24578 /* store value into lvalue object */ 24579 put_lvalue(s, opcode, scope, var_name, 24580 label_lvalue, PUT_LVALUE_NOKEEP_DEPTH, 24581 (tok == TOK_CONST || tok == TOK_LET)); 24582 } 24583 if (s->token.val == ']') 24584 break; 24585 if (has_spread) 24586 return js_parse_error(s, "rest element must be the last one"); 24587 /* accept a trailing comma before the ']' */ 24588 if (js_parse_expect(s, ',')) 24589 return -1; 24590 } 24591 /* close iterator object: 24592 if completed, enum_obj has been replaced by undefined */ 24593 emit_op(s, OP_iterator_close); 24594 pop_break_entry(s->cur_func); 24595 if (next_token(s)) 24596 return -1; 24597 } else { 24598 return js_parse_error(s, "invalid assignment syntax"); 24599 } 24600 if (s->token.val == '=' && allow_initializer) { 24601 label_done = emit_goto(s, OP_goto, -1); 24602 if (next_token(s)) 24603 return -1; 24604 emit_label(s, label_parse); 24605 if (hasval) 24606 emit_op(s, OP_drop); 24607 if (js_parse_assign_expr(s)) 24608 return -1; 24609 emit_goto(s, OP_goto, label_assign); 24610 emit_label(s, label_done); 24611 has_initializer = TRUE; 24612 } else { 24613 /* normally hasval is true except if 24614 js_parse_skip_parens_token() was wrong in the parsing */ 24615 // assert(hasval); 24616 if (!hasval) { 24617 js_parse_error(s, "too complicated destructuring expression"); 24618 return -1; 24619 } 24620 /* remove test and decrement label ref count */ 24621 memset(s->cur_func->byte_code.buf + start_addr, OP_nop, 24622 assign_addr - start_addr); 24623 s->cur_func->label_slots[label_parse].ref_count--; 24624 has_initializer = FALSE; 24625 } 24626 return has_initializer; 24627 24628 prop_error: 24629 JS_FreeAtom(s->ctx, prop_name); 24630 var_error: 24631 JS_FreeAtom(s->ctx, var_name); 24632 return -1; 24633 } 24634 24635 typedef enum FuncCallType { 24636 FUNC_CALL_NORMAL, 24637 FUNC_CALL_NEW, 24638 FUNC_CALL_SUPER_CTOR, 24639 FUNC_CALL_TEMPLATE, 24640 } FuncCallType; 24641 24642 static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, 24643 int drop_count) 24644 { 24645 int label_next, i; 24646 if (*poptional_chaining_label < 0) 24647 *poptional_chaining_label = new_label(s); 24648 /* XXX: could be more efficient with a specific opcode */ 24649 emit_op(s, OP_dup); 24650 emit_op(s, OP_is_undefined_or_null); 24651 label_next = emit_goto(s, OP_if_false, -1); 24652 for(i = 0; i < drop_count; i++) 24653 emit_op(s, OP_drop); 24654 emit_op(s, OP_undefined); 24655 emit_goto(s, OP_goto, *poptional_chaining_label); 24656 emit_label(s, label_next); 24657 } 24658 24659 /* allowed parse_flags: PF_POSTFIX_CALL */ 24660 static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) 24661 { 24662 FuncCallType call_type; 24663 int optional_chaining_label; 24664 BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0; 24665 24666 call_type = FUNC_CALL_NORMAL; 24667 switch(s->token.val) { 24668 case TOK_NUMBER: 24669 { 24670 JSValue val; 24671 val = s->token.u.num.val; 24672 24673 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { 24674 emit_op(s, OP_push_i32); 24675 emit_u32(s, JS_VALUE_GET_INT(val)); 24676 } else 24677 #ifdef CONFIG_BIGNUM 24678 if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) { 24679 slimb_t e; 24680 int ret; 24681 24682 /* need a runtime conversion */ 24683 /* XXX: could add a cache and/or do it once at 24684 the start of the function */ 24685 if (emit_push_const(s, val, 0) < 0) 24686 return -1; 24687 e = s->token.u.num.exponent; 24688 if (e == (int32_t)e) { 24689 emit_op(s, OP_push_i32); 24690 emit_u32(s, e); 24691 } else { 24692 val = JS_NewBigInt64_1(s->ctx, e); 24693 if (JS_IsException(val)) 24694 return -1; 24695 ret = emit_push_const(s, val, 0); 24696 JS_FreeValue(s->ctx, val); 24697 if (ret < 0) 24698 return -1; 24699 } 24700 emit_op(s, OP_mul_pow10); 24701 } else 24702 #endif 24703 { 24704 if (emit_push_const(s, val, 0) < 0) 24705 return -1; 24706 } 24707 } 24708 if (next_token(s)) 24709 return -1; 24710 break; 24711 case TOK_TEMPLATE: 24712 if (js_parse_template(s, 0, NULL)) 24713 return -1; 24714 break; 24715 case TOK_STRING: 24716 if (emit_push_const(s, s->token.u.str.str, 1)) 24717 return -1; 24718 if (next_token(s)) 24719 return -1; 24720 break; 24721 24722 case TOK_DIV_ASSIGN: 24723 s->buf_ptr -= 2; 24724 goto parse_regexp; 24725 case '/': 24726 s->buf_ptr--; 24727 parse_regexp: 24728 { 24729 JSValue str; 24730 int ret, backtrace_flags; 24731 if (!s->ctx->compile_regexp) 24732 return js_parse_error(s, "RegExp are not supported"); 24733 /* the previous token is '/' or '/=', so no need to free */ 24734 if (js_parse_regexp(s)) 24735 return -1; 24736 ret = emit_push_const(s, s->token.u.regexp.body, 0); 24737 str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body, 24738 s->token.u.regexp.flags); 24739 if (JS_IsException(str)) { 24740 /* add the line number info */ 24741 backtrace_flags = 0; 24742 if (s->cur_func && s->cur_func->backtrace_barrier) 24743 backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; 24744 build_backtrace(s->ctx, s->ctx->rt->current_exception, 24745 s->filename, s->token.line_num, 24746 backtrace_flags); 24747 return -1; 24748 } 24749 ret = emit_push_const(s, str, 0); 24750 JS_FreeValue(s->ctx, str); 24751 if (ret) 24752 return -1; 24753 /* we use a specific opcode to be sure the correct 24754 function is called (otherwise the bytecode would have 24755 to be verified by the RegExp constructor) */ 24756 emit_op(s, OP_regexp); 24757 if (next_token(s)) 24758 return -1; 24759 } 24760 break; 24761 case '(': 24762 if (js_parse_expr_paren(s)) 24763 return -1; 24764 break; 24765 case TOK_FUNCTION: 24766 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, 24767 JS_FUNC_NORMAL, JS_ATOM_NULL, 24768 s->token.ptr, s->token.line_num)) 24769 return -1; 24770 break; 24771 case TOK_CLASS: 24772 if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE)) 24773 return -1; 24774 break; 24775 case TOK_NULL: 24776 if (next_token(s)) 24777 return -1; 24778 emit_op(s, OP_null); 24779 break; 24780 case TOK_THIS: 24781 if (next_token(s)) 24782 return -1; 24783 emit_op(s, OP_scope_get_var); 24784 emit_atom(s, JS_ATOM_this); 24785 emit_u16(s, 0); 24786 break; 24787 case TOK_FALSE: 24788 if (next_token(s)) 24789 return -1; 24790 emit_op(s, OP_push_false); 24791 break; 24792 case TOK_TRUE: 24793 if (next_token(s)) 24794 return -1; 24795 emit_op(s, OP_push_true); 24796 break; 24797 case TOK_IDENT: 24798 { 24799 JSAtom name; 24800 if (s->token.u.ident.is_reserved) { 24801 return js_parse_error_reserved_identifier(s); 24802 } 24803 if (token_is_pseudo_keyword(s, JS_ATOM_async) && 24804 peek_token(s, TRUE) != '\n') { 24805 const uint8_t *source_ptr; 24806 int source_line_num; 24807 24808 source_ptr = s->token.ptr; 24809 source_line_num = s->token.line_num; 24810 if (next_token(s)) 24811 return -1; 24812 if (s->token.val == TOK_FUNCTION) { 24813 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, 24814 JS_FUNC_ASYNC, JS_ATOM_NULL, 24815 source_ptr, source_line_num)) 24816 return -1; 24817 } else { 24818 name = JS_DupAtom(s->ctx, JS_ATOM_async); 24819 goto do_get_var; 24820 } 24821 } else { 24822 if (s->token.u.ident.atom == JS_ATOM_arguments && 24823 !s->cur_func->arguments_allowed) { 24824 js_parse_error(s, "'arguments' identifier is not allowed in class field initializer"); 24825 return -1; 24826 } 24827 name = JS_DupAtom(s->ctx, s->token.u.ident.atom); 24828 if (next_token(s)) { /* update line number before emitting code */ 24829 JS_FreeAtom(s->ctx, name); 24830 return -1; 24831 } 24832 do_get_var: 24833 emit_op(s, OP_scope_get_var); 24834 emit_u32(s, name); 24835 emit_u16(s, s->cur_func->scope_level); 24836 } 24837 } 24838 break; 24839 case '{': 24840 case '[': 24841 { 24842 int skip_bits; 24843 if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { 24844 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) 24845 return -1; 24846 } else { 24847 if (s->token.val == '{') { 24848 if (js_parse_object_literal(s)) 24849 return -1; 24850 } else { 24851 if (js_parse_array_literal(s)) 24852 return -1; 24853 } 24854 } 24855 } 24856 break; 24857 case TOK_NEW: 24858 if (next_token(s)) 24859 return -1; 24860 if (s->token.val == '.') { 24861 if (next_token(s)) 24862 return -1; 24863 if (!token_is_pseudo_keyword(s, JS_ATOM_target)) 24864 return js_parse_error(s, "expecting target"); 24865 if (!s->cur_func->new_target_allowed) 24866 return js_parse_error(s, "new.target only allowed within functions"); 24867 if (next_token(s)) 24868 return -1; 24869 emit_op(s, OP_scope_get_var); 24870 emit_atom(s, JS_ATOM_new_target); 24871 emit_u16(s, 0); 24872 } else { 24873 if (js_parse_postfix_expr(s, 0)) 24874 return -1; 24875 accept_lparen = TRUE; 24876 if (s->token.val != '(') { 24877 /* new operator on an object */ 24878 emit_op(s, OP_dup); 24879 emit_op(s, OP_call_constructor); 24880 emit_u16(s, 0); 24881 } else { 24882 call_type = FUNC_CALL_NEW; 24883 } 24884 } 24885 break; 24886 case TOK_SUPER: 24887 if (next_token(s)) 24888 return -1; 24889 if (s->token.val == '(') { 24890 if (!s->cur_func->super_call_allowed) 24891 return js_parse_error(s, "super() is only valid in a derived class constructor"); 24892 call_type = FUNC_CALL_SUPER_CTOR; 24893 } else if (s->token.val == '.' || s->token.val == '[') { 24894 if (!s->cur_func->super_allowed) 24895 return js_parse_error(s, "'super' is only valid in a method"); 24896 emit_op(s, OP_scope_get_var); 24897 emit_atom(s, JS_ATOM_this); 24898 emit_u16(s, 0); 24899 emit_op(s, OP_scope_get_var); 24900 emit_atom(s, JS_ATOM_home_object); 24901 emit_u16(s, 0); 24902 emit_op(s, OP_get_super); 24903 } else { 24904 return js_parse_error(s, "invalid use of 'super'"); 24905 } 24906 break; 24907 case TOK_IMPORT: 24908 if (next_token(s)) 24909 return -1; 24910 if (s->token.val == '.') { 24911 if (next_token(s)) 24912 return -1; 24913 if (!token_is_pseudo_keyword(s, JS_ATOM_meta)) 24914 return js_parse_error(s, "meta expected"); 24915 if (!s->is_module) 24916 return js_parse_error(s, "import.meta only valid in module code"); 24917 if (next_token(s)) 24918 return -1; 24919 emit_op(s, OP_special_object); 24920 emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META); 24921 } else { 24922 if (js_parse_expect(s, '(')) 24923 return -1; 24924 if (!accept_lparen) 24925 return js_parse_error(s, "invalid use of 'import()'"); 24926 if (js_parse_assign_expr(s)) 24927 return -1; 24928 if (js_parse_expect(s, ')')) 24929 return -1; 24930 emit_op(s, OP_import); 24931 } 24932 break; 24933 default: 24934 return js_parse_error(s, "unexpected token in expression: '%.*s'", 24935 (int)(s->buf_ptr - s->token.ptr), s->token.ptr); 24936 } 24937 24938 optional_chaining_label = -1; 24939 for(;;) { 24940 JSFunctionDef *fd = s->cur_func; 24941 BOOL has_optional_chain = FALSE; 24942 24943 if (s->token.val == TOK_QUESTION_MARK_DOT) { 24944 /* optional chaining */ 24945 if (next_token(s)) 24946 return -1; 24947 has_optional_chain = TRUE; 24948 if (s->token.val == '(' && accept_lparen) { 24949 goto parse_func_call; 24950 } else if (s->token.val == '[') { 24951 goto parse_array_access; 24952 } else { 24953 goto parse_property; 24954 } 24955 } else if (s->token.val == TOK_TEMPLATE && 24956 call_type == FUNC_CALL_NORMAL) { 24957 if (optional_chaining_label >= 0) { 24958 return js_parse_error(s, "template literal cannot appear in an optional chain"); 24959 } 24960 call_type = FUNC_CALL_TEMPLATE; 24961 goto parse_func_call2; 24962 } else if (s->token.val == '(' && accept_lparen) { 24963 int opcode, arg_count, drop_count; 24964 24965 /* function call */ 24966 parse_func_call: 24967 if (next_token(s)) 24968 return -1; 24969 24970 if (call_type == FUNC_CALL_NORMAL) { 24971 parse_func_call2: 24972 switch(opcode = get_prev_opcode(fd)) { 24973 case OP_get_field: 24974 /* keep the object on the stack */ 24975 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; 24976 drop_count = 2; 24977 break; 24978 case OP_get_field_opt_chain: 24979 { 24980 int opt_chain_label, next_label; 24981 opt_chain_label = get_u32(fd->byte_code.buf + 24982 fd->last_opcode_pos + 1 + 4 + 1); 24983 /* keep the object on the stack */ 24984 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; 24985 fd->byte_code.size = fd->last_opcode_pos + 1 + 4; 24986 next_label = emit_goto(s, OP_goto, -1); 24987 emit_label(s, opt_chain_label); 24988 /* need an additional undefined value for the 24989 case where the optional field does not 24990 exists */ 24991 emit_op(s, OP_undefined); 24992 emit_label(s, next_label); 24993 drop_count = 2; 24994 opcode = OP_get_field; 24995 } 24996 break; 24997 case OP_scope_get_private_field: 24998 /* keep the object on the stack */ 24999 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; 25000 drop_count = 2; 25001 break; 25002 case OP_get_array_el: 25003 /* keep the object on the stack */ 25004 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; 25005 drop_count = 2; 25006 break; 25007 case OP_get_array_el_opt_chain: 25008 { 25009 int opt_chain_label, next_label; 25010 opt_chain_label = get_u32(fd->byte_code.buf + 25011 fd->last_opcode_pos + 1 + 1); 25012 /* keep the object on the stack */ 25013 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; 25014 fd->byte_code.size = fd->last_opcode_pos + 1; 25015 next_label = emit_goto(s, OP_goto, -1); 25016 emit_label(s, opt_chain_label); 25017 /* need an additional undefined value for the 25018 case where the optional field does not 25019 exists */ 25020 emit_op(s, OP_undefined); 25021 emit_label(s, next_label); 25022 drop_count = 2; 25023 opcode = OP_get_array_el; 25024 } 25025 break; 25026 case OP_scope_get_var: 25027 { 25028 JSAtom name; 25029 int scope; 25030 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 25031 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5); 25032 if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) { 25033 /* direct 'eval' */ 25034 opcode = OP_eval; 25035 } else { 25036 /* verify if function name resolves to a simple 25037 get_loc/get_arg: a function call inside a `with` 25038 statement can resolve to a method call of the 25039 `with` context object 25040 */ 25041 /* XXX: always generate the OP_scope_get_ref 25042 and remove it in variable resolution 25043 pass ? */ 25044 if (has_with_scope(fd, scope)) { 25045 opcode = OP_scope_get_ref; 25046 fd->byte_code.buf[fd->last_opcode_pos] = opcode; 25047 } 25048 } 25049 drop_count = 1; 25050 } 25051 break; 25052 case OP_get_super_value: 25053 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el; 25054 /* on stack: this func_obj */ 25055 opcode = OP_get_array_el; 25056 drop_count = 2; 25057 break; 25058 default: 25059 opcode = OP_invalid; 25060 drop_count = 1; 25061 break; 25062 } 25063 if (has_optional_chain) { 25064 optional_chain_test(s, &optional_chaining_label, 25065 drop_count); 25066 } 25067 } else { 25068 opcode = OP_invalid; 25069 } 25070 25071 if (call_type == FUNC_CALL_TEMPLATE) { 25072 if (js_parse_template(s, 1, &arg_count)) 25073 return -1; 25074 goto emit_func_call; 25075 } else if (call_type == FUNC_CALL_SUPER_CTOR) { 25076 emit_op(s, OP_scope_get_var); 25077 emit_atom(s, JS_ATOM_this_active_func); 25078 emit_u16(s, 0); 25079 25080 emit_op(s, OP_get_super); 25081 25082 emit_op(s, OP_scope_get_var); 25083 emit_atom(s, JS_ATOM_new_target); 25084 emit_u16(s, 0); 25085 } else if (call_type == FUNC_CALL_NEW) { 25086 emit_op(s, OP_dup); /* new.target = function */ 25087 } 25088 25089 /* parse arguments */ 25090 arg_count = 0; 25091 while (s->token.val != ')') { 25092 if (arg_count >= 65535) { 25093 return js_parse_error(s, "Too many call arguments"); 25094 } 25095 if (s->token.val == TOK_ELLIPSIS) 25096 break; 25097 if (js_parse_assign_expr(s)) 25098 return -1; 25099 arg_count++; 25100 if (s->token.val == ')') 25101 break; 25102 /* accept a trailing comma before the ')' */ 25103 if (js_parse_expect(s, ',')) 25104 return -1; 25105 } 25106 if (s->token.val == TOK_ELLIPSIS) { 25107 emit_op(s, OP_array_from); 25108 emit_u16(s, arg_count); 25109 emit_op(s, OP_push_i32); 25110 emit_u32(s, arg_count); 25111 25112 /* on stack: array idx */ 25113 while (s->token.val != ')') { 25114 if (s->token.val == TOK_ELLIPSIS) { 25115 if (next_token(s)) 25116 return -1; 25117 if (js_parse_assign_expr(s)) 25118 return -1; 25119 #if 1 25120 /* XXX: could pass is_last indicator? */ 25121 emit_op(s, OP_append); 25122 #else 25123 int label_next, label_done; 25124 label_next = new_label(s); 25125 label_done = new_label(s); 25126 /* push enumerate object below array/idx pair */ 25127 emit_op(s, OP_for_of_start); 25128 emit_op(s, OP_rot5l); 25129 emit_op(s, OP_rot5l); 25130 emit_label(s, label_next); 25131 /* on stack: enum_rec array idx */ 25132 emit_op(s, OP_for_of_next); 25133 emit_u8(s, 2); 25134 emit_goto(s, OP_if_true, label_done); 25135 /* append element */ 25136 /* enum_rec array idx val -> enum_rec array new_idx */ 25137 emit_op(s, OP_define_array_el); 25138 emit_op(s, OP_inc); 25139 emit_goto(s, OP_goto, label_next); 25140 emit_label(s, label_done); 25141 /* close enumeration, drop enum_rec and idx */ 25142 emit_op(s, OP_drop); /* drop undef */ 25143 emit_op(s, OP_nip1); /* drop enum_rec */ 25144 emit_op(s, OP_nip1); 25145 emit_op(s, OP_nip1); 25146 #endif 25147 } else { 25148 if (js_parse_assign_expr(s)) 25149 return -1; 25150 /* array idx val */ 25151 emit_op(s, OP_define_array_el); 25152 emit_op(s, OP_inc); 25153 } 25154 if (s->token.val == ')') 25155 break; 25156 /* accept a trailing comma before the ')' */ 25157 if (js_parse_expect(s, ',')) 25158 return -1; 25159 } 25160 if (next_token(s)) 25161 return -1; 25162 /* drop the index */ 25163 emit_op(s, OP_drop); 25164 25165 /* apply function call */ 25166 switch(opcode) { 25167 case OP_get_field: 25168 case OP_scope_get_private_field: 25169 case OP_get_array_el: 25170 case OP_scope_get_ref: 25171 /* obj func array -> func obj array */ 25172 emit_op(s, OP_perm3); 25173 emit_op(s, OP_apply); 25174 emit_u16(s, call_type == FUNC_CALL_NEW); 25175 break; 25176 case OP_eval: 25177 emit_op(s, OP_apply_eval); 25178 emit_u16(s, fd->scope_level); 25179 fd->has_eval_call = TRUE; 25180 break; 25181 default: 25182 if (call_type == FUNC_CALL_SUPER_CTOR) { 25183 emit_op(s, OP_apply); 25184 emit_u16(s, 1); 25185 /* set the 'this' value */ 25186 emit_op(s, OP_dup); 25187 emit_op(s, OP_scope_put_var_init); 25188 emit_atom(s, JS_ATOM_this); 25189 emit_u16(s, 0); 25190 25191 emit_class_field_init(s); 25192 } else if (call_type == FUNC_CALL_NEW) { 25193 /* obj func array -> func obj array */ 25194 emit_op(s, OP_perm3); 25195 emit_op(s, OP_apply); 25196 emit_u16(s, 1); 25197 } else { 25198 /* func array -> func undef array */ 25199 emit_op(s, OP_undefined); 25200 emit_op(s, OP_swap); 25201 emit_op(s, OP_apply); 25202 emit_u16(s, 0); 25203 } 25204 break; 25205 } 25206 } else { 25207 if (next_token(s)) 25208 return -1; 25209 emit_func_call: 25210 switch(opcode) { 25211 case OP_get_field: 25212 case OP_scope_get_private_field: 25213 case OP_get_array_el: 25214 case OP_scope_get_ref: 25215 emit_op(s, OP_call_method); 25216 emit_u16(s, arg_count); 25217 break; 25218 case OP_eval: 25219 emit_op(s, OP_eval); 25220 emit_u16(s, arg_count); 25221 emit_u16(s, fd->scope_level); 25222 fd->has_eval_call = TRUE; 25223 break; 25224 default: 25225 if (call_type == FUNC_CALL_SUPER_CTOR) { 25226 emit_op(s, OP_call_constructor); 25227 emit_u16(s, arg_count); 25228 25229 /* set the 'this' value */ 25230 emit_op(s, OP_dup); 25231 emit_op(s, OP_scope_put_var_init); 25232 emit_atom(s, JS_ATOM_this); 25233 emit_u16(s, 0); 25234 25235 emit_class_field_init(s); 25236 } else if (call_type == FUNC_CALL_NEW) { 25237 emit_op(s, OP_call_constructor); 25238 emit_u16(s, arg_count); 25239 } else { 25240 emit_op(s, OP_call); 25241 emit_u16(s, arg_count); 25242 } 25243 break; 25244 } 25245 } 25246 call_type = FUNC_CALL_NORMAL; 25247 } else if (s->token.val == '.') { 25248 if (next_token(s)) 25249 return -1; 25250 parse_property: 25251 if (s->token.val == TOK_PRIVATE_NAME) { 25252 /* private class field */ 25253 if (get_prev_opcode(fd) == OP_get_super) { 25254 return js_parse_error(s, "private class field forbidden after super"); 25255 } 25256 if (has_optional_chain) { 25257 optional_chain_test(s, &optional_chaining_label, 1); 25258 } 25259 emit_op(s, OP_scope_get_private_field); 25260 emit_atom(s, s->token.u.ident.atom); 25261 emit_u16(s, s->cur_func->scope_level); 25262 } else { 25263 if (!token_is_ident(s->token.val)) { 25264 return js_parse_error(s, "expecting field name"); 25265 } 25266 if (get_prev_opcode(fd) == OP_get_super) { 25267 JSValue val; 25268 int ret; 25269 val = JS_AtomToValue(s->ctx, s->token.u.ident.atom); 25270 ret = emit_push_const(s, val, 1); 25271 JS_FreeValue(s->ctx, val); 25272 if (ret) 25273 return -1; 25274 emit_op(s, OP_get_super_value); 25275 } else { 25276 if (has_optional_chain) { 25277 optional_chain_test(s, &optional_chaining_label, 1); 25278 } 25279 emit_op(s, OP_get_field); 25280 emit_atom(s, s->token.u.ident.atom); 25281 } 25282 } 25283 if (next_token(s)) 25284 return -1; 25285 } else if (s->token.val == '[') { 25286 int prev_op; 25287 25288 parse_array_access: 25289 prev_op = get_prev_opcode(fd); 25290 if (has_optional_chain) { 25291 optional_chain_test(s, &optional_chaining_label, 1); 25292 } 25293 if (next_token(s)) 25294 return -1; 25295 if (js_parse_expr(s)) 25296 return -1; 25297 if (js_parse_expect(s, ']')) 25298 return -1; 25299 if (prev_op == OP_get_super) { 25300 emit_op(s, OP_get_super_value); 25301 } else { 25302 emit_op(s, OP_get_array_el); 25303 } 25304 } else { 25305 break; 25306 } 25307 } 25308 if (optional_chaining_label >= 0) { 25309 JSFunctionDef *fd = s->cur_func; 25310 int opcode; 25311 emit_label_raw(s, optional_chaining_label); 25312 /* modify the last opcode so that it is an indicator of an 25313 optional chain */ 25314 opcode = get_prev_opcode(fd); 25315 if (opcode == OP_get_field || opcode == OP_get_array_el) { 25316 if (opcode == OP_get_field) 25317 opcode = OP_get_field_opt_chain; 25318 else 25319 opcode = OP_get_array_el_opt_chain; 25320 fd->byte_code.buf[fd->last_opcode_pos] = opcode; 25321 } else { 25322 fd->last_opcode_pos = -1; 25323 } 25324 } 25325 return 0; 25326 } 25327 25328 static __exception int js_parse_delete(JSParseState *s) 25329 { 25330 JSFunctionDef *fd = s->cur_func; 25331 JSAtom name; 25332 int opcode; 25333 25334 if (next_token(s)) 25335 return -1; 25336 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 25337 return -1; 25338 switch(opcode = get_prev_opcode(fd)) { 25339 case OP_get_field: 25340 case OP_get_field_opt_chain: 25341 { 25342 JSValue val; 25343 int ret, opt_chain_label, next_label; 25344 if (opcode == OP_get_field_opt_chain) { 25345 opt_chain_label = get_u32(fd->byte_code.buf + 25346 fd->last_opcode_pos + 1 + 4 + 1); 25347 } else { 25348 opt_chain_label = -1; 25349 } 25350 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 25351 fd->byte_code.size = fd->last_opcode_pos; 25352 val = JS_AtomToValue(s->ctx, name); 25353 ret = emit_push_const(s, val, 1); 25354 JS_FreeValue(s->ctx, val); 25355 JS_FreeAtom(s->ctx, name); 25356 if (ret) 25357 return ret; 25358 emit_op(s, OP_delete); 25359 if (opt_chain_label >= 0) { 25360 next_label = emit_goto(s, OP_goto, -1); 25361 emit_label(s, opt_chain_label); 25362 /* if the optional chain is not taken, return 'true' */ 25363 emit_op(s, OP_drop); 25364 emit_op(s, OP_push_true); 25365 emit_label(s, next_label); 25366 } 25367 fd->last_opcode_pos = -1; 25368 } 25369 break; 25370 case OP_get_array_el: 25371 fd->byte_code.size = fd->last_opcode_pos; 25372 fd->last_opcode_pos = -1; 25373 emit_op(s, OP_delete); 25374 break; 25375 case OP_get_array_el_opt_chain: 25376 { 25377 int opt_chain_label, next_label; 25378 opt_chain_label = get_u32(fd->byte_code.buf + 25379 fd->last_opcode_pos + 1 + 1); 25380 fd->byte_code.size = fd->last_opcode_pos; 25381 emit_op(s, OP_delete); 25382 next_label = emit_goto(s, OP_goto, -1); 25383 emit_label(s, opt_chain_label); 25384 /* if the optional chain is not taken, return 'true' */ 25385 emit_op(s, OP_drop); 25386 emit_op(s, OP_push_true); 25387 emit_label(s, next_label); 25388 fd->last_opcode_pos = -1; 25389 } 25390 break; 25391 case OP_scope_get_var: 25392 /* 'delete this': this is not a reference */ 25393 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); 25394 if (name == JS_ATOM_this || name == JS_ATOM_new_target) 25395 goto ret_true; 25396 if (fd->js_mode & JS_MODE_STRICT) { 25397 return js_parse_error(s, "cannot delete a direct reference in strict mode"); 25398 } else { 25399 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var; 25400 } 25401 break; 25402 case OP_scope_get_private_field: 25403 return js_parse_error(s, "cannot delete a private class field"); 25404 case OP_get_super_value: 25405 fd->byte_code.size = fd->last_opcode_pos; 25406 fd->last_opcode_pos = -1; 25407 emit_op(s, OP_throw_error); 25408 emit_atom(s, JS_ATOM_NULL); 25409 emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); 25410 break; 25411 default: 25412 ret_true: 25413 emit_op(s, OP_drop); 25414 emit_op(s, OP_push_true); 25415 break; 25416 } 25417 return 0; 25418 } 25419 25420 /* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ 25421 static __exception int js_parse_unary(JSParseState *s, int parse_flags) 25422 { 25423 int op; 25424 25425 switch(s->token.val) { 25426 case '+': 25427 case '-': 25428 case '!': 25429 case '~': 25430 case TOK_VOID: 25431 op = s->token.val; 25432 if (next_token(s)) 25433 return -1; 25434 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 25435 return -1; 25436 switch(op) { 25437 case '-': 25438 emit_op(s, OP_neg); 25439 break; 25440 case '+': 25441 emit_op(s, OP_plus); 25442 break; 25443 case '!': 25444 emit_op(s, OP_lnot); 25445 break; 25446 case '~': 25447 emit_op(s, OP_not); 25448 break; 25449 case TOK_VOID: 25450 emit_op(s, OP_drop); 25451 emit_op(s, OP_undefined); 25452 break; 25453 default: 25454 abort(); 25455 } 25456 parse_flags = 0; 25457 break; 25458 case TOK_DEC: 25459 case TOK_INC: 25460 { 25461 int opcode, op, scope, label; 25462 JSAtom name; 25463 op = s->token.val; 25464 if (next_token(s)) 25465 return -1; 25466 if (js_parse_unary(s, 0)) 25467 return -1; 25468 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) 25469 return -1; 25470 emit_op(s, OP_dec + op - TOK_DEC); 25471 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, 25472 FALSE); 25473 } 25474 break; 25475 case TOK_TYPEOF: 25476 { 25477 JSFunctionDef *fd; 25478 if (next_token(s)) 25479 return -1; 25480 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 25481 return -1; 25482 /* reference access should not return an exception, so we 25483 patch the get_var */ 25484 fd = s->cur_func; 25485 if (get_prev_opcode(fd) == OP_scope_get_var) { 25486 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef; 25487 } 25488 emit_op(s, OP_typeof); 25489 parse_flags = 0; 25490 } 25491 break; 25492 case TOK_DELETE: 25493 if (js_parse_delete(s)) 25494 return -1; 25495 parse_flags = 0; 25496 break; 25497 case TOK_AWAIT: 25498 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) 25499 return js_parse_error(s, "unexpected 'await' keyword"); 25500 if (!s->cur_func->in_function_body) 25501 return js_parse_error(s, "await in default expression"); 25502 if (next_token(s)) 25503 return -1; 25504 if (js_parse_unary(s, PF_POW_FORBIDDEN)) 25505 return -1; 25506 s->cur_func->has_await = TRUE; 25507 emit_op(s, OP_await); 25508 parse_flags = 0; 25509 break; 25510 default: 25511 if (js_parse_postfix_expr(s, PF_POSTFIX_CALL)) 25512 return -1; 25513 if (!s->got_lf && 25514 (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { 25515 int opcode, op, scope, label; 25516 JSAtom name; 25517 op = s->token.val; 25518 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op)) 25519 return -1; 25520 emit_op(s, OP_post_dec + op - TOK_DEC); 25521 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND, 25522 FALSE); 25523 if (next_token(s)) 25524 return -1; 25525 } 25526 break; 25527 } 25528 if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) { 25529 #ifdef CONFIG_BIGNUM 25530 if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) { 25531 /* Extended exponentiation syntax rules: we extend the ES7 25532 grammar in order to have more intuitive semantics: 25533 -2**2 evaluates to -4. */ 25534 if (!(s->cur_func->js_mode & JS_MODE_MATH)) { 25535 if (parse_flags & PF_POW_FORBIDDEN) { 25536 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); 25537 return -1; 25538 } 25539 } 25540 if (next_token(s)) 25541 return -1; 25542 if (js_parse_unary(s, PF_POW_ALLOWED)) 25543 return -1; 25544 emit_op(s, OP_pow); 25545 } 25546 #else 25547 if (s->token.val == TOK_POW) { 25548 /* Strict ES7 exponentiation syntax rules: To solve 25549 conficting semantics between different implementations 25550 regarding the precedence of prefix operators and the 25551 postifx exponential, ES7 specifies that -2**2 is a 25552 syntax error. */ 25553 if (parse_flags & PF_POW_FORBIDDEN) { 25554 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'"); 25555 return -1; 25556 } 25557 if (next_token(s)) 25558 return -1; 25559 if (js_parse_unary(s, PF_POW_ALLOWED)) 25560 return -1; 25561 emit_op(s, OP_pow); 25562 } 25563 #endif 25564 } 25565 return 0; 25566 } 25567 25568 /* allowed parse_flags: PF_IN_ACCEPTED */ 25569 static __exception int js_parse_expr_binary(JSParseState *s, int level, 25570 int parse_flags) 25571 { 25572 int op, opcode; 25573 25574 if (level == 0) { 25575 return js_parse_unary(s, PF_POW_ALLOWED); 25576 } else if (s->token.val == TOK_PRIVATE_NAME && 25577 (parse_flags & PF_IN_ACCEPTED) && level == 4 && 25578 peek_token(s, FALSE) == TOK_IN) { 25579 JSAtom atom; 25580 25581 atom = JS_DupAtom(s->ctx, s->token.u.ident.atom); 25582 if (next_token(s)) 25583 goto fail_private_in; 25584 if (s->token.val != TOK_IN) 25585 goto fail_private_in; 25586 if (next_token(s)) 25587 goto fail_private_in; 25588 if (js_parse_expr_binary(s, level - 1, parse_flags)) { 25589 fail_private_in: 25590 JS_FreeAtom(s->ctx, atom); 25591 return -1; 25592 } 25593 emit_op(s, OP_scope_in_private_field); 25594 emit_atom(s, atom); 25595 emit_u16(s, s->cur_func->scope_level); 25596 JS_FreeAtom(s->ctx, atom); 25597 return 0; 25598 } else { 25599 if (js_parse_expr_binary(s, level - 1, parse_flags)) 25600 return -1; 25601 } 25602 for(;;) { 25603 op = s->token.val; 25604 switch(level) { 25605 case 1: 25606 switch(op) { 25607 case '*': 25608 opcode = OP_mul; 25609 break; 25610 case '/': 25611 opcode = OP_div; 25612 break; 25613 case '%': 25614 #ifdef CONFIG_BIGNUM 25615 if (s->cur_func->js_mode & JS_MODE_MATH) 25616 opcode = OP_math_mod; 25617 else 25618 #endif 25619 opcode = OP_mod; 25620 break; 25621 default: 25622 return 0; 25623 } 25624 break; 25625 case 2: 25626 switch(op) { 25627 case '+': 25628 opcode = OP_add; 25629 break; 25630 case '-': 25631 opcode = OP_sub; 25632 break; 25633 default: 25634 return 0; 25635 } 25636 break; 25637 case 3: 25638 switch(op) { 25639 case TOK_SHL: 25640 opcode = OP_shl; 25641 break; 25642 case TOK_SAR: 25643 opcode = OP_sar; 25644 break; 25645 case TOK_SHR: 25646 opcode = OP_shr; 25647 break; 25648 default: 25649 return 0; 25650 } 25651 break; 25652 case 4: 25653 switch(op) { 25654 case '<': 25655 opcode = OP_lt; 25656 break; 25657 case '>': 25658 opcode = OP_gt; 25659 break; 25660 case TOK_LTE: 25661 opcode = OP_lte; 25662 break; 25663 case TOK_GTE: 25664 opcode = OP_gte; 25665 break; 25666 case TOK_INSTANCEOF: 25667 opcode = OP_instanceof; 25668 break; 25669 case TOK_IN: 25670 if (parse_flags & PF_IN_ACCEPTED) { 25671 opcode = OP_in; 25672 } else { 25673 return 0; 25674 } 25675 break; 25676 default: 25677 return 0; 25678 } 25679 break; 25680 case 5: 25681 switch(op) { 25682 case TOK_EQ: 25683 opcode = OP_eq; 25684 break; 25685 case TOK_NEQ: 25686 opcode = OP_neq; 25687 break; 25688 case TOK_STRICT_EQ: 25689 opcode = OP_strict_eq; 25690 break; 25691 case TOK_STRICT_NEQ: 25692 opcode = OP_strict_neq; 25693 break; 25694 default: 25695 return 0; 25696 } 25697 break; 25698 case 6: 25699 switch(op) { 25700 case '&': 25701 opcode = OP_and; 25702 break; 25703 default: 25704 return 0; 25705 } 25706 break; 25707 case 7: 25708 switch(op) { 25709 case '^': 25710 opcode = OP_xor; 25711 break; 25712 default: 25713 return 0; 25714 } 25715 break; 25716 case 8: 25717 switch(op) { 25718 case '|': 25719 opcode = OP_or; 25720 break; 25721 default: 25722 return 0; 25723 } 25724 break; 25725 default: 25726 abort(); 25727 } 25728 if (next_token(s)) 25729 return -1; 25730 if (js_parse_expr_binary(s, level - 1, parse_flags)) 25731 return -1; 25732 emit_op(s, opcode); 25733 } 25734 return 0; 25735 } 25736 25737 /* allowed parse_flags: PF_IN_ACCEPTED */ 25738 static __exception int js_parse_logical_and_or(JSParseState *s, int op, 25739 int parse_flags) 25740 { 25741 int label1; 25742 25743 if (op == TOK_LAND) { 25744 if (js_parse_expr_binary(s, 8, parse_flags)) 25745 return -1; 25746 } else { 25747 if (js_parse_logical_and_or(s, TOK_LAND, parse_flags)) 25748 return -1; 25749 } 25750 if (s->token.val == op) { 25751 label1 = new_label(s); 25752 25753 for(;;) { 25754 if (next_token(s)) 25755 return -1; 25756 emit_op(s, OP_dup); 25757 emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1); 25758 emit_op(s, OP_drop); 25759 25760 if (op == TOK_LAND) { 25761 if (js_parse_expr_binary(s, 8, parse_flags)) 25762 return -1; 25763 } else { 25764 if (js_parse_logical_and_or(s, TOK_LAND, 25765 parse_flags)) 25766 return -1; 25767 } 25768 if (s->token.val != op) { 25769 if (s->token.val == TOK_DOUBLE_QUESTION_MARK) 25770 return js_parse_error(s, "cannot mix ?? with && or ||"); 25771 break; 25772 } 25773 } 25774 25775 emit_label(s, label1); 25776 } 25777 return 0; 25778 } 25779 25780 static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) 25781 { 25782 int label1; 25783 25784 if (js_parse_logical_and_or(s, TOK_LOR, parse_flags)) 25785 return -1; 25786 if (s->token.val == TOK_DOUBLE_QUESTION_MARK) { 25787 label1 = new_label(s); 25788 for(;;) { 25789 if (next_token(s)) 25790 return -1; 25791 25792 emit_op(s, OP_dup); 25793 emit_op(s, OP_is_undefined_or_null); 25794 emit_goto(s, OP_if_false, label1); 25795 emit_op(s, OP_drop); 25796 25797 if (js_parse_expr_binary(s, 8, parse_flags)) 25798 return -1; 25799 if (s->token.val != TOK_DOUBLE_QUESTION_MARK) 25800 break; 25801 } 25802 emit_label(s, label1); 25803 } 25804 return 0; 25805 } 25806 25807 /* allowed parse_flags: PF_IN_ACCEPTED */ 25808 static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) 25809 { 25810 int label1, label2; 25811 25812 if (js_parse_coalesce_expr(s, parse_flags)) 25813 return -1; 25814 if (s->token.val == '?') { 25815 if (next_token(s)) 25816 return -1; 25817 label1 = emit_goto(s, OP_if_false, -1); 25818 25819 if (js_parse_assign_expr(s)) 25820 return -1; 25821 if (js_parse_expect(s, ':')) 25822 return -1; 25823 25824 label2 = emit_goto(s, OP_goto, -1); 25825 25826 emit_label(s, label1); 25827 25828 if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED)) 25829 return -1; 25830 25831 emit_label(s, label2); 25832 } 25833 return 0; 25834 } 25835 25836 static void emit_return(JSParseState *s, BOOL hasval); 25837 25838 /* allowed parse_flags: PF_IN_ACCEPTED */ 25839 static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) 25840 { 25841 int opcode, op, scope; 25842 JSAtom name0 = JS_ATOM_NULL; 25843 JSAtom name; 25844 25845 if (s->token.val == TOK_YIELD) { 25846 BOOL is_star = FALSE, is_async; 25847 25848 if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR)) 25849 return js_parse_error(s, "unexpected 'yield' keyword"); 25850 if (!s->cur_func->in_function_body) 25851 return js_parse_error(s, "yield in default expression"); 25852 if (next_token(s)) 25853 return -1; 25854 /* XXX: is there a better method to detect 'yield' without 25855 parameters ? */ 25856 if (s->token.val != ';' && s->token.val != ')' && 25857 s->token.val != ']' && s->token.val != '}' && 25858 s->token.val != ',' && s->token.val != ':' && !s->got_lf) { 25859 if (s->token.val == '*') { 25860 is_star = TRUE; 25861 if (next_token(s)) 25862 return -1; 25863 } 25864 if (js_parse_assign_expr2(s, parse_flags)) 25865 return -1; 25866 } else { 25867 emit_op(s, OP_undefined); 25868 } 25869 is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR); 25870 25871 if (is_star) { 25872 int label_loop, label_return, label_next; 25873 int label_return1, label_yield, label_throw, label_throw1; 25874 int label_throw2; 25875 25876 label_loop = new_label(s); 25877 label_yield = new_label(s); 25878 25879 emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start); 25880 25881 /* remove the catch offset (XXX: could avoid pushing back 25882 undefined) */ 25883 emit_op(s, OP_drop); 25884 emit_op(s, OP_undefined); 25885 25886 emit_op(s, OP_undefined); /* initial value */ 25887 25888 emit_label(s, label_loop); 25889 emit_op(s, OP_iterator_next); 25890 if (is_async) 25891 emit_op(s, OP_await); 25892 emit_op(s, OP_iterator_check_object); 25893 emit_op(s, OP_get_field2); 25894 emit_atom(s, JS_ATOM_done); 25895 label_next = emit_goto(s, OP_if_true, -1); /* end of loop */ 25896 emit_label(s, label_yield); 25897 if (is_async) { 25898 /* OP_async_yield_star takes the value as parameter */ 25899 emit_op(s, OP_get_field); 25900 emit_atom(s, JS_ATOM_value); 25901 emit_op(s, OP_async_yield_star); 25902 } else { 25903 /* OP_yield_star takes (value, done) as parameter */ 25904 emit_op(s, OP_yield_star); 25905 } 25906 emit_op(s, OP_dup); 25907 label_return = emit_goto(s, OP_if_true, -1); 25908 emit_op(s, OP_drop); 25909 emit_goto(s, OP_goto, label_loop); 25910 25911 emit_label(s, label_return); 25912 emit_op(s, OP_push_i32); 25913 emit_u32(s, 2); 25914 emit_op(s, OP_strict_eq); 25915 label_throw = emit_goto(s, OP_if_true, -1); 25916 25917 /* return handling */ 25918 if (is_async) 25919 emit_op(s, OP_await); 25920 emit_op(s, OP_iterator_call); 25921 emit_u8(s, 0); 25922 label_return1 = emit_goto(s, OP_if_true, -1); 25923 if (is_async) 25924 emit_op(s, OP_await); 25925 emit_op(s, OP_iterator_check_object); 25926 emit_op(s, OP_get_field2); 25927 emit_atom(s, JS_ATOM_done); 25928 emit_goto(s, OP_if_false, label_yield); 25929 25930 emit_op(s, OP_get_field); 25931 emit_atom(s, JS_ATOM_value); 25932 25933 emit_label(s, label_return1); 25934 emit_op(s, OP_nip); 25935 emit_op(s, OP_nip); 25936 emit_op(s, OP_nip); 25937 emit_return(s, TRUE); 25938 25939 /* throw handling */ 25940 emit_label(s, label_throw); 25941 emit_op(s, OP_iterator_call); 25942 emit_u8(s, 1); 25943 label_throw1 = emit_goto(s, OP_if_true, -1); 25944 if (is_async) 25945 emit_op(s, OP_await); 25946 emit_op(s, OP_iterator_check_object); 25947 emit_op(s, OP_get_field2); 25948 emit_atom(s, JS_ATOM_done); 25949 emit_goto(s, OP_if_false, label_yield); 25950 emit_goto(s, OP_goto, label_next); 25951 /* close the iterator and throw a type error exception */ 25952 emit_label(s, label_throw1); 25953 emit_op(s, OP_iterator_call); 25954 emit_u8(s, 2); 25955 label_throw2 = emit_goto(s, OP_if_true, -1); 25956 if (is_async) 25957 emit_op(s, OP_await); 25958 emit_label(s, label_throw2); 25959 25960 emit_op(s, OP_throw_error); 25961 emit_atom(s, JS_ATOM_NULL); 25962 emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW); 25963 25964 emit_label(s, label_next); 25965 emit_op(s, OP_get_field); 25966 emit_atom(s, JS_ATOM_value); 25967 emit_op(s, OP_nip); /* keep the value associated with 25968 done = true */ 25969 emit_op(s, OP_nip); 25970 emit_op(s, OP_nip); 25971 } else { 25972 int label_next; 25973 25974 if (is_async) 25975 emit_op(s, OP_await); 25976 emit_op(s, OP_yield); 25977 label_next = emit_goto(s, OP_if_false, -1); 25978 emit_return(s, TRUE); 25979 emit_label(s, label_next); 25980 } 25981 return 0; 25982 } else if (s->token.val == '(' && 25983 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { 25984 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 25985 JS_FUNC_NORMAL, JS_ATOM_NULL, 25986 s->token.ptr, s->token.line_num); 25987 } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { 25988 const uint8_t *source_ptr; 25989 int source_line_num, tok; 25990 JSParsePos pos; 25991 25992 /* fast test */ 25993 tok = peek_token(s, TRUE); 25994 if (tok == TOK_FUNCTION || tok == '\n') 25995 goto next; 25996 25997 source_ptr = s->token.ptr; 25998 source_line_num = s->token.line_num; 25999 js_parse_get_pos(s, &pos); 26000 if (next_token(s)) 26001 return -1; 26002 if ((s->token.val == '(' && 26003 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || 26004 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && 26005 peek_token(s, TRUE) == TOK_ARROW)) { 26006 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 26007 JS_FUNC_ASYNC, JS_ATOM_NULL, 26008 source_ptr, source_line_num); 26009 } else { 26010 /* undo the token parsing */ 26011 if (js_parse_seek_token(s, &pos)) 26012 return -1; 26013 } 26014 } else if (s->token.val == TOK_IDENT && 26015 peek_token(s, TRUE) == TOK_ARROW) { 26016 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, 26017 JS_FUNC_NORMAL, JS_ATOM_NULL, 26018 s->token.ptr, s->token.line_num); 26019 } 26020 next: 26021 if (s->token.val == TOK_IDENT) { 26022 /* name0 is used to check for OP_set_name pattern, not duplicated */ 26023 name0 = s->token.u.ident.atom; 26024 } 26025 if (js_parse_cond_expr(s, parse_flags)) 26026 return -1; 26027 26028 op = s->token.val; 26029 if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) { 26030 int label; 26031 if (next_token(s)) 26032 return -1; 26033 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0) 26034 return -1; 26035 26036 if (js_parse_assign_expr2(s, parse_flags)) { 26037 JS_FreeAtom(s->ctx, name); 26038 return -1; 26039 } 26040 26041 if (op == '=') { 26042 if (opcode == OP_get_ref_value && name == name0) { 26043 set_object_name(s, name); 26044 } 26045 } else { 26046 static const uint8_t assign_opcodes[] = { 26047 OP_mul, OP_div, OP_mod, OP_add, OP_sub, 26048 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or, 26049 #ifdef CONFIG_BIGNUM 26050 OP_pow, 26051 #endif 26052 OP_pow, 26053 }; 26054 op = assign_opcodes[op - TOK_MUL_ASSIGN]; 26055 #ifdef CONFIG_BIGNUM 26056 if (s->cur_func->js_mode & JS_MODE_MATH) { 26057 if (op == OP_mod) 26058 op = OP_math_mod; 26059 } 26060 #endif 26061 emit_op(s, op); 26062 } 26063 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE); 26064 } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) { 26065 int label, label1, depth_lvalue, label2; 26066 26067 if (next_token(s)) 26068 return -1; 26069 if (get_lvalue(s, &opcode, &scope, &name, &label, 26070 &depth_lvalue, TRUE, op) < 0) 26071 return -1; 26072 26073 emit_op(s, OP_dup); 26074 if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN) 26075 emit_op(s, OP_is_undefined_or_null); 26076 label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false, 26077 -1); 26078 emit_op(s, OP_drop); 26079 26080 if (js_parse_assign_expr2(s, parse_flags)) { 26081 JS_FreeAtom(s->ctx, name); 26082 return -1; 26083 } 26084 26085 if (opcode == OP_get_ref_value && name == name0) { 26086 set_object_name(s, name); 26087 } 26088 26089 switch(depth_lvalue) { 26090 case 1: 26091 emit_op(s, OP_insert2); 26092 break; 26093 case 2: 26094 emit_op(s, OP_insert3); 26095 break; 26096 case 3: 26097 emit_op(s, OP_insert4); 26098 break; 26099 default: 26100 abort(); 26101 } 26102 26103 /* XXX: we disable the OP_put_ref_value optimization by not 26104 using put_lvalue() otherwise depth_lvalue is not correct */ 26105 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH, 26106 FALSE); 26107 label2 = emit_goto(s, OP_goto, -1); 26108 26109 emit_label(s, label1); 26110 26111 /* remove the lvalue stack entries */ 26112 while (depth_lvalue != 0) { 26113 emit_op(s, OP_nip); 26114 depth_lvalue--; 26115 } 26116 26117 emit_label(s, label2); 26118 } 26119 return 0; 26120 } 26121 26122 static __exception int js_parse_assign_expr(JSParseState *s) 26123 { 26124 return js_parse_assign_expr2(s, PF_IN_ACCEPTED); 26125 } 26126 26127 /* allowed parse_flags: PF_IN_ACCEPTED */ 26128 static __exception int js_parse_expr2(JSParseState *s, int parse_flags) 26129 { 26130 BOOL comma = FALSE; 26131 for(;;) { 26132 if (js_parse_assign_expr2(s, parse_flags)) 26133 return -1; 26134 if (comma) { 26135 /* prevent get_lvalue from using the last expression 26136 as an lvalue. This also prevents the conversion of 26137 of get_var to get_ref for method lookup in function 26138 call inside `with` statement. 26139 */ 26140 s->cur_func->last_opcode_pos = -1; 26141 } 26142 if (s->token.val != ',') 26143 break; 26144 comma = TRUE; 26145 if (next_token(s)) 26146 return -1; 26147 emit_op(s, OP_drop); 26148 } 26149 return 0; 26150 } 26151 26152 static __exception int js_parse_expr(JSParseState *s) 26153 { 26154 return js_parse_expr2(s, PF_IN_ACCEPTED); 26155 } 26156 26157 static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, 26158 JSAtom label_name, 26159 int label_break, int label_cont, 26160 int drop_count) 26161 { 26162 be->prev = fd->top_break; 26163 fd->top_break = be; 26164 be->label_name = label_name; 26165 be->label_break = label_break; 26166 be->label_cont = label_cont; 26167 be->drop_count = drop_count; 26168 be->label_finally = -1; 26169 be->scope_level = fd->scope_level; 26170 be->has_iterator = FALSE; 26171 } 26172 26173 static void pop_break_entry(JSFunctionDef *fd) 26174 { 26175 BlockEnv *be; 26176 be = fd->top_break; 26177 fd->top_break = be->prev; 26178 } 26179 26180 static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) 26181 { 26182 BlockEnv *top; 26183 int i, scope_level; 26184 26185 scope_level = s->cur_func->scope_level; 26186 top = s->cur_func->top_break; 26187 while (top != NULL) { 26188 close_scopes(s, scope_level, top->scope_level); 26189 scope_level = top->scope_level; 26190 if (is_cont && 26191 top->label_cont != -1 && 26192 (name == JS_ATOM_NULL || top->label_name == name)) { 26193 /* continue stays inside the same block */ 26194 emit_goto(s, OP_goto, top->label_cont); 26195 return 0; 26196 } 26197 if (!is_cont && 26198 top->label_break != -1 && 26199 (name == JS_ATOM_NULL || top->label_name == name)) { 26200 emit_goto(s, OP_goto, top->label_break); 26201 return 0; 26202 } 26203 i = 0; 26204 if (top->has_iterator) { 26205 emit_op(s, OP_iterator_close); 26206 i += 3; 26207 } 26208 for(; i < top->drop_count; i++) 26209 emit_op(s, OP_drop); 26210 if (top->label_finally != -1) { 26211 /* must push dummy value to keep same stack depth */ 26212 emit_op(s, OP_undefined); 26213 emit_goto(s, OP_gosub, top->label_finally); 26214 emit_op(s, OP_drop); 26215 } 26216 top = top->prev; 26217 } 26218 if (name == JS_ATOM_NULL) { 26219 if (is_cont) 26220 return js_parse_error(s, "continue must be inside loop"); 26221 else 26222 return js_parse_error(s, "break must be inside loop or switch"); 26223 } else { 26224 return js_parse_error(s, "break/continue label not found"); 26225 } 26226 } 26227 26228 /* execute the finally blocks before return */ 26229 static void emit_return(JSParseState *s, BOOL hasval) 26230 { 26231 BlockEnv *top; 26232 26233 if (s->cur_func->func_kind != JS_FUNC_NORMAL) { 26234 if (!hasval) { 26235 /* no value: direct return in case of async generator */ 26236 emit_op(s, OP_undefined); 26237 hasval = TRUE; 26238 } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { 26239 /* the await must be done before handling the "finally" in 26240 case it raises an exception */ 26241 emit_op(s, OP_await); 26242 } 26243 } 26244 26245 top = s->cur_func->top_break; 26246 while (top != NULL) { 26247 if (top->has_iterator || top->label_finally != -1) { 26248 if (!hasval) { 26249 emit_op(s, OP_undefined); 26250 hasval = TRUE; 26251 } 26252 /* Remove the stack elements up to and including the catch 26253 offset. When 'yield' is used in an expression we have 26254 no easy way to count them, so we use this specific 26255 instruction instead. */ 26256 emit_op(s, OP_nip_catch); 26257 /* stack: iter_obj next ret_val */ 26258 if (top->has_iterator) { 26259 if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { 26260 int label_next, label_next2; 26261 emit_op(s, OP_nip); /* next */ 26262 emit_op(s, OP_swap); 26263 emit_op(s, OP_get_field2); 26264 emit_atom(s, JS_ATOM_return); 26265 /* stack: iter_obj return_func */ 26266 emit_op(s, OP_dup); 26267 emit_op(s, OP_is_undefined_or_null); 26268 label_next = emit_goto(s, OP_if_true, -1); 26269 emit_op(s, OP_call_method); 26270 emit_u16(s, 0); 26271 emit_op(s, OP_iterator_check_object); 26272 emit_op(s, OP_await); 26273 label_next2 = emit_goto(s, OP_goto, -1); 26274 emit_label(s, label_next); 26275 emit_op(s, OP_drop); 26276 emit_label(s, label_next2); 26277 emit_op(s, OP_drop); 26278 } else { 26279 emit_op(s, OP_rot3r); 26280 emit_op(s, OP_undefined); /* dummy catch offset */ 26281 emit_op(s, OP_iterator_close); 26282 } 26283 } else { 26284 /* execute the "finally" block */ 26285 emit_goto(s, OP_gosub, top->label_finally); 26286 } 26287 } 26288 top = top->prev; 26289 } 26290 if (s->cur_func->is_derived_class_constructor) { 26291 int label_return; 26292 26293 /* 'this' can be uninitialized, so it may be accessed only if 26294 the derived class constructor does not return an object */ 26295 if (hasval) { 26296 emit_op(s, OP_check_ctor_return); 26297 label_return = emit_goto(s, OP_if_false, -1); 26298 emit_op(s, OP_drop); 26299 } else { 26300 label_return = -1; 26301 } 26302 26303 /* The error should be raised in the caller context, so we use 26304 a specific opcode */ 26305 emit_op(s, OP_scope_get_var_checkthis); 26306 emit_atom(s, JS_ATOM_this); 26307 emit_u16(s, 0); 26308 26309 emit_label(s, label_return); 26310 emit_op(s, OP_return); 26311 } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { 26312 emit_op(s, OP_return_async); 26313 } else { 26314 emit_op(s, hasval ? OP_return : OP_return_undef); 26315 } 26316 } 26317 26318 #define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */ 26319 /* ored with DECL_MASK_FUNC if function declarations are allowed with a label */ 26320 #define DECL_MASK_FUNC_WITH_LABEL (1 << 1) 26321 #define DECL_MASK_OTHER (1 << 2) /* all other declarations */ 26322 #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) 26323 26324 static __exception int js_parse_statement_or_decl(JSParseState *s, 26325 int decl_mask); 26326 26327 static __exception int js_parse_statement(JSParseState *s) 26328 { 26329 return js_parse_statement_or_decl(s, 0); 26330 } 26331 26332 static __exception int js_parse_block(JSParseState *s) 26333 { 26334 if (js_parse_expect(s, '{')) 26335 return -1; 26336 if (s->token.val != '}') { 26337 push_scope(s); 26338 for(;;) { 26339 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 26340 return -1; 26341 if (s->token.val == '}') 26342 break; 26343 } 26344 pop_scope(s); 26345 } 26346 if (next_token(s)) 26347 return -1; 26348 return 0; 26349 } 26350 26351 /* allowed parse_flags: PF_IN_ACCEPTED */ 26352 static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, 26353 BOOL export_flag) 26354 { 26355 JSContext *ctx = s->ctx; 26356 JSFunctionDef *fd = s->cur_func; 26357 JSAtom name = JS_ATOM_NULL; 26358 26359 for (;;) { 26360 if (s->token.val == TOK_IDENT) { 26361 if (s->token.u.ident.is_reserved) { 26362 return js_parse_error_reserved_identifier(s); 26363 } 26364 name = JS_DupAtom(ctx, s->token.u.ident.atom); 26365 if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) { 26366 js_parse_error(s, "'let' is not a valid lexical identifier"); 26367 goto var_error; 26368 } 26369 if (next_token(s)) 26370 goto var_error; 26371 if (js_define_var(s, name, tok)) 26372 goto var_error; 26373 if (export_flag) { 26374 if (!add_export_entry(s, s->cur_func->module, name, name, 26375 JS_EXPORT_TYPE_LOCAL)) 26376 goto var_error; 26377 } 26378 26379 if (s->token.val == '=') { 26380 if (next_token(s)) 26381 goto var_error; 26382 if (tok == TOK_VAR) { 26383 /* Must make a reference for proper `with` semantics */ 26384 int opcode, scope, label; 26385 JSAtom name1; 26386 26387 emit_op(s, OP_scope_get_var); 26388 emit_atom(s, name); 26389 emit_u16(s, fd->scope_level); 26390 if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0) 26391 goto var_error; 26392 if (js_parse_assign_expr2(s, parse_flags)) { 26393 JS_FreeAtom(ctx, name1); 26394 goto var_error; 26395 } 26396 set_object_name(s, name); 26397 put_lvalue(s, opcode, scope, name1, label, 26398 PUT_LVALUE_NOKEEP, FALSE); 26399 } else { 26400 if (js_parse_assign_expr2(s, parse_flags)) 26401 goto var_error; 26402 set_object_name(s, name); 26403 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? 26404 OP_scope_put_var_init : OP_scope_put_var); 26405 emit_atom(s, name); 26406 emit_u16(s, fd->scope_level); 26407 } 26408 } else { 26409 if (tok == TOK_CONST) { 26410 js_parse_error(s, "missing initializer for const variable"); 26411 goto var_error; 26412 } 26413 if (tok == TOK_LET) { 26414 /* initialize lexical variable upon entering its scope */ 26415 emit_op(s, OP_undefined); 26416 emit_op(s, OP_scope_put_var_init); 26417 emit_atom(s, name); 26418 emit_u16(s, fd->scope_level); 26419 } 26420 } 26421 JS_FreeAtom(ctx, name); 26422 } else { 26423 int skip_bits; 26424 if ((s->token.val == '[' || s->token.val == '{') 26425 && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') { 26426 emit_op(s, OP_undefined); 26427 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) 26428 return -1; 26429 } else { 26430 return js_parse_error(s, "variable name expected"); 26431 } 26432 } 26433 if (s->token.val != ',') 26434 break; 26435 if (next_token(s)) 26436 return -1; 26437 } 26438 return 0; 26439 26440 var_error: 26441 JS_FreeAtom(ctx, name); 26442 return -1; 26443 } 26444 26445 /* test if the current token is a label. Use simplistic look-ahead scanner */ 26446 static BOOL is_label(JSParseState *s) 26447 { 26448 return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && 26449 peek_token(s, FALSE) == ':'); 26450 } 26451 26452 /* test if the current token is a let keyword. Use simplistic look-ahead scanner */ 26453 static int is_let(JSParseState *s, int decl_mask) 26454 { 26455 int res = FALSE; 26456 26457 if (token_is_pseudo_keyword(s, JS_ATOM_let)) { 26458 JSParsePos pos; 26459 js_parse_get_pos(s, &pos); 26460 for (;;) { 26461 if (next_token(s)) { 26462 res = -1; 26463 break; 26464 } 26465 if (s->token.val == '[') { 26466 /* let [ is a syntax restriction: 26467 it never introduces an ExpressionStatement */ 26468 res = TRUE; 26469 break; 26470 } 26471 if (s->token.val == '{' || 26472 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) || 26473 s->token.val == TOK_LET || 26474 s->token.val == TOK_YIELD || 26475 s->token.val == TOK_AWAIT) { 26476 /* Check for possible ASI if not scanning for Declaration */ 26477 /* XXX: should also check that `{` introduces a BindingPattern, 26478 but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */ 26479 if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) { 26480 res = TRUE; 26481 break; 26482 } 26483 break; 26484 } 26485 break; 26486 } 26487 if (js_parse_seek_token(s, &pos)) { 26488 res = -1; 26489 } 26490 } 26491 return res; 26492 } 26493 26494 /* XXX: handle IteratorClose when exiting the loop before the 26495 enumeration is done */ 26496 static __exception int js_parse_for_in_of(JSParseState *s, int label_name, 26497 BOOL is_async) 26498 { 26499 JSContext *ctx = s->ctx; 26500 JSFunctionDef *fd = s->cur_func; 26501 JSAtom var_name; 26502 BOOL has_initializer, is_for_of, has_destructuring; 26503 int tok, tok1, opcode, scope, block_scope_level; 26504 int label_next, label_expr, label_cont, label_body, label_break; 26505 int pos_next, pos_expr; 26506 BlockEnv break_entry; 26507 26508 has_initializer = FALSE; 26509 has_destructuring = FALSE; 26510 is_for_of = FALSE; 26511 block_scope_level = fd->scope_level; 26512 label_cont = new_label(s); 26513 label_body = new_label(s); 26514 label_break = new_label(s); 26515 label_next = new_label(s); 26516 26517 /* create scope for the lexical variables declared in the enumeration 26518 expressions. XXX: Not completely correct because of weird capturing 26519 semantics in `for (i of o) a.push(function(){return i})` */ 26520 push_scope(s); 26521 26522 /* local for_in scope starts here so individual elements 26523 can be closed in statement. */ 26524 push_break_entry(s->cur_func, &break_entry, 26525 label_name, label_break, label_cont, 1); 26526 break_entry.scope_level = block_scope_level; 26527 26528 label_expr = emit_goto(s, OP_goto, -1); 26529 26530 pos_next = s->cur_func->byte_code.size; 26531 emit_label(s, label_next); 26532 26533 tok = s->token.val; 26534 switch (is_let(s, DECL_MASK_OTHER)) { 26535 case TRUE: 26536 tok = TOK_LET; 26537 break; 26538 case FALSE: 26539 break; 26540 default: 26541 return -1; 26542 } 26543 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { 26544 if (next_token(s)) 26545 return -1; 26546 26547 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { 26548 if (s->token.val == '[' || s->token.val == '{') { 26549 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0) 26550 return -1; 26551 has_destructuring = TRUE; 26552 } else { 26553 return js_parse_error(s, "variable name expected"); 26554 } 26555 var_name = JS_ATOM_NULL; 26556 } else { 26557 var_name = JS_DupAtom(ctx, s->token.u.ident.atom); 26558 if (next_token(s)) { 26559 JS_FreeAtom(s->ctx, var_name); 26560 return -1; 26561 } 26562 if (js_define_var(s, var_name, tok)) { 26563 JS_FreeAtom(s->ctx, var_name); 26564 return -1; 26565 } 26566 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ? 26567 OP_scope_put_var_init : OP_scope_put_var); 26568 emit_atom(s, var_name); 26569 emit_u16(s, fd->scope_level); 26570 } 26571 } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && 26572 peek_token(s, FALSE) == TOK_OF) { 26573 return js_parse_error(s, "'for of' expression cannot start with 'async'"); 26574 } else { 26575 int skip_bits; 26576 if ((s->token.val == '[' || s->token.val == '{') 26577 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) { 26578 if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0) 26579 return -1; 26580 } else { 26581 int lvalue_label; 26582 if (js_parse_left_hand_side_expr(s)) 26583 return -1; 26584 if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label, 26585 NULL, FALSE, TOK_FOR)) 26586 return -1; 26587 put_lvalue(s, opcode, scope, var_name, lvalue_label, 26588 PUT_LVALUE_NOKEEP_BOTTOM, FALSE); 26589 } 26590 var_name = JS_ATOM_NULL; 26591 } 26592 emit_goto(s, OP_goto, label_body); 26593 26594 pos_expr = s->cur_func->byte_code.size; 26595 emit_label(s, label_expr); 26596 if (s->token.val == '=') { 26597 /* XXX: potential scoping issue if inside `with` statement */ 26598 has_initializer = TRUE; 26599 /* parse and evaluate initializer prior to evaluating the 26600 object (only used with "for in" with a non lexical variable 26601 in non strict mode */ 26602 if (next_token(s) || js_parse_assign_expr2(s, 0)) { 26603 JS_FreeAtom(ctx, var_name); 26604 return -1; 26605 } 26606 if (var_name != JS_ATOM_NULL) { 26607 emit_op(s, OP_scope_put_var); 26608 emit_atom(s, var_name); 26609 emit_u16(s, fd->scope_level); 26610 } 26611 } 26612 JS_FreeAtom(ctx, var_name); 26613 26614 if (token_is_pseudo_keyword(s, JS_ATOM_of)) { 26615 break_entry.has_iterator = is_for_of = TRUE; 26616 break_entry.drop_count += 2; 26617 if (has_initializer) 26618 goto initializer_error; 26619 } else if (s->token.val == TOK_IN) { 26620 if (is_async) 26621 return js_parse_error(s, "'for await' loop should be used with 'of'"); 26622 if (has_initializer && 26623 (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) || 26624 has_destructuring)) { 26625 initializer_error: 26626 return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer", 26627 is_for_of ? "of" : "in"); 26628 } 26629 } else { 26630 return js_parse_error(s, "expected 'of' or 'in' in for control expression"); 26631 } 26632 if (next_token(s)) 26633 return -1; 26634 if (is_for_of) { 26635 if (js_parse_assign_expr(s)) 26636 return -1; 26637 } else { 26638 if (js_parse_expr(s)) 26639 return -1; 26640 } 26641 /* close the scope after having evaluated the expression so that 26642 the TDZ values are in the closures */ 26643 close_scopes(s, s->cur_func->scope_level, block_scope_level); 26644 if (is_for_of) { 26645 if (is_async) 26646 emit_op(s, OP_for_await_of_start); 26647 else 26648 emit_op(s, OP_for_of_start); 26649 /* on stack: enum_rec */ 26650 } else { 26651 emit_op(s, OP_for_in_start); 26652 /* on stack: enum_obj */ 26653 } 26654 emit_goto(s, OP_goto, label_cont); 26655 26656 if (js_parse_expect(s, ')')) 26657 return -1; 26658 26659 if (OPTIMIZE) { 26660 /* move the `next` code here */ 26661 DynBuf *bc = &s->cur_func->byte_code; 26662 int chunk_size = pos_expr - pos_next; 26663 int offset = bc->size - pos_next; 26664 int i; 26665 dbuf_realloc(bc, bc->size + chunk_size); 26666 dbuf_put(bc, bc->buf + pos_next, chunk_size); 26667 memset(bc->buf + pos_next, OP_nop, chunk_size); 26668 /* `next` part ends with a goto */ 26669 s->cur_func->last_opcode_pos = bc->size - 5; 26670 /* relocate labels */ 26671 for (i = label_cont; i < s->cur_func->label_count; i++) { 26672 LabelSlot *ls = &s->cur_func->label_slots[i]; 26673 if (ls->pos >= pos_next && ls->pos < pos_expr) 26674 ls->pos += offset; 26675 } 26676 } 26677 26678 emit_label(s, label_body); 26679 if (js_parse_statement(s)) 26680 return -1; 26681 26682 close_scopes(s, s->cur_func->scope_level, block_scope_level); 26683 26684 emit_label(s, label_cont); 26685 if (is_for_of) { 26686 if (is_async) { 26687 /* call the next method */ 26688 /* stack: iter_obj next catch_offset */ 26689 emit_op(s, OP_dup3); 26690 emit_op(s, OP_drop); 26691 emit_op(s, OP_call_method); 26692 emit_u16(s, 0); 26693 /* get the result of the promise */ 26694 emit_op(s, OP_await); 26695 /* unwrap the value and done values */ 26696 emit_op(s, OP_iterator_get_value_done); 26697 } else { 26698 emit_op(s, OP_for_of_next); 26699 emit_u8(s, 0); 26700 } 26701 } else { 26702 emit_op(s, OP_for_in_next); 26703 } 26704 /* on stack: enum_rec / enum_obj value bool */ 26705 emit_goto(s, OP_if_false, label_next); 26706 /* drop the undefined value from for_xx_next */ 26707 emit_op(s, OP_drop); 26708 26709 emit_label(s, label_break); 26710 if (is_for_of) { 26711 /* close and drop enum_rec */ 26712 emit_op(s, OP_iterator_close); 26713 } else { 26714 emit_op(s, OP_drop); 26715 } 26716 pop_break_entry(s->cur_func); 26717 pop_scope(s); 26718 return 0; 26719 } 26720 26721 static void set_eval_ret_undefined(JSParseState *s) 26722 { 26723 if (s->cur_func->eval_ret_idx >= 0) { 26724 emit_op(s, OP_undefined); 26725 emit_op(s, OP_put_loc); 26726 emit_u16(s, s->cur_func->eval_ret_idx); 26727 } 26728 } 26729 26730 static __exception int js_parse_statement_or_decl(JSParseState *s, 26731 int decl_mask) 26732 { 26733 JSContext *ctx = s->ctx; 26734 JSAtom label_name; 26735 int tok; 26736 26737 /* specific label handling */ 26738 /* XXX: support multiple labels on loop statements */ 26739 label_name = JS_ATOM_NULL; 26740 if (is_label(s)) { 26741 BlockEnv *be; 26742 26743 label_name = JS_DupAtom(ctx, s->token.u.ident.atom); 26744 26745 for (be = s->cur_func->top_break; be; be = be->prev) { 26746 if (be->label_name == label_name) { 26747 js_parse_error(s, "duplicate label name"); 26748 goto fail; 26749 } 26750 } 26751 26752 if (next_token(s)) 26753 goto fail; 26754 if (js_parse_expect(s, ':')) 26755 goto fail; 26756 if (s->token.val != TOK_FOR 26757 && s->token.val != TOK_DO 26758 && s->token.val != TOK_WHILE) { 26759 /* labelled regular statement */ 26760 int label_break, mask; 26761 BlockEnv break_entry; 26762 26763 label_break = new_label(s); 26764 push_break_entry(s->cur_func, &break_entry, 26765 label_name, label_break, -1, 0); 26766 if (!(s->cur_func->js_mode & JS_MODE_STRICT) && 26767 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) { 26768 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL; 26769 } else { 26770 mask = 0; 26771 } 26772 if (js_parse_statement_or_decl(s, mask)) 26773 goto fail; 26774 emit_label(s, label_break); 26775 pop_break_entry(s->cur_func); 26776 goto done; 26777 } 26778 } 26779 26780 switch(tok = s->token.val) { 26781 case '{': 26782 if (js_parse_block(s)) 26783 goto fail; 26784 break; 26785 case TOK_RETURN: 26786 if (s->cur_func->is_eval) { 26787 js_parse_error(s, "return not in a function"); 26788 goto fail; 26789 } 26790 if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { 26791 js_parse_error(s, "return in a static initializer block"); 26792 goto fail; 26793 } 26794 if (next_token(s)) 26795 goto fail; 26796 if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { 26797 if (js_parse_expr(s)) 26798 goto fail; 26799 emit_return(s, TRUE); 26800 } else { 26801 emit_return(s, FALSE); 26802 } 26803 if (js_parse_expect_semi(s)) 26804 goto fail; 26805 break; 26806 case TOK_THROW: 26807 if (next_token(s)) 26808 goto fail; 26809 if (s->got_lf) { 26810 js_parse_error(s, "line terminator not allowed after throw"); 26811 goto fail; 26812 } 26813 if (js_parse_expr(s)) 26814 goto fail; 26815 emit_op(s, OP_throw); 26816 if (js_parse_expect_semi(s)) 26817 goto fail; 26818 break; 26819 case TOK_LET: 26820 case TOK_CONST: 26821 haslet: 26822 if (!(decl_mask & DECL_MASK_OTHER)) { 26823 js_parse_error(s, "lexical declarations can't appear in single-statement context"); 26824 goto fail; 26825 } 26826 /* fall thru */ 26827 case TOK_VAR: 26828 if (next_token(s)) 26829 goto fail; 26830 if (js_parse_var(s, TRUE, tok, FALSE)) 26831 goto fail; 26832 if (js_parse_expect_semi(s)) 26833 goto fail; 26834 break; 26835 case TOK_IF: 26836 { 26837 int label1, label2, mask; 26838 if (next_token(s)) 26839 goto fail; 26840 /* create a new scope for `let f;if(1) function f(){}` */ 26841 push_scope(s); 26842 set_eval_ret_undefined(s); 26843 if (js_parse_expr_paren(s)) 26844 goto fail; 26845 label1 = emit_goto(s, OP_if_false, -1); 26846 if (s->cur_func->js_mode & JS_MODE_STRICT) 26847 mask = 0; 26848 else 26849 mask = DECL_MASK_FUNC; /* Annex B.3.4 */ 26850 26851 if (js_parse_statement_or_decl(s, mask)) 26852 goto fail; 26853 26854 if (s->token.val == TOK_ELSE) { 26855 label2 = emit_goto(s, OP_goto, -1); 26856 if (next_token(s)) 26857 goto fail; 26858 26859 emit_label(s, label1); 26860 if (js_parse_statement_or_decl(s, mask)) 26861 goto fail; 26862 26863 label1 = label2; 26864 } 26865 emit_label(s, label1); 26866 pop_scope(s); 26867 } 26868 break; 26869 case TOK_WHILE: 26870 { 26871 int label_cont, label_break; 26872 BlockEnv break_entry; 26873 26874 label_cont = new_label(s); 26875 label_break = new_label(s); 26876 26877 push_break_entry(s->cur_func, &break_entry, 26878 label_name, label_break, label_cont, 0); 26879 26880 if (next_token(s)) 26881 goto fail; 26882 26883 set_eval_ret_undefined(s); 26884 26885 emit_label(s, label_cont); 26886 if (js_parse_expr_paren(s)) 26887 goto fail; 26888 emit_goto(s, OP_if_false, label_break); 26889 26890 if (js_parse_statement(s)) 26891 goto fail; 26892 emit_goto(s, OP_goto, label_cont); 26893 26894 emit_label(s, label_break); 26895 26896 pop_break_entry(s->cur_func); 26897 } 26898 break; 26899 case TOK_DO: 26900 { 26901 int label_cont, label_break, label1; 26902 BlockEnv break_entry; 26903 26904 label_cont = new_label(s); 26905 label_break = new_label(s); 26906 label1 = new_label(s); 26907 26908 push_break_entry(s->cur_func, &break_entry, 26909 label_name, label_break, label_cont, 0); 26910 26911 if (next_token(s)) 26912 goto fail; 26913 26914 emit_label(s, label1); 26915 26916 set_eval_ret_undefined(s); 26917 26918 if (js_parse_statement(s)) 26919 goto fail; 26920 26921 emit_label(s, label_cont); 26922 if (js_parse_expect(s, TOK_WHILE)) 26923 goto fail; 26924 if (js_parse_expr_paren(s)) 26925 goto fail; 26926 /* Insert semicolon if missing */ 26927 if (s->token.val == ';') { 26928 if (next_token(s)) 26929 goto fail; 26930 } 26931 emit_goto(s, OP_if_true, label1); 26932 26933 emit_label(s, label_break); 26934 26935 pop_break_entry(s->cur_func); 26936 } 26937 break; 26938 case TOK_FOR: 26939 { 26940 int label_cont, label_break, label_body, label_test; 26941 int pos_cont, pos_body, block_scope_level; 26942 BlockEnv break_entry; 26943 int tok, bits; 26944 BOOL is_async; 26945 26946 if (next_token(s)) 26947 goto fail; 26948 26949 set_eval_ret_undefined(s); 26950 bits = 0; 26951 is_async = FALSE; 26952 if (s->token.val == '(') { 26953 js_parse_skip_parens_token(s, &bits, FALSE); 26954 } else if (s->token.val == TOK_AWAIT) { 26955 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) { 26956 js_parse_error(s, "for await is only valid in asynchronous functions"); 26957 goto fail; 26958 } 26959 is_async = TRUE; 26960 if (next_token(s)) 26961 goto fail; 26962 s->cur_func->has_await = TRUE; 26963 } 26964 if (js_parse_expect(s, '(')) 26965 goto fail; 26966 26967 if (!(bits & SKIP_HAS_SEMI)) { 26968 /* parse for/in or for/of */ 26969 if (js_parse_for_in_of(s, label_name, is_async)) 26970 goto fail; 26971 break; 26972 } 26973 block_scope_level = s->cur_func->scope_level; 26974 26975 /* create scope for the lexical variables declared in the initial, 26976 test and increment expressions */ 26977 push_scope(s); 26978 /* initial expression */ 26979 tok = s->token.val; 26980 if (tok != ';') { 26981 switch (is_let(s, DECL_MASK_OTHER)) { 26982 case TRUE: 26983 tok = TOK_LET; 26984 break; 26985 case FALSE: 26986 break; 26987 default: 26988 goto fail; 26989 } 26990 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) { 26991 if (next_token(s)) 26992 goto fail; 26993 if (js_parse_var(s, FALSE, tok, FALSE)) 26994 goto fail; 26995 } else { 26996 if (js_parse_expr2(s, FALSE)) 26997 goto fail; 26998 emit_op(s, OP_drop); 26999 } 27000 27001 /* close the closures before the first iteration */ 27002 close_scopes(s, s->cur_func->scope_level, block_scope_level); 27003 } 27004 if (js_parse_expect(s, ';')) 27005 goto fail; 27006 27007 label_test = new_label(s); 27008 label_cont = new_label(s); 27009 label_body = new_label(s); 27010 label_break = new_label(s); 27011 27012 push_break_entry(s->cur_func, &break_entry, 27013 label_name, label_break, label_cont, 0); 27014 27015 /* test expression */ 27016 if (s->token.val == ';') { 27017 /* no test expression */ 27018 label_test = label_body; 27019 } else { 27020 emit_label(s, label_test); 27021 if (js_parse_expr(s)) 27022 goto fail; 27023 emit_goto(s, OP_if_false, label_break); 27024 } 27025 if (js_parse_expect(s, ';')) 27026 goto fail; 27027 27028 if (s->token.val == ')') { 27029 /* no end expression */ 27030 break_entry.label_cont = label_cont = label_test; 27031 pos_cont = 0; /* avoid warning */ 27032 } else { 27033 /* skip the end expression */ 27034 emit_goto(s, OP_goto, label_body); 27035 27036 pos_cont = s->cur_func->byte_code.size; 27037 emit_label(s, label_cont); 27038 if (js_parse_expr(s)) 27039 goto fail; 27040 emit_op(s, OP_drop); 27041 if (label_test != label_body) 27042 emit_goto(s, OP_goto, label_test); 27043 } 27044 if (js_parse_expect(s, ')')) 27045 goto fail; 27046 27047 pos_body = s->cur_func->byte_code.size; 27048 emit_label(s, label_body); 27049 if (js_parse_statement(s)) 27050 goto fail; 27051 27052 /* close the closures before the next iteration */ 27053 /* XXX: check continue case */ 27054 close_scopes(s, s->cur_func->scope_level, block_scope_level); 27055 27056 if (OPTIMIZE && label_test != label_body && label_cont != label_test) { 27057 /* move the increment code here */ 27058 DynBuf *bc = &s->cur_func->byte_code; 27059 int chunk_size = pos_body - pos_cont; 27060 int offset = bc->size - pos_cont; 27061 int i; 27062 dbuf_realloc(bc, bc->size + chunk_size); 27063 dbuf_put(bc, bc->buf + pos_cont, chunk_size); 27064 memset(bc->buf + pos_cont, OP_nop, chunk_size); 27065 /* increment part ends with a goto */ 27066 s->cur_func->last_opcode_pos = bc->size - 5; 27067 /* relocate labels */ 27068 for (i = label_cont; i < s->cur_func->label_count; i++) { 27069 LabelSlot *ls = &s->cur_func->label_slots[i]; 27070 if (ls->pos >= pos_cont && ls->pos < pos_body) 27071 ls->pos += offset; 27072 } 27073 } else { 27074 emit_goto(s, OP_goto, label_cont); 27075 } 27076 27077 emit_label(s, label_break); 27078 27079 pop_break_entry(s->cur_func); 27080 pop_scope(s); 27081 } 27082 break; 27083 case TOK_BREAK: 27084 case TOK_CONTINUE: 27085 { 27086 int is_cont = s->token.val - TOK_BREAK; 27087 int label; 27088 27089 if (next_token(s)) 27090 goto fail; 27091 if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) 27092 label = s->token.u.ident.atom; 27093 else 27094 label = JS_ATOM_NULL; 27095 if (emit_break(s, label, is_cont)) 27096 goto fail; 27097 if (label != JS_ATOM_NULL) { 27098 if (next_token(s)) 27099 goto fail; 27100 } 27101 if (js_parse_expect_semi(s)) 27102 goto fail; 27103 } 27104 break; 27105 case TOK_SWITCH: 27106 { 27107 int label_case, label_break, label1; 27108 int default_label_pos; 27109 BlockEnv break_entry; 27110 27111 if (next_token(s)) 27112 goto fail; 27113 27114 set_eval_ret_undefined(s); 27115 if (js_parse_expr_paren(s)) 27116 goto fail; 27117 27118 push_scope(s); 27119 label_break = new_label(s); 27120 push_break_entry(s->cur_func, &break_entry, 27121 label_name, label_break, -1, 1); 27122 27123 if (js_parse_expect(s, '{')) 27124 goto fail; 27125 27126 default_label_pos = -1; 27127 label_case = -1; 27128 while (s->token.val != '}') { 27129 if (s->token.val == TOK_CASE) { 27130 label1 = -1; 27131 if (label_case >= 0) { 27132 /* skip the case if needed */ 27133 label1 = emit_goto(s, OP_goto, -1); 27134 } 27135 emit_label(s, label_case); 27136 label_case = -1; 27137 for (;;) { 27138 /* parse a sequence of case clauses */ 27139 if (next_token(s)) 27140 goto fail; 27141 emit_op(s, OP_dup); 27142 if (js_parse_expr(s)) 27143 goto fail; 27144 if (js_parse_expect(s, ':')) 27145 goto fail; 27146 emit_op(s, OP_strict_eq); 27147 if (s->token.val == TOK_CASE) { 27148 label1 = emit_goto(s, OP_if_true, label1); 27149 } else { 27150 label_case = emit_goto(s, OP_if_false, -1); 27151 emit_label(s, label1); 27152 break; 27153 } 27154 } 27155 } else if (s->token.val == TOK_DEFAULT) { 27156 if (next_token(s)) 27157 goto fail; 27158 if (js_parse_expect(s, ':')) 27159 goto fail; 27160 if (default_label_pos >= 0) { 27161 js_parse_error(s, "duplicate default"); 27162 goto fail; 27163 } 27164 if (label_case < 0) { 27165 /* falling thru direct from switch expression */ 27166 label_case = emit_goto(s, OP_goto, -1); 27167 } 27168 /* Emit a dummy label opcode. Label will be patched after 27169 the end of the switch body. Do not use emit_label(s, 0) 27170 because it would clobber label 0 address, preventing 27171 proper optimizer operation. 27172 */ 27173 emit_op(s, OP_label); 27174 emit_u32(s, 0); 27175 default_label_pos = s->cur_func->byte_code.size - 4; 27176 } else { 27177 if (label_case < 0) { 27178 /* falling thru direct from switch expression */ 27179 js_parse_error(s, "invalid switch statement"); 27180 goto fail; 27181 } 27182 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 27183 goto fail; 27184 } 27185 } 27186 if (js_parse_expect(s, '}')) 27187 goto fail; 27188 if (default_label_pos >= 0) { 27189 /* Ugly patch for the the `default` label, shameful and risky */ 27190 put_u32(s->cur_func->byte_code.buf + default_label_pos, 27191 label_case); 27192 s->cur_func->label_slots[label_case].pos = default_label_pos + 4; 27193 } else { 27194 emit_label(s, label_case); 27195 } 27196 emit_label(s, label_break); 27197 emit_op(s, OP_drop); /* drop the switch expression */ 27198 27199 pop_break_entry(s->cur_func); 27200 pop_scope(s); 27201 } 27202 break; 27203 case TOK_TRY: 27204 { 27205 int label_catch, label_catch2, label_finally, label_end; 27206 JSAtom name; 27207 BlockEnv block_env; 27208 27209 set_eval_ret_undefined(s); 27210 if (next_token(s)) 27211 goto fail; 27212 label_catch = new_label(s); 27213 label_catch2 = new_label(s); 27214 label_finally = new_label(s); 27215 label_end = new_label(s); 27216 27217 emit_goto(s, OP_catch, label_catch); 27218 27219 push_break_entry(s->cur_func, &block_env, 27220 JS_ATOM_NULL, -1, -1, 1); 27221 block_env.label_finally = label_finally; 27222 27223 if (js_parse_block(s)) 27224 goto fail; 27225 27226 pop_break_entry(s->cur_func); 27227 27228 if (js_is_live_code(s)) { 27229 /* drop the catch offset */ 27230 emit_op(s, OP_drop); 27231 /* must push dummy value to keep same stack size */ 27232 emit_op(s, OP_undefined); 27233 emit_goto(s, OP_gosub, label_finally); 27234 emit_op(s, OP_drop); 27235 27236 emit_goto(s, OP_goto, label_end); 27237 } 27238 27239 if (s->token.val == TOK_CATCH) { 27240 if (next_token(s)) 27241 goto fail; 27242 27243 push_scope(s); /* catch variable */ 27244 emit_label(s, label_catch); 27245 27246 if (s->token.val == '{') { 27247 /* support optional-catch-binding feature */ 27248 emit_op(s, OP_drop); /* pop the exception object */ 27249 } else { 27250 if (js_parse_expect(s, '(')) 27251 goto fail; 27252 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) { 27253 if (s->token.val == '[' || s->token.val == '{') { 27254 /* XXX: TOK_LET is not completely correct */ 27255 if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0) 27256 goto fail; 27257 } else { 27258 js_parse_error(s, "identifier expected"); 27259 goto fail; 27260 } 27261 } else { 27262 name = JS_DupAtom(ctx, s->token.u.ident.atom); 27263 if (next_token(s) 27264 || js_define_var(s, name, TOK_CATCH) < 0) { 27265 JS_FreeAtom(ctx, name); 27266 goto fail; 27267 } 27268 /* store the exception value in the catch variable */ 27269 emit_op(s, OP_scope_put_var); 27270 emit_u32(s, name); 27271 emit_u16(s, s->cur_func->scope_level); 27272 } 27273 if (js_parse_expect(s, ')')) 27274 goto fail; 27275 } 27276 /* XXX: should keep the address to nop it out if there is no finally block */ 27277 emit_goto(s, OP_catch, label_catch2); 27278 27279 push_scope(s); /* catch block */ 27280 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, 27281 -1, -1, 1); 27282 block_env.label_finally = label_finally; 27283 27284 if (js_parse_block(s)) 27285 goto fail; 27286 27287 pop_break_entry(s->cur_func); 27288 pop_scope(s); /* catch block */ 27289 pop_scope(s); /* catch variable */ 27290 27291 if (js_is_live_code(s)) { 27292 /* drop the catch2 offset */ 27293 emit_op(s, OP_drop); 27294 /* XXX: should keep the address to nop it out if there is no finally block */ 27295 /* must push dummy value to keep same stack size */ 27296 emit_op(s, OP_undefined); 27297 emit_goto(s, OP_gosub, label_finally); 27298 emit_op(s, OP_drop); 27299 emit_goto(s, OP_goto, label_end); 27300 } 27301 /* catch exceptions thrown in the catch block to execute the 27302 * finally clause and rethrow the exception */ 27303 emit_label(s, label_catch2); 27304 /* catch value is at TOS, no need to push undefined */ 27305 emit_goto(s, OP_gosub, label_finally); 27306 emit_op(s, OP_throw); 27307 27308 } else if (s->token.val == TOK_FINALLY) { 27309 /* finally without catch : execute the finally clause 27310 * and rethrow the exception */ 27311 emit_label(s, label_catch); 27312 /* catch value is at TOS, no need to push undefined */ 27313 emit_goto(s, OP_gosub, label_finally); 27314 emit_op(s, OP_throw); 27315 } else { 27316 js_parse_error(s, "expecting catch or finally"); 27317 goto fail; 27318 } 27319 emit_label(s, label_finally); 27320 if (s->token.val == TOK_FINALLY) { 27321 int saved_eval_ret_idx = 0; /* avoid warning */ 27322 27323 if (next_token(s)) 27324 goto fail; 27325 /* on the stack: ret_value gosub_ret_value */ 27326 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL, 27327 -1, -1, 2); 27328 27329 if (s->cur_func->eval_ret_idx >= 0) { 27330 /* 'finally' updates eval_ret only if not a normal 27331 termination */ 27332 saved_eval_ret_idx = 27333 add_var(s->ctx, s->cur_func, JS_ATOM__ret_); 27334 if (saved_eval_ret_idx < 0) 27335 goto fail; 27336 emit_op(s, OP_get_loc); 27337 emit_u16(s, s->cur_func->eval_ret_idx); 27338 emit_op(s, OP_put_loc); 27339 emit_u16(s, saved_eval_ret_idx); 27340 set_eval_ret_undefined(s); 27341 } 27342 27343 if (js_parse_block(s)) 27344 goto fail; 27345 27346 if (s->cur_func->eval_ret_idx >= 0) { 27347 emit_op(s, OP_get_loc); 27348 emit_u16(s, saved_eval_ret_idx); 27349 emit_op(s, OP_put_loc); 27350 emit_u16(s, s->cur_func->eval_ret_idx); 27351 } 27352 pop_break_entry(s->cur_func); 27353 } 27354 emit_op(s, OP_ret); 27355 emit_label(s, label_end); 27356 } 27357 break; 27358 case ';': 27359 /* empty statement */ 27360 if (next_token(s)) 27361 goto fail; 27362 break; 27363 case TOK_WITH: 27364 if (s->cur_func->js_mode & JS_MODE_STRICT) { 27365 js_parse_error(s, "invalid keyword: with"); 27366 goto fail; 27367 } else { 27368 int with_idx; 27369 27370 if (next_token(s)) 27371 goto fail; 27372 27373 if (js_parse_expr_paren(s)) 27374 goto fail; 27375 27376 push_scope(s); 27377 with_idx = define_var(s, s->cur_func, JS_ATOM__with_, 27378 JS_VAR_DEF_WITH); 27379 if (with_idx < 0) 27380 goto fail; 27381 emit_op(s, OP_to_object); 27382 emit_op(s, OP_put_loc); 27383 emit_u16(s, with_idx); 27384 27385 set_eval_ret_undefined(s); 27386 if (js_parse_statement(s)) 27387 goto fail; 27388 27389 /* Popping scope drops lexical context for the with object variable */ 27390 pop_scope(s); 27391 } 27392 break; 27393 case TOK_FUNCTION: 27394 /* ES6 Annex B.3.2 and B.3.3 semantics */ 27395 if (!(decl_mask & DECL_MASK_FUNC)) 27396 goto func_decl_error; 27397 if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*') 27398 goto func_decl_error; 27399 goto parse_func_var; 27400 case TOK_IDENT: 27401 if (s->token.u.ident.is_reserved) { 27402 js_parse_error_reserved_identifier(s); 27403 goto fail; 27404 } 27405 /* Determine if `let` introduces a Declaration or an ExpressionStatement */ 27406 switch (is_let(s, decl_mask)) { 27407 case TRUE: 27408 tok = TOK_LET; 27409 goto haslet; 27410 case FALSE: 27411 break; 27412 default: 27413 goto fail; 27414 } 27415 if (token_is_pseudo_keyword(s, JS_ATOM_async) && 27416 peek_token(s, TRUE) == TOK_FUNCTION) { 27417 if (!(decl_mask & DECL_MASK_OTHER)) { 27418 func_decl_error: 27419 js_parse_error(s, "function declarations can't appear in single-statement context"); 27420 goto fail; 27421 } 27422 parse_func_var: 27423 if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR, 27424 JS_FUNC_NORMAL, JS_ATOM_NULL, 27425 s->token.ptr, s->token.line_num)) 27426 goto fail; 27427 break; 27428 } 27429 goto hasexpr; 27430 27431 case TOK_CLASS: 27432 if (!(decl_mask & DECL_MASK_OTHER)) { 27433 js_parse_error(s, "class declarations can't appear in single-statement context"); 27434 goto fail; 27435 } 27436 if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE)) 27437 return -1; 27438 break; 27439 27440 case TOK_DEBUGGER: 27441 /* currently no debugger, so just skip the keyword */ 27442 if (next_token(s)) 27443 goto fail; 27444 if (js_parse_expect_semi(s)) 27445 goto fail; 27446 break; 27447 27448 case TOK_ENUM: 27449 case TOK_EXPORT: 27450 case TOK_EXTENDS: 27451 js_unsupported_keyword(s, s->token.u.ident.atom); 27452 goto fail; 27453 27454 default: 27455 hasexpr: 27456 if (js_parse_expr(s)) 27457 goto fail; 27458 if (s->cur_func->eval_ret_idx >= 0) { 27459 /* store the expression value so that it can be returned 27460 by eval() */ 27461 emit_op(s, OP_put_loc); 27462 emit_u16(s, s->cur_func->eval_ret_idx); 27463 } else { 27464 emit_op(s, OP_drop); /* drop the result */ 27465 } 27466 if (js_parse_expect_semi(s)) 27467 goto fail; 27468 break; 27469 } 27470 done: 27471 JS_FreeAtom(ctx, label_name); 27472 return 0; 27473 fail: 27474 JS_FreeAtom(ctx, label_name); 27475 return -1; 27476 } 27477 27478 /* 'name' is freed */ 27479 static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) 27480 { 27481 JSModuleDef *m; 27482 m = js_mallocz(ctx, sizeof(*m)); 27483 if (!m) { 27484 JS_FreeAtom(ctx, name); 27485 return NULL; 27486 } 27487 m->header.ref_count = 1; 27488 m->module_name = name; 27489 m->module_ns = JS_UNDEFINED; 27490 m->func_obj = JS_UNDEFINED; 27491 m->eval_exception = JS_UNDEFINED; 27492 m->meta_obj = JS_UNDEFINED; 27493 m->promise = JS_UNDEFINED; 27494 m->resolving_funcs[0] = JS_UNDEFINED; 27495 m->resolving_funcs[1] = JS_UNDEFINED; 27496 list_add_tail(&m->link, &ctx->loaded_modules); 27497 return m; 27498 } 27499 27500 static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, 27501 JS_MarkFunc *mark_func) 27502 { 27503 int i; 27504 27505 for(i = 0; i < m->export_entries_count; i++) { 27506 JSExportEntry *me = &m->export_entries[i]; 27507 if (me->export_type == JS_EXPORT_TYPE_LOCAL && 27508 me->u.local.var_ref) { 27509 mark_func(rt, &me->u.local.var_ref->header); 27510 } 27511 } 27512 27513 JS_MarkValue(rt, m->module_ns, mark_func); 27514 JS_MarkValue(rt, m->func_obj, mark_func); 27515 JS_MarkValue(rt, m->eval_exception, mark_func); 27516 JS_MarkValue(rt, m->meta_obj, mark_func); 27517 JS_MarkValue(rt, m->promise, mark_func); 27518 JS_MarkValue(rt, m->resolving_funcs[0], mark_func); 27519 JS_MarkValue(rt, m->resolving_funcs[1], mark_func); 27520 } 27521 27522 static void js_free_module_def(JSContext *ctx, JSModuleDef *m) 27523 { 27524 int i; 27525 27526 JS_FreeAtom(ctx, m->module_name); 27527 27528 for(i = 0; i < m->req_module_entries_count; i++) { 27529 JSReqModuleEntry *rme = &m->req_module_entries[i]; 27530 JS_FreeAtom(ctx, rme->module_name); 27531 } 27532 js_free(ctx, m->req_module_entries); 27533 27534 for(i = 0; i < m->export_entries_count; i++) { 27535 JSExportEntry *me = &m->export_entries[i]; 27536 if (me->export_type == JS_EXPORT_TYPE_LOCAL) 27537 free_var_ref(ctx->rt, me->u.local.var_ref); 27538 JS_FreeAtom(ctx, me->export_name); 27539 JS_FreeAtom(ctx, me->local_name); 27540 } 27541 js_free(ctx, m->export_entries); 27542 27543 js_free(ctx, m->star_export_entries); 27544 27545 for(i = 0; i < m->import_entries_count; i++) { 27546 JSImportEntry *mi = &m->import_entries[i]; 27547 JS_FreeAtom(ctx, mi->import_name); 27548 } 27549 js_free(ctx, m->import_entries); 27550 js_free(ctx, m->async_parent_modules); 27551 27552 JS_FreeValue(ctx, m->module_ns); 27553 JS_FreeValue(ctx, m->func_obj); 27554 JS_FreeValue(ctx, m->eval_exception); 27555 JS_FreeValue(ctx, m->meta_obj); 27556 JS_FreeValue(ctx, m->promise); 27557 JS_FreeValue(ctx, m->resolving_funcs[0]); 27558 JS_FreeValue(ctx, m->resolving_funcs[1]); 27559 list_del(&m->link); 27560 js_free(ctx, m); 27561 } 27562 27563 static int add_req_module_entry(JSContext *ctx, JSModuleDef *m, 27564 JSAtom module_name) 27565 { 27566 JSReqModuleEntry *rme; 27567 int i; 27568 27569 /* no need to add the module request if it is already present */ 27570 for(i = 0; i < m->req_module_entries_count; i++) { 27571 rme = &m->req_module_entries[i]; 27572 if (rme->module_name == module_name) 27573 return i; 27574 } 27575 27576 if (js_resize_array(ctx, (void **)&m->req_module_entries, 27577 sizeof(JSReqModuleEntry), 27578 &m->req_module_entries_size, 27579 m->req_module_entries_count + 1)) 27580 return -1; 27581 rme = &m->req_module_entries[m->req_module_entries_count++]; 27582 rme->module_name = JS_DupAtom(ctx, module_name); 27583 rme->module = NULL; 27584 return i; 27585 } 27586 27587 static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m, 27588 JSAtom export_name) 27589 { 27590 JSExportEntry *me; 27591 int i; 27592 for(i = 0; i < m->export_entries_count; i++) { 27593 me = &m->export_entries[i]; 27594 if (me->export_name == export_name) 27595 return me; 27596 } 27597 return NULL; 27598 } 27599 27600 static JSExportEntry *add_export_entry2(JSContext *ctx, 27601 JSParseState *s, JSModuleDef *m, 27602 JSAtom local_name, JSAtom export_name, 27603 JSExportTypeEnum export_type) 27604 { 27605 JSExportEntry *me; 27606 27607 if (find_export_entry(ctx, m, export_name)) { 27608 char buf1[ATOM_GET_STR_BUF_SIZE]; 27609 if (s) { 27610 js_parse_error(s, "duplicate exported name '%s'", 27611 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name)); 27612 } else { 27613 JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name); 27614 } 27615 return NULL; 27616 } 27617 27618 if (js_resize_array(ctx, (void **)&m->export_entries, 27619 sizeof(JSExportEntry), 27620 &m->export_entries_size, 27621 m->export_entries_count + 1)) 27622 return NULL; 27623 me = &m->export_entries[m->export_entries_count++]; 27624 memset(me, 0, sizeof(*me)); 27625 me->local_name = JS_DupAtom(ctx, local_name); 27626 me->export_name = JS_DupAtom(ctx, export_name); 27627 me->export_type = export_type; 27628 return me; 27629 } 27630 27631 static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m, 27632 JSAtom local_name, JSAtom export_name, 27633 JSExportTypeEnum export_type) 27634 { 27635 return add_export_entry2(s->ctx, s, m, local_name, export_name, 27636 export_type); 27637 } 27638 27639 static int add_star_export_entry(JSContext *ctx, JSModuleDef *m, 27640 int req_module_idx) 27641 { 27642 JSStarExportEntry *se; 27643 27644 if (js_resize_array(ctx, (void **)&m->star_export_entries, 27645 sizeof(JSStarExportEntry), 27646 &m->star_export_entries_size, 27647 m->star_export_entries_count + 1)) 27648 return -1; 27649 se = &m->star_export_entries[m->star_export_entries_count++]; 27650 se->req_module_idx = req_module_idx; 27651 return 0; 27652 } 27653 27654 /* create a C module */ 27655 JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str, 27656 JSModuleInitFunc *func) 27657 { 27658 JSModuleDef *m; 27659 JSAtom name; 27660 name = JS_NewAtom(ctx, name_str); 27661 if (name == JS_ATOM_NULL) 27662 return NULL; 27663 m = js_new_module_def(ctx, name); 27664 m->init_func = func; 27665 return m; 27666 } 27667 27668 int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name) 27669 { 27670 JSExportEntry *me; 27671 JSAtom name; 27672 name = JS_NewAtom(ctx, export_name); 27673 if (name == JS_ATOM_NULL) 27674 return -1; 27675 me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name, 27676 JS_EXPORT_TYPE_LOCAL); 27677 JS_FreeAtom(ctx, name); 27678 if (!me) 27679 return -1; 27680 else 27681 return 0; 27682 } 27683 27684 int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, 27685 JSValue val) 27686 { 27687 JSExportEntry *me; 27688 JSAtom name; 27689 name = JS_NewAtom(ctx, export_name); 27690 if (name == JS_ATOM_NULL) 27691 goto fail; 27692 me = find_export_entry(ctx, m, name); 27693 JS_FreeAtom(ctx, name); 27694 if (!me) 27695 goto fail; 27696 set_value(ctx, me->u.local.var_ref->pvalue, val); 27697 return 0; 27698 fail: 27699 JS_FreeValue(ctx, val); 27700 return -1; 27701 } 27702 27703 void JS_SetModuleLoaderFunc(JSRuntime *rt, 27704 JSModuleNormalizeFunc *module_normalize, 27705 JSModuleLoaderFunc *module_loader, void *opaque) 27706 { 27707 rt->module_normalize_func = module_normalize; 27708 rt->module_loader_func = module_loader; 27709 rt->module_loader_opaque = opaque; 27710 } 27711 27712 /* default module filename normalizer */ 27713 static char *js_default_module_normalize_name(JSContext *ctx, 27714 const char *base_name, 27715 const char *name) 27716 { 27717 char *filename, *p; 27718 const char *r; 27719 int cap; 27720 int len; 27721 27722 if (name[0] != '.') { 27723 /* if no initial dot, the module name is not modified */ 27724 return js_strdup(ctx, name); 27725 } 27726 27727 p = strrchr(base_name, '/'); 27728 if (p) 27729 len = p - base_name; 27730 else 27731 len = 0; 27732 27733 cap = len + strlen(name) + 1 + 1; 27734 filename = js_malloc(ctx, cap); 27735 if (!filename) 27736 return NULL; 27737 memcpy(filename, base_name, len); 27738 filename[len] = '\0'; 27739 27740 /* we only normalize the leading '..' or '.' */ 27741 r = name; 27742 for(;;) { 27743 if (r[0] == '.' && r[1] == '/') { 27744 r += 2; 27745 } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') { 27746 /* remove the last path element of filename, except if "." 27747 or ".." */ 27748 if (filename[0] == '\0') 27749 break; 27750 p = strrchr(filename, '/'); 27751 if (!p) 27752 p = filename; 27753 else 27754 p++; 27755 if (!strcmp(p, ".") || !strcmp(p, "..")) 27756 break; 27757 if (p > filename) 27758 p--; 27759 *p = '\0'; 27760 r += 3; 27761 } else { 27762 break; 27763 } 27764 } 27765 if (filename[0] != '\0') 27766 pstrcat(filename, cap, "/"); 27767 pstrcat(filename, cap, r); 27768 // printf("normalize: %s %s -> %s\n", base_name, name, filename); 27769 return filename; 27770 } 27771 27772 static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name) 27773 { 27774 struct list_head *el; 27775 JSModuleDef *m; 27776 27777 /* first look at the loaded modules */ 27778 list_for_each(el, &ctx->loaded_modules) { 27779 m = list_entry(el, JSModuleDef, link); 27780 if (m->module_name == name) 27781 return m; 27782 } 27783 return NULL; 27784 } 27785 27786 /* return NULL in case of exception (e.g. module could not be loaded) */ 27787 static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx, 27788 const char *base_cname, 27789 const char *cname1) 27790 { 27791 JSRuntime *rt = ctx->rt; 27792 JSModuleDef *m; 27793 char *cname; 27794 JSAtom module_name; 27795 27796 if (!rt->module_normalize_func) { 27797 cname = js_default_module_normalize_name(ctx, base_cname, cname1); 27798 } else { 27799 cname = rt->module_normalize_func(ctx, base_cname, cname1, 27800 rt->module_loader_opaque); 27801 } 27802 if (!cname) 27803 return NULL; 27804 27805 module_name = JS_NewAtom(ctx, cname); 27806 if (module_name == JS_ATOM_NULL) { 27807 js_free(ctx, cname); 27808 return NULL; 27809 } 27810 27811 /* first look at the loaded modules */ 27812 m = js_find_loaded_module(ctx, module_name); 27813 if (m) { 27814 js_free(ctx, cname); 27815 JS_FreeAtom(ctx, module_name); 27816 return m; 27817 } 27818 27819 JS_FreeAtom(ctx, module_name); 27820 27821 /* load the module */ 27822 if (!rt->module_loader_func) { 27823 /* XXX: use a syntax error ? */ 27824 JS_ThrowReferenceError(ctx, "could not load module '%s'", 27825 cname); 27826 js_free(ctx, cname); 27827 return NULL; 27828 } 27829 27830 m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque); 27831 js_free(ctx, cname); 27832 return m; 27833 } 27834 27835 static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx, 27836 JSAtom base_module_name, 27837 JSAtom module_name1) 27838 { 27839 const char *base_cname, *cname; 27840 JSModuleDef *m; 27841 27842 base_cname = JS_AtomToCString(ctx, base_module_name); 27843 if (!base_cname) 27844 return NULL; 27845 cname = JS_AtomToCString(ctx, module_name1); 27846 if (!cname) { 27847 JS_FreeCString(ctx, base_cname); 27848 return NULL; 27849 } 27850 m = js_host_resolve_imported_module(ctx, base_cname, cname); 27851 JS_FreeCString(ctx, base_cname); 27852 JS_FreeCString(ctx, cname); 27853 return m; 27854 } 27855 27856 typedef struct JSResolveEntry { 27857 JSModuleDef *module; 27858 JSAtom name; 27859 } JSResolveEntry; 27860 27861 typedef struct JSResolveState { 27862 JSResolveEntry *array; 27863 int size; 27864 int count; 27865 } JSResolveState; 27866 27867 static int find_resolve_entry(JSResolveState *s, 27868 JSModuleDef *m, JSAtom name) 27869 { 27870 int i; 27871 for(i = 0; i < s->count; i++) { 27872 JSResolveEntry *re = &s->array[i]; 27873 if (re->module == m && re->name == name) 27874 return i; 27875 } 27876 return -1; 27877 } 27878 27879 static int add_resolve_entry(JSContext *ctx, JSResolveState *s, 27880 JSModuleDef *m, JSAtom name) 27881 { 27882 JSResolveEntry *re; 27883 27884 if (js_resize_array(ctx, (void **)&s->array, 27885 sizeof(JSResolveEntry), 27886 &s->size, s->count + 1)) 27887 return -1; 27888 re = &s->array[s->count++]; 27889 re->module = m; 27890 re->name = JS_DupAtom(ctx, name); 27891 return 0; 27892 } 27893 27894 typedef enum JSResolveResultEnum { 27895 JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */ 27896 JS_RESOLVE_RES_FOUND = 0, 27897 JS_RESOLVE_RES_NOT_FOUND, 27898 JS_RESOLVE_RES_CIRCULAR, 27899 JS_RESOLVE_RES_AMBIGUOUS, 27900 } JSResolveResultEnum; 27901 27902 static JSResolveResultEnum js_resolve_export1(JSContext *ctx, 27903 JSModuleDef **pmodule, 27904 JSExportEntry **pme, 27905 JSModuleDef *m, 27906 JSAtom export_name, 27907 JSResolveState *s) 27908 { 27909 JSExportEntry *me; 27910 27911 *pmodule = NULL; 27912 *pme = NULL; 27913 if (find_resolve_entry(s, m, export_name) >= 0) 27914 return JS_RESOLVE_RES_CIRCULAR; 27915 if (add_resolve_entry(ctx, s, m, export_name) < 0) 27916 return JS_RESOLVE_RES_EXCEPTION; 27917 me = find_export_entry(ctx, m, export_name); 27918 if (me) { 27919 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 27920 /* local export */ 27921 *pmodule = m; 27922 *pme = me; 27923 return JS_RESOLVE_RES_FOUND; 27924 } else { 27925 /* indirect export */ 27926 JSModuleDef *m1; 27927 m1 = m->req_module_entries[me->u.req_module_idx].module; 27928 if (me->local_name == JS_ATOM__star_) { 27929 /* export ns from */ 27930 *pmodule = m; 27931 *pme = me; 27932 return JS_RESOLVE_RES_FOUND; 27933 } else { 27934 return js_resolve_export1(ctx, pmodule, pme, m1, 27935 me->local_name, s); 27936 } 27937 } 27938 } else { 27939 if (export_name != JS_ATOM_default) { 27940 /* not found in direct or indirect exports: try star exports */ 27941 int i; 27942 27943 for(i = 0; i < m->star_export_entries_count; i++) { 27944 JSStarExportEntry *se = &m->star_export_entries[i]; 27945 JSModuleDef *m1, *res_m; 27946 JSExportEntry *res_me; 27947 JSResolveResultEnum ret; 27948 27949 m1 = m->req_module_entries[se->req_module_idx].module; 27950 ret = js_resolve_export1(ctx, &res_m, &res_me, m1, 27951 export_name, s); 27952 if (ret == JS_RESOLVE_RES_AMBIGUOUS || 27953 ret == JS_RESOLVE_RES_EXCEPTION) { 27954 return ret; 27955 } else if (ret == JS_RESOLVE_RES_FOUND) { 27956 if (*pme != NULL) { 27957 if (*pmodule != res_m || 27958 res_me->local_name != (*pme)->local_name) { 27959 *pmodule = NULL; 27960 *pme = NULL; 27961 return JS_RESOLVE_RES_AMBIGUOUS; 27962 } 27963 } else { 27964 *pmodule = res_m; 27965 *pme = res_me; 27966 } 27967 } 27968 } 27969 if (*pme != NULL) 27970 return JS_RESOLVE_RES_FOUND; 27971 } 27972 return JS_RESOLVE_RES_NOT_FOUND; 27973 } 27974 } 27975 27976 /* If the return value is JS_RESOLVE_RES_FOUND, return the module 27977 (*pmodule) and the corresponding local export entry 27978 (*pme). Otherwise return (NULL, NULL) */ 27979 static JSResolveResultEnum js_resolve_export(JSContext *ctx, 27980 JSModuleDef **pmodule, 27981 JSExportEntry **pme, 27982 JSModuleDef *m, 27983 JSAtom export_name) 27984 { 27985 JSResolveState ss, *s = &ss; 27986 int i; 27987 JSResolveResultEnum ret; 27988 27989 s->array = NULL; 27990 s->size = 0; 27991 s->count = 0; 27992 27993 ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s); 27994 27995 for(i = 0; i < s->count; i++) 27996 JS_FreeAtom(ctx, s->array[i].name); 27997 js_free(ctx, s->array); 27998 27999 return ret; 28000 } 28001 28002 static void js_resolve_export_throw_error(JSContext *ctx, 28003 JSResolveResultEnum res, 28004 JSModuleDef *m, JSAtom export_name) 28005 { 28006 char buf1[ATOM_GET_STR_BUF_SIZE]; 28007 char buf2[ATOM_GET_STR_BUF_SIZE]; 28008 switch(res) { 28009 case JS_RESOLVE_RES_EXCEPTION: 28010 break; 28011 default: 28012 case JS_RESOLVE_RES_NOT_FOUND: 28013 JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'", 28014 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 28015 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 28016 break; 28017 case JS_RESOLVE_RES_CIRCULAR: 28018 JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'", 28019 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 28020 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 28021 break; 28022 case JS_RESOLVE_RES_AMBIGUOUS: 28023 JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous", 28024 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name), 28025 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name)); 28026 break; 28027 } 28028 } 28029 28030 28031 typedef enum { 28032 EXPORTED_NAME_AMBIGUOUS, 28033 EXPORTED_NAME_NORMAL, 28034 EXPORTED_NAME_NS, 28035 } ExportedNameEntryEnum; 28036 28037 typedef struct ExportedNameEntry { 28038 JSAtom export_name; 28039 ExportedNameEntryEnum export_type; 28040 union { 28041 JSExportEntry *me; /* using when the list is built */ 28042 JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */ 28043 JSModuleDef *module; /* for EXPORTED_NAME_NS */ 28044 } u; 28045 } ExportedNameEntry; 28046 28047 typedef struct GetExportNamesState { 28048 JSModuleDef **modules; 28049 int modules_size; 28050 int modules_count; 28051 28052 ExportedNameEntry *exported_names; 28053 int exported_names_size; 28054 int exported_names_count; 28055 } GetExportNamesState; 28056 28057 static int find_exported_name(GetExportNamesState *s, JSAtom name) 28058 { 28059 int i; 28060 for(i = 0; i < s->exported_names_count; i++) { 28061 if (s->exported_names[i].export_name == name) 28062 return i; 28063 } 28064 return -1; 28065 } 28066 28067 static __exception int get_exported_names(JSContext *ctx, 28068 GetExportNamesState *s, 28069 JSModuleDef *m, BOOL from_star) 28070 { 28071 ExportedNameEntry *en; 28072 int i, j; 28073 28074 /* check circular reference */ 28075 for(i = 0; i < s->modules_count; i++) { 28076 if (s->modules[i] == m) 28077 return 0; 28078 } 28079 if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]), 28080 &s->modules_size, s->modules_count + 1)) 28081 return -1; 28082 s->modules[s->modules_count++] = m; 28083 28084 for(i = 0; i < m->export_entries_count; i++) { 28085 JSExportEntry *me = &m->export_entries[i]; 28086 if (from_star && me->export_name == JS_ATOM_default) 28087 continue; 28088 j = find_exported_name(s, me->export_name); 28089 if (j < 0) { 28090 if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]), 28091 &s->exported_names_size, 28092 s->exported_names_count + 1)) 28093 return -1; 28094 en = &s->exported_names[s->exported_names_count++]; 28095 en->export_name = me->export_name; 28096 /* avoid a second lookup for simple module exports */ 28097 if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL) 28098 en->u.me = NULL; 28099 else 28100 en->u.me = me; 28101 } else { 28102 en = &s->exported_names[j]; 28103 en->u.me = NULL; 28104 } 28105 } 28106 for(i = 0; i < m->star_export_entries_count; i++) { 28107 JSStarExportEntry *se = &m->star_export_entries[i]; 28108 JSModuleDef *m1; 28109 m1 = m->req_module_entries[se->req_module_idx].module; 28110 if (get_exported_names(ctx, s, m1, TRUE)) 28111 return -1; 28112 } 28113 return 0; 28114 } 28115 28116 /* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */ 28117 static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom) 28118 { 28119 return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL); 28120 } 28121 28122 static const JSClassExoticMethods js_module_ns_exotic_methods = { 28123 .has_property = js_module_ns_has, 28124 }; 28125 28126 static int exported_names_cmp(const void *p1, const void *p2, void *opaque) 28127 { 28128 JSContext *ctx = opaque; 28129 const ExportedNameEntry *me1 = p1; 28130 const ExportedNameEntry *me2 = p2; 28131 JSValue str1, str2; 28132 int ret; 28133 28134 /* XXX: should avoid allocation memory in atom comparison */ 28135 str1 = JS_AtomToString(ctx, me1->export_name); 28136 str2 = JS_AtomToString(ctx, me2->export_name); 28137 if (JS_IsException(str1) || JS_IsException(str2)) { 28138 /* XXX: raise an error ? */ 28139 ret = 0; 28140 } else { 28141 ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1), 28142 JS_VALUE_GET_STRING(str2)); 28143 } 28144 JS_FreeValue(ctx, str1); 28145 JS_FreeValue(ctx, str2); 28146 return ret; 28147 } 28148 28149 static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, 28150 void *opaque) 28151 { 28152 JSModuleDef *m = opaque; 28153 return JS_GetModuleNamespace(ctx, m); 28154 } 28155 28156 static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) 28157 { 28158 JSValue obj; 28159 JSObject *p; 28160 GetExportNamesState s_s, *s = &s_s; 28161 int i, ret; 28162 JSProperty *pr; 28163 28164 obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS); 28165 if (JS_IsException(obj)) 28166 return obj; 28167 p = JS_VALUE_GET_OBJ(obj); 28168 28169 memset(s, 0, sizeof(*s)); 28170 ret = get_exported_names(ctx, s, m, FALSE); 28171 js_free(ctx, s->modules); 28172 if (ret) 28173 goto fail; 28174 28175 /* Resolve the exported names. The ambiguous exports are removed */ 28176 for(i = 0; i < s->exported_names_count; i++) { 28177 ExportedNameEntry *en = &s->exported_names[i]; 28178 JSResolveResultEnum res; 28179 JSExportEntry *res_me; 28180 JSModuleDef *res_m; 28181 28182 if (en->u.me) { 28183 res_me = en->u.me; /* fast case: no resolution needed */ 28184 res_m = m; 28185 res = JS_RESOLVE_RES_FOUND; 28186 } else { 28187 res = js_resolve_export(ctx, &res_m, &res_me, m, 28188 en->export_name); 28189 } 28190 if (res != JS_RESOLVE_RES_FOUND) { 28191 if (res != JS_RESOLVE_RES_AMBIGUOUS) { 28192 js_resolve_export_throw_error(ctx, res, m, en->export_name); 28193 goto fail; 28194 } 28195 en->export_type = EXPORTED_NAME_AMBIGUOUS; 28196 } else { 28197 if (res_me->local_name == JS_ATOM__star_) { 28198 en->export_type = EXPORTED_NAME_NS; 28199 en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module; 28200 } else { 28201 en->export_type = EXPORTED_NAME_NORMAL; 28202 if (res_me->u.local.var_ref) { 28203 en->u.var_ref = res_me->u.local.var_ref; 28204 } else { 28205 JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 28206 p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 28207 en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; 28208 } 28209 } 28210 } 28211 } 28212 28213 /* sort the exported names */ 28214 rqsort(s->exported_names, s->exported_names_count, 28215 sizeof(s->exported_names[0]), exported_names_cmp, ctx); 28216 28217 for(i = 0; i < s->exported_names_count; i++) { 28218 ExportedNameEntry *en = &s->exported_names[i]; 28219 switch(en->export_type) { 28220 case EXPORTED_NAME_NORMAL: 28221 { 28222 JSVarRef *var_ref = en->u.var_ref; 28223 pr = add_property(ctx, p, en->export_name, 28224 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | 28225 JS_PROP_VARREF); 28226 if (!pr) 28227 goto fail; 28228 var_ref->header.ref_count++; 28229 pr->u.var_ref = var_ref; 28230 } 28231 break; 28232 case EXPORTED_NAME_NS: 28233 /* the exported namespace must be created on demand */ 28234 if (JS_DefineAutoInitProperty(ctx, obj, 28235 en->export_name, 28236 JS_AUTOINIT_ID_MODULE_NS, 28237 en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) 28238 goto fail; 28239 break; 28240 default: 28241 break; 28242 } 28243 } 28244 28245 js_free(ctx, s->exported_names); 28246 28247 JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag, 28248 JS_AtomToString(ctx, JS_ATOM_Module), 28249 0); 28250 28251 p->extensible = FALSE; 28252 return obj; 28253 fail: 28254 js_free(ctx, s->exported_names); 28255 JS_FreeValue(ctx, obj); 28256 return JS_EXCEPTION; 28257 } 28258 28259 JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) 28260 { 28261 if (JS_IsUndefined(m->module_ns)) { 28262 JSValue val; 28263 val = js_build_module_ns(ctx, m); 28264 if (JS_IsException(val)) 28265 return JS_EXCEPTION; 28266 m->module_ns = val; 28267 } 28268 return JS_DupValue(ctx, m->module_ns); 28269 } 28270 28271 /* Load all the required modules for module 'm' */ 28272 static int js_resolve_module(JSContext *ctx, JSModuleDef *m) 28273 { 28274 int i; 28275 JSModuleDef *m1; 28276 28277 if (m->resolved) 28278 return 0; 28279 #ifdef DUMP_MODULE_RESOLVE 28280 { 28281 char buf1[ATOM_GET_STR_BUF_SIZE]; 28282 printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 28283 } 28284 #endif 28285 m->resolved = TRUE; 28286 /* resolve each requested module */ 28287 for(i = 0; i < m->req_module_entries_count; i++) { 28288 JSReqModuleEntry *rme = &m->req_module_entries[i]; 28289 m1 = js_host_resolve_imported_module_atom(ctx, m->module_name, 28290 rme->module_name); 28291 if (!m1) 28292 return -1; 28293 rme->module = m1; 28294 /* already done in js_host_resolve_imported_module() except if 28295 the module was loaded with JS_EvalBinary() */ 28296 if (js_resolve_module(ctx, m1) < 0) 28297 return -1; 28298 } 28299 return 0; 28300 } 28301 28302 static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical) 28303 { 28304 JSVarRef *var_ref; 28305 var_ref = js_malloc(ctx, sizeof(JSVarRef)); 28306 if (!var_ref) 28307 return NULL; 28308 var_ref->header.ref_count = 1; 28309 if (is_lexical) 28310 var_ref->value = JS_UNINITIALIZED; 28311 else 28312 var_ref->value = JS_UNDEFINED; 28313 var_ref->pvalue = &var_ref->value; 28314 var_ref->is_detached = TRUE; 28315 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); 28316 return var_ref; 28317 } 28318 28319 /* Create the <eval> function associated with the module */ 28320 static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m) 28321 { 28322 JSFunctionBytecode *b; 28323 int i; 28324 JSVarRef **var_refs; 28325 JSValue func_obj, bfunc; 28326 JSObject *p; 28327 28328 bfunc = m->func_obj; 28329 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 28330 JS_CLASS_BYTECODE_FUNCTION); 28331 28332 if (JS_IsException(func_obj)) 28333 return -1; 28334 b = JS_VALUE_GET_PTR(bfunc); 28335 28336 p = JS_VALUE_GET_OBJ(func_obj); 28337 p->u.func.function_bytecode = b; 28338 b->header.ref_count++; 28339 p->u.func.home_object = NULL; 28340 p->u.func.var_refs = NULL; 28341 if (b->closure_var_count) { 28342 var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); 28343 if (!var_refs) 28344 goto fail; 28345 p->u.func.var_refs = var_refs; 28346 28347 /* create the global variables. The other variables are 28348 imported from other modules */ 28349 for(i = 0; i < b->closure_var_count; i++) { 28350 JSClosureVar *cv = &b->closure_var[i]; 28351 JSVarRef *var_ref; 28352 if (cv->is_local) { 28353 var_ref = js_create_module_var(ctx, cv->is_lexical); 28354 if (!var_ref) 28355 goto fail; 28356 #ifdef DUMP_MODULE_RESOLVE 28357 printf("local %d: %p\n", i, var_ref); 28358 #endif 28359 var_refs[i] = var_ref; 28360 } 28361 } 28362 } 28363 m->func_obj = func_obj; 28364 JS_FreeValue(ctx, bfunc); 28365 return 0; 28366 fail: 28367 JS_FreeValue(ctx, func_obj); 28368 return -1; 28369 } 28370 28371 /* must be done before js_link_module() because of cyclic references */ 28372 static int js_create_module_function(JSContext *ctx, JSModuleDef *m) 28373 { 28374 BOOL is_c_module; 28375 int i; 28376 JSVarRef *var_ref; 28377 28378 if (m->func_created) 28379 return 0; 28380 28381 is_c_module = (m->init_func != NULL); 28382 28383 if (is_c_module) { 28384 /* initialize the exported variables */ 28385 for(i = 0; i < m->export_entries_count; i++) { 28386 JSExportEntry *me = &m->export_entries[i]; 28387 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 28388 var_ref = js_create_module_var(ctx, FALSE); 28389 if (!var_ref) 28390 return -1; 28391 me->u.local.var_ref = var_ref; 28392 } 28393 } 28394 } else { 28395 if (js_create_module_bytecode_function(ctx, m)) 28396 return -1; 28397 } 28398 m->func_created = TRUE; 28399 28400 /* do it on the dependencies */ 28401 28402 for(i = 0; i < m->req_module_entries_count; i++) { 28403 JSReqModuleEntry *rme = &m->req_module_entries[i]; 28404 if (js_create_module_function(ctx, rme->module) < 0) 28405 return -1; 28406 } 28407 28408 return 0; 28409 } 28410 28411 28412 /* Prepare a module to be executed by resolving all the imported 28413 variables. */ 28414 static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, 28415 JSModuleDef **pstack_top, int index) 28416 { 28417 int i; 28418 JSImportEntry *mi; 28419 JSModuleDef *m1; 28420 JSVarRef **var_refs, *var_ref; 28421 JSObject *p; 28422 BOOL is_c_module; 28423 JSValue ret_val; 28424 28425 if (js_check_stack_overflow(ctx->rt, 0)) { 28426 JS_ThrowStackOverflow(ctx); 28427 return -1; 28428 } 28429 28430 #ifdef DUMP_MODULE_RESOLVE 28431 { 28432 char buf1[ATOM_GET_STR_BUF_SIZE]; 28433 printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 28434 } 28435 #endif 28436 28437 if (m->status == JS_MODULE_STATUS_LINKING || 28438 m->status == JS_MODULE_STATUS_LINKED || 28439 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 28440 m->status == JS_MODULE_STATUS_EVALUATED) 28441 return index; 28442 28443 assert(m->status == JS_MODULE_STATUS_UNLINKED); 28444 m->status = JS_MODULE_STATUS_LINKING; 28445 m->dfs_index = index; 28446 m->dfs_ancestor_index = index; 28447 index++; 28448 /* push 'm' on stack */ 28449 m->stack_prev = *pstack_top; 28450 *pstack_top = m; 28451 28452 for(i = 0; i < m->req_module_entries_count; i++) { 28453 JSReqModuleEntry *rme = &m->req_module_entries[i]; 28454 m1 = rme->module; 28455 index = js_inner_module_linking(ctx, m1, pstack_top, index); 28456 if (index < 0) 28457 goto fail; 28458 assert(m1->status == JS_MODULE_STATUS_LINKING || 28459 m1->status == JS_MODULE_STATUS_LINKED || 28460 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 28461 m1->status == JS_MODULE_STATUS_EVALUATED); 28462 if (m1->status == JS_MODULE_STATUS_LINKING) { 28463 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, 28464 m1->dfs_ancestor_index); 28465 } 28466 } 28467 28468 #ifdef DUMP_MODULE_RESOLVE 28469 { 28470 char buf1[ATOM_GET_STR_BUF_SIZE]; 28471 printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 28472 } 28473 #endif 28474 /* check the indirect exports */ 28475 for(i = 0; i < m->export_entries_count; i++) { 28476 JSExportEntry *me = &m->export_entries[i]; 28477 if (me->export_type == JS_EXPORT_TYPE_INDIRECT && 28478 me->local_name != JS_ATOM__star_) { 28479 JSResolveResultEnum ret; 28480 JSExportEntry *res_me; 28481 JSModuleDef *res_m, *m1; 28482 m1 = m->req_module_entries[me->u.req_module_idx].module; 28483 ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name); 28484 if (ret != JS_RESOLVE_RES_FOUND) { 28485 js_resolve_export_throw_error(ctx, ret, m, me->export_name); 28486 goto fail; 28487 } 28488 } 28489 } 28490 28491 #ifdef DUMP_MODULE_RESOLVE 28492 { 28493 printf("exported bindings:\n"); 28494 for(i = 0; i < m->export_entries_count; i++) { 28495 JSExportEntry *me = &m->export_entries[i]; 28496 printf(" name="); print_atom(ctx, me->export_name); 28497 printf(" local="); print_atom(ctx, me->local_name); 28498 printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx); 28499 } 28500 } 28501 #endif 28502 28503 is_c_module = (m->init_func != NULL); 28504 28505 if (!is_c_module) { 28506 p = JS_VALUE_GET_OBJ(m->func_obj); 28507 var_refs = p->u.func.var_refs; 28508 28509 for(i = 0; i < m->import_entries_count; i++) { 28510 mi = &m->import_entries[i]; 28511 #ifdef DUMP_MODULE_RESOLVE 28512 printf("import var_idx=%d name=", mi->var_idx); 28513 print_atom(ctx, mi->import_name); 28514 printf(": "); 28515 #endif 28516 m1 = m->req_module_entries[mi->req_module_idx].module; 28517 if (mi->import_name == JS_ATOM__star_) { 28518 JSValue val; 28519 /* name space import */ 28520 val = JS_GetModuleNamespace(ctx, m1); 28521 if (JS_IsException(val)) 28522 goto fail; 28523 set_value(ctx, &var_refs[mi->var_idx]->value, val); 28524 #ifdef DUMP_MODULE_RESOLVE 28525 printf("namespace\n"); 28526 #endif 28527 } else { 28528 JSResolveResultEnum ret; 28529 JSExportEntry *res_me; 28530 JSModuleDef *res_m; 28531 JSObject *p1; 28532 28533 ret = js_resolve_export(ctx, &res_m, 28534 &res_me, m1, mi->import_name); 28535 if (ret != JS_RESOLVE_RES_FOUND) { 28536 js_resolve_export_throw_error(ctx, ret, m1, mi->import_name); 28537 goto fail; 28538 } 28539 if (res_me->local_name == JS_ATOM__star_) { 28540 JSValue val; 28541 JSModuleDef *m2; 28542 /* name space import from */ 28543 m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; 28544 val = JS_GetModuleNamespace(ctx, m2); 28545 if (JS_IsException(val)) 28546 goto fail; 28547 var_ref = js_create_module_var(ctx, TRUE); 28548 if (!var_ref) { 28549 JS_FreeValue(ctx, val); 28550 goto fail; 28551 } 28552 set_value(ctx, &var_ref->value, val); 28553 var_refs[mi->var_idx] = var_ref; 28554 #ifdef DUMP_MODULE_RESOLVE 28555 printf("namespace from\n"); 28556 #endif 28557 } else { 28558 var_ref = res_me->u.local.var_ref; 28559 if (!var_ref) { 28560 p1 = JS_VALUE_GET_OBJ(res_m->func_obj); 28561 var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; 28562 } 28563 var_ref->header.ref_count++; 28564 var_refs[mi->var_idx] = var_ref; 28565 #ifdef DUMP_MODULE_RESOLVE 28566 printf("local export (var_ref=%p)\n", var_ref); 28567 #endif 28568 } 28569 } 28570 } 28571 28572 /* keep the exported variables in the module export entries (they 28573 are used when the eval function is deleted and cannot be 28574 initialized before in case imports are exported) */ 28575 for(i = 0; i < m->export_entries_count; i++) { 28576 JSExportEntry *me = &m->export_entries[i]; 28577 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 28578 var_ref = var_refs[me->u.local.var_idx]; 28579 var_ref->header.ref_count++; 28580 me->u.local.var_ref = var_ref; 28581 } 28582 } 28583 28584 /* initialize the global variables */ 28585 ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL); 28586 if (JS_IsException(ret_val)) 28587 goto fail; 28588 JS_FreeValue(ctx, ret_val); 28589 } 28590 28591 assert(m->dfs_ancestor_index <= m->dfs_index); 28592 if (m->dfs_index == m->dfs_ancestor_index) { 28593 for(;;) { 28594 /* pop m1 from stack */ 28595 m1 = *pstack_top; 28596 *pstack_top = m1->stack_prev; 28597 m1->status = JS_MODULE_STATUS_LINKED; 28598 if (m1 == m) 28599 break; 28600 } 28601 } 28602 28603 #ifdef DUMP_MODULE_RESOLVE 28604 printf("js_inner_module_linking done\n"); 28605 #endif 28606 return index; 28607 fail: 28608 return -1; 28609 } 28610 28611 /* Prepare a module to be executed by resolving all the imported 28612 variables. */ 28613 static int js_link_module(JSContext *ctx, JSModuleDef *m) 28614 { 28615 JSModuleDef *stack_top, *m1; 28616 28617 #ifdef DUMP_MODULE_RESOLVE 28618 { 28619 char buf1[ATOM_GET_STR_BUF_SIZE]; 28620 printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 28621 } 28622 #endif 28623 assert(m->status == JS_MODULE_STATUS_UNLINKED || 28624 m->status == JS_MODULE_STATUS_LINKED || 28625 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 28626 m->status == JS_MODULE_STATUS_EVALUATED); 28627 stack_top = NULL; 28628 if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) { 28629 while (stack_top != NULL) { 28630 m1 = stack_top; 28631 assert(m1->status == JS_MODULE_STATUS_LINKING); 28632 m1->status = JS_MODULE_STATUS_UNLINKED; 28633 stack_top = m1->stack_prev; 28634 } 28635 return -1; 28636 } 28637 assert(stack_top == NULL); 28638 assert(m->status == JS_MODULE_STATUS_LINKED || 28639 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 28640 m->status == JS_MODULE_STATUS_EVALUATED); 28641 return 0; 28642 } 28643 28644 /* return JS_ATOM_NULL if the name cannot be found. Only works with 28645 not striped bytecode functions. */ 28646 JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) 28647 { 28648 JSStackFrame *sf; 28649 JSFunctionBytecode *b; 28650 JSObject *p; 28651 /* XXX: currently we just use the filename of the englobing 28652 function from the debug info. May need to add a ScriptOrModule 28653 info in JSFunctionBytecode. */ 28654 sf = ctx->rt->current_stack_frame; 28655 if (!sf) 28656 return JS_ATOM_NULL; 28657 while (n_stack_levels-- > 0) { 28658 sf = sf->prev_frame; 28659 if (!sf) 28660 return JS_ATOM_NULL; 28661 } 28662 for(;;) { 28663 if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) 28664 return JS_ATOM_NULL; 28665 p = JS_VALUE_GET_OBJ(sf->cur_func); 28666 if (!js_class_has_bytecode(p->class_id)) 28667 return JS_ATOM_NULL; 28668 b = p->u.func.function_bytecode; 28669 if (!b->is_direct_or_indirect_eval) { 28670 if (!b->has_debug) 28671 return JS_ATOM_NULL; 28672 return JS_DupAtom(ctx, b->debug.filename); 28673 } else { 28674 sf = sf->prev_frame; 28675 if (!sf) 28676 return JS_ATOM_NULL; 28677 } 28678 } 28679 } 28680 28681 JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) 28682 { 28683 return JS_DupAtom(ctx, m->module_name); 28684 } 28685 28686 JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m) 28687 { 28688 JSValue obj; 28689 /* allocate meta_obj only if requested to save memory */ 28690 obj = m->meta_obj; 28691 if (JS_IsUndefined(obj)) { 28692 obj = JS_NewObjectProto(ctx, JS_NULL); 28693 if (JS_IsException(obj)) 28694 return JS_EXCEPTION; 28695 m->meta_obj = obj; 28696 } 28697 return JS_DupValue(ctx, obj); 28698 } 28699 28700 static JSValue js_import_meta(JSContext *ctx) 28701 { 28702 JSAtom filename; 28703 JSModuleDef *m; 28704 28705 filename = JS_GetScriptOrModuleName(ctx, 0); 28706 if (filename == JS_ATOM_NULL) 28707 goto fail; 28708 28709 /* XXX: inefficient, need to add a module or script pointer in 28710 JSFunctionBytecode */ 28711 m = js_find_loaded_module(ctx, filename); 28712 JS_FreeAtom(ctx, filename); 28713 if (!m) { 28714 fail: 28715 JS_ThrowTypeError(ctx, "import.meta not supported in this context"); 28716 return JS_EXCEPTION; 28717 } 28718 return JS_GetImportMeta(ctx, m); 28719 } 28720 28721 static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) 28722 { 28723 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); 28724 } 28725 28726 static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, 28727 int argc, JSValueConst *argv, int magic, JSValue *func_data) 28728 { 28729 JSValueConst *resolving_funcs = (JSValueConst *)func_data; 28730 JSValueConst error; 28731 JSValue ret; 28732 28733 /* XXX: check if the test is necessary */ 28734 if (argc >= 1) 28735 error = argv[0]; 28736 else 28737 error = JS_UNDEFINED; 28738 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 28739 1, &error); 28740 JS_FreeValue(ctx, ret); 28741 return JS_UNDEFINED; 28742 } 28743 28744 static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, 28745 int argc, JSValueConst *argv, int magic, JSValue *func_data) 28746 { 28747 JSValueConst *resolving_funcs = (JSValueConst *)func_data; 28748 JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); 28749 JSValue ret, ns; 28750 28751 /* return the module namespace */ 28752 ns = JS_GetModuleNamespace(ctx, m); 28753 if (JS_IsException(ns)) { 28754 JSValue err = JS_GetException(ctx); 28755 js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); 28756 return JS_UNDEFINED; 28757 } 28758 ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, 28759 1, (JSValueConst *)&ns); 28760 JS_FreeValue(ctx, ret); 28761 JS_FreeValue(ctx, ns); 28762 return JS_UNDEFINED; 28763 } 28764 28765 static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, 28766 const char *filename, 28767 JSValueConst *resolving_funcs) 28768 { 28769 JSValue evaluate_promise; 28770 JSModuleDef *m; 28771 JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; 28772 JSValueConst func_data[3]; 28773 28774 m = js_host_resolve_imported_module(ctx, basename, filename); 28775 if (!m) 28776 goto fail; 28777 28778 if (js_resolve_module(ctx, m) < 0) { 28779 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); 28780 goto fail; 28781 } 28782 28783 /* Evaluate the module code */ 28784 func_obj = JS_NewModuleValue(ctx, m); 28785 evaluate_promise = JS_EvalFunction(ctx, func_obj); 28786 if (JS_IsException(evaluate_promise)) { 28787 fail: 28788 err = JS_GetException(ctx); 28789 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 28790 1, (JSValueConst *)&err); 28791 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ 28792 JS_FreeValue(ctx, err); 28793 return; 28794 } 28795 28796 func_obj = JS_NewModuleValue(ctx, m); 28797 func_data[0] = resolving_funcs[0]; 28798 func_data[1] = resolving_funcs[1]; 28799 func_data[2] = func_obj; 28800 evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); 28801 evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); 28802 JS_FreeValue(ctx, func_obj); 28803 ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); 28804 JS_FreeValue(ctx, ret); 28805 JS_FreeValue(ctx, evaluate_resolving_funcs[0]); 28806 JS_FreeValue(ctx, evaluate_resolving_funcs[1]); 28807 JS_FreeValue(ctx, evaluate_promise); 28808 } 28809 28810 /* Return a promise or an exception in case of memory error. Used by 28811 os.Worker() */ 28812 JSValue JS_LoadModule(JSContext *ctx, const char *basename, 28813 const char *filename) 28814 { 28815 JSValue promise, resolving_funcs[2]; 28816 28817 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 28818 if (JS_IsException(promise)) 28819 return JS_EXCEPTION; 28820 JS_LoadModuleInternal(ctx, basename, filename, 28821 (JSValueConst *)resolving_funcs); 28822 JS_FreeValue(ctx, resolving_funcs[0]); 28823 JS_FreeValue(ctx, resolving_funcs[1]); 28824 return promise; 28825 } 28826 28827 static JSValue js_dynamic_import_job(JSContext *ctx, 28828 int argc, JSValueConst *argv) 28829 { 28830 JSValueConst *resolving_funcs = argv; 28831 JSValueConst basename_val = argv[2]; 28832 JSValueConst specifier = argv[3]; 28833 const char *basename = NULL, *filename; 28834 JSValue ret, err; 28835 28836 if (!JS_IsString(basename_val)) { 28837 JS_ThrowTypeError(ctx, "no function filename for import()"); 28838 goto exception; 28839 } 28840 basename = JS_ToCString(ctx, basename_val); 28841 if (!basename) 28842 goto exception; 28843 28844 filename = JS_ToCString(ctx, specifier); 28845 if (!filename) 28846 goto exception; 28847 28848 JS_LoadModuleInternal(ctx, basename, filename, 28849 resolving_funcs); 28850 JS_FreeCString(ctx, filename); 28851 JS_FreeCString(ctx, basename); 28852 return JS_UNDEFINED; 28853 exception: 28854 err = JS_GetException(ctx); 28855 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 28856 1, (JSValueConst *)&err); 28857 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ 28858 JS_FreeValue(ctx, err); 28859 JS_FreeCString(ctx, basename); 28860 return JS_UNDEFINED; 28861 } 28862 28863 static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) 28864 { 28865 JSAtom basename; 28866 JSValue promise, resolving_funcs[2], basename_val; 28867 JSValueConst args[4]; 28868 28869 basename = JS_GetScriptOrModuleName(ctx, 0); 28870 if (basename == JS_ATOM_NULL) 28871 basename_val = JS_NULL; 28872 else 28873 basename_val = JS_AtomToValue(ctx, basename); 28874 JS_FreeAtom(ctx, basename); 28875 if (JS_IsException(basename_val)) 28876 return basename_val; 28877 28878 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 28879 if (JS_IsException(promise)) { 28880 JS_FreeValue(ctx, basename_val); 28881 return promise; 28882 } 28883 28884 args[0] = resolving_funcs[0]; 28885 args[1] = resolving_funcs[1]; 28886 args[2] = basename_val; 28887 args[3] = specifier; 28888 28889 /* cannot run JS_LoadModuleInternal synchronously because it would 28890 cause an unexpected recursion in js_evaluate_module() */ 28891 JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); 28892 28893 JS_FreeValue(ctx, basename_val); 28894 JS_FreeValue(ctx, resolving_funcs[0]); 28895 JS_FreeValue(ctx, resolving_funcs[1]); 28896 return promise; 28897 } 28898 28899 static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) 28900 { 28901 m->status = JS_MODULE_STATUS_EVALUATED; 28902 if (!JS_IsUndefined(m->promise)) { 28903 JSValue value, ret_val; 28904 assert(m->cycle_root == m); 28905 value = JS_UNDEFINED; 28906 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, 28907 1, (JSValueConst *)&value); 28908 JS_FreeValue(ctx, ret_val); 28909 } 28910 } 28911 28912 typedef struct { 28913 JSModuleDef **tab; 28914 int count; 28915 int size; 28916 } ExecModuleList; 28917 28918 /* XXX: slow. Could use a linked list instead of ExecModuleList */ 28919 static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m) 28920 { 28921 int i; 28922 for(i = 0; i < exec_list->count; i++) { 28923 if (exec_list->tab[i] == m) 28924 return TRUE; 28925 } 28926 return FALSE; 28927 } 28928 28929 static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module, 28930 ExecModuleList *exec_list) 28931 { 28932 int i; 28933 28934 if (js_check_stack_overflow(ctx->rt, 0)) { 28935 JS_ThrowStackOverflow(ctx); 28936 return -1; 28937 } 28938 for(i = 0; i < module->async_parent_modules_count; i++) { 28939 JSModuleDef *m = module->async_parent_modules[i]; 28940 if (!find_in_exec_module_list(exec_list, m) && 28941 !m->cycle_root->eval_has_exception) { 28942 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 28943 assert(!m->eval_has_exception); 28944 assert(m->async_evaluation); 28945 assert(m->pending_async_dependencies > 0); 28946 m->pending_async_dependencies--; 28947 if (m->pending_async_dependencies == 0) { 28948 if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) { 28949 return -1; 28950 } 28951 exec_list->tab[exec_list->count++] = m; 28952 if (!m->has_tla) { 28953 if (gather_available_ancestors(ctx, m, exec_list)) 28954 return -1; 28955 } 28956 } 28957 } 28958 } 28959 return 0; 28960 } 28961 28962 static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque) 28963 { 28964 JSModuleDef *m1 = *(JSModuleDef **)p1; 28965 JSModuleDef *m2 = *(JSModuleDef **)p2; 28966 return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) - 28967 (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp); 28968 } 28969 28970 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m); 28971 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, 28972 JSValue *pvalue); 28973 28974 static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, 28975 int argc, JSValueConst *argv, int magic, JSValue *func_data) 28976 { 28977 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); 28978 JSValueConst error = argv[0]; 28979 int i; 28980 28981 if (js_check_stack_overflow(ctx->rt, 0)) 28982 return JS_ThrowStackOverflow(ctx); 28983 28984 if (module->status == JS_MODULE_STATUS_EVALUATED) { 28985 assert(module->eval_has_exception); 28986 return JS_UNDEFINED; 28987 } 28988 28989 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 28990 assert(!module->eval_has_exception); 28991 assert(module->async_evaluation); 28992 28993 module->eval_has_exception = TRUE; 28994 module->eval_exception = JS_DupValue(ctx, error); 28995 module->status = JS_MODULE_STATUS_EVALUATED; 28996 28997 for(i = 0; i < module->async_parent_modules_count; i++) { 28998 JSModuleDef *m = module->async_parent_modules[i]; 28999 JSValue m_obj = JS_NewModuleValue(ctx, m); 29000 js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, 29001 &m_obj); 29002 JS_FreeValue(ctx, m_obj); 29003 } 29004 29005 if (!JS_IsUndefined(module->promise)) { 29006 JSValue ret_val; 29007 assert(module->cycle_root == module); 29008 ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED, 29009 1, &error); 29010 JS_FreeValue(ctx, ret_val); 29011 } 29012 return JS_UNDEFINED; 29013 } 29014 29015 static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, 29016 int argc, JSValueConst *argv, int magic, JSValue *func_data) 29017 { 29018 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); 29019 ExecModuleList exec_list_s, *exec_list = &exec_list_s; 29020 int i; 29021 29022 if (module->status == JS_MODULE_STATUS_EVALUATED) { 29023 assert(module->eval_has_exception); 29024 return JS_UNDEFINED; 29025 } 29026 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); 29027 assert(!module->eval_has_exception); 29028 assert(module->async_evaluation); 29029 module->async_evaluation = FALSE; 29030 js_set_module_evaluated(ctx, module); 29031 29032 exec_list->tab = NULL; 29033 exec_list->count = 0; 29034 exec_list->size = 0; 29035 29036 if (gather_available_ancestors(ctx, module, exec_list) < 0) { 29037 js_free(ctx, exec_list->tab); 29038 return JS_EXCEPTION; 29039 } 29040 29041 /* sort by increasing async_evaluation timestamp */ 29042 rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]), 29043 exec_module_list_cmp, NULL); 29044 29045 for(i = 0; i < exec_list->count; i++) { 29046 JSModuleDef *m = exec_list->tab[i]; 29047 if (m->status == JS_MODULE_STATUS_EVALUATED) { 29048 assert(m->eval_has_exception); 29049 } else if (m->has_tla) { 29050 js_execute_async_module(ctx, m); 29051 } else { 29052 JSValue error; 29053 if (js_execute_sync_module(ctx, m, &error) < 0) { 29054 JSValue m_obj = JS_NewModuleValue(ctx, m); 29055 js_async_module_execution_rejected(ctx, JS_UNDEFINED, 29056 1, (JSValueConst *)&error, 0, 29057 &m_obj); 29058 JS_FreeValue(ctx, m_obj); 29059 JS_FreeValue(ctx, error); 29060 } else { 29061 js_set_module_evaluated(ctx, m); 29062 } 29063 } 29064 } 29065 js_free(ctx, exec_list->tab); 29066 return JS_UNDEFINED; 29067 } 29068 29069 static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) 29070 { 29071 JSValue promise, m_obj; 29072 JSValue resolve_funcs[2], ret_val; 29073 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); 29074 if (JS_IsException(promise)) 29075 return -1; 29076 m_obj = JS_NewModuleValue(ctx, m); 29077 resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); 29078 resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); 29079 ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); 29080 JS_FreeValue(ctx, ret_val); 29081 JS_FreeValue(ctx, m_obj); 29082 JS_FreeValue(ctx, resolve_funcs[0]); 29083 JS_FreeValue(ctx, resolve_funcs[1]); 29084 JS_FreeValue(ctx, promise); 29085 return 0; 29086 } 29087 29088 /* return < 0 in case of exception. *pvalue contains the exception. */ 29089 static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, 29090 JSValue *pvalue) 29091 { 29092 if (m->init_func) { 29093 /* C module init : no asynchronous execution */ 29094 if (m->init_func(ctx, m) < 0) 29095 goto fail; 29096 } else { 29097 JSValue promise; 29098 JSPromiseStateEnum state; 29099 29100 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); 29101 if (JS_IsException(promise)) 29102 goto fail; 29103 state = JS_PromiseState(ctx, promise); 29104 if (state == JS_PROMISE_FULFILLED) { 29105 JS_FreeValue(ctx, promise); 29106 } else if (state == JS_PROMISE_REJECTED) { 29107 *pvalue = JS_PromiseResult(ctx, promise); 29108 JS_FreeValue(ctx, promise); 29109 return -1; 29110 } else { 29111 JS_FreeValue(ctx, promise); 29112 JS_ThrowTypeError(ctx, "promise is pending"); 29113 fail: 29114 *pvalue = JS_GetException(ctx); 29115 return -1; 29116 } 29117 } 29118 *pvalue = JS_UNDEFINED; 29119 return 0; 29120 } 29121 29122 /* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1, 29123 exception) */ 29124 static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, 29125 int index, JSModuleDef **pstack_top, 29126 JSValue *pvalue) 29127 { 29128 JSModuleDef *m1; 29129 int i; 29130 29131 if (js_check_stack_overflow(ctx->rt, 0)) { 29132 JS_ThrowStackOverflow(ctx); 29133 *pvalue = JS_GetException(ctx); 29134 return -1; 29135 } 29136 29137 #ifdef DUMP_MODULE_RESOLVE 29138 { 29139 char buf1[ATOM_GET_STR_BUF_SIZE]; 29140 printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); 29141 } 29142 #endif 29143 29144 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29145 m->status == JS_MODULE_STATUS_EVALUATED) { 29146 if (m->eval_has_exception) { 29147 *pvalue = JS_DupValue(ctx, m->eval_exception); 29148 return -1; 29149 } else { 29150 *pvalue = JS_UNDEFINED; 29151 return index; 29152 } 29153 } 29154 if (m->status == JS_MODULE_STATUS_EVALUATING) { 29155 *pvalue = JS_UNDEFINED; 29156 return index; 29157 } 29158 assert(m->status == JS_MODULE_STATUS_LINKED); 29159 29160 m->status = JS_MODULE_STATUS_EVALUATING; 29161 m->dfs_index = index; 29162 m->dfs_ancestor_index = index; 29163 m->pending_async_dependencies = 0; 29164 index++; 29165 /* push 'm' on stack */ 29166 m->stack_prev = *pstack_top; 29167 *pstack_top = m; 29168 29169 for(i = 0; i < m->req_module_entries_count; i++) { 29170 JSReqModuleEntry *rme = &m->req_module_entries[i]; 29171 m1 = rme->module; 29172 index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue); 29173 if (index < 0) 29174 return -1; 29175 assert(m1->status == JS_MODULE_STATUS_EVALUATING || 29176 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29177 m1->status == JS_MODULE_STATUS_EVALUATED); 29178 if (m1->status == JS_MODULE_STATUS_EVALUATING) { 29179 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, 29180 m1->dfs_ancestor_index); 29181 } else { 29182 m1 = m1->cycle_root; 29183 assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29184 m1->status == JS_MODULE_STATUS_EVALUATED); 29185 if (m1->eval_has_exception) { 29186 *pvalue = JS_DupValue(ctx, m1->eval_exception); 29187 return -1; 29188 } 29189 } 29190 if (m1->async_evaluation) { 29191 m->pending_async_dependencies++; 29192 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)) { 29193 *pvalue = JS_GetException(ctx); 29194 return -1; 29195 } 29196 m1->async_parent_modules[m1->async_parent_modules_count++] = m; 29197 } 29198 } 29199 29200 if (m->pending_async_dependencies > 0) { 29201 assert(!m->async_evaluation); 29202 m->async_evaluation = TRUE; 29203 m->async_evaluation_timestamp = 29204 ctx->rt->module_async_evaluation_next_timestamp++; 29205 } else if (m->has_tla) { 29206 assert(!m->async_evaluation); 29207 m->async_evaluation = TRUE; 29208 m->async_evaluation_timestamp = 29209 ctx->rt->module_async_evaluation_next_timestamp++; 29210 js_execute_async_module(ctx, m); 29211 } else { 29212 if (js_execute_sync_module(ctx, m, pvalue) < 0) 29213 return -1; 29214 } 29215 29216 assert(m->dfs_ancestor_index <= m->dfs_index); 29217 if (m->dfs_index == m->dfs_ancestor_index) { 29218 for(;;) { 29219 /* pop m1 from stack */ 29220 m1 = *pstack_top; 29221 *pstack_top = m1->stack_prev; 29222 if (!m1->async_evaluation) { 29223 m1->status = JS_MODULE_STATUS_EVALUATED; 29224 } else { 29225 m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC; 29226 } 29227 /* spec bug: cycle_root must be assigned before the test */ 29228 m1->cycle_root = m; 29229 if (m1 == m) 29230 break; 29231 } 29232 } 29233 *pvalue = JS_UNDEFINED; 29234 return index; 29235 } 29236 29237 /* Run the <eval> function of the module and of all its requested 29238 modules. Return a promise or an exception. */ 29239 static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) 29240 { 29241 JSModuleDef *m1, *stack_top; 29242 JSValue ret_val, result; 29243 29244 assert(m->status == JS_MODULE_STATUS_LINKED || 29245 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29246 m->status == JS_MODULE_STATUS_EVALUATED); 29247 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29248 m->status == JS_MODULE_STATUS_EVALUATED) { 29249 m = m->cycle_root; 29250 } 29251 /* a promise may be created only on the cycle_root of a cycle */ 29252 if (!JS_IsUndefined(m->promise)) 29253 return JS_DupValue(ctx, m->promise); 29254 m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); 29255 if (JS_IsException(m->promise)) 29256 return JS_EXCEPTION; 29257 29258 stack_top = NULL; 29259 if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) { 29260 while (stack_top != NULL) { 29261 m1 = stack_top; 29262 assert(m1->status == JS_MODULE_STATUS_EVALUATING); 29263 m1->status = JS_MODULE_STATUS_EVALUATED; 29264 m1->eval_has_exception = TRUE; 29265 m1->eval_exception = JS_DupValue(ctx, result); 29266 m1->cycle_root = m; /* spec bug: should be present */ 29267 stack_top = m1->stack_prev; 29268 } 29269 JS_FreeValue(ctx, result); 29270 assert(m->status == JS_MODULE_STATUS_EVALUATED); 29271 assert(m->eval_has_exception); 29272 ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, 29273 1, (JSValueConst *)&m->eval_exception); 29274 JS_FreeValue(ctx, ret_val); 29275 } else { 29276 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || 29277 m->status == JS_MODULE_STATUS_EVALUATED); 29278 assert(!m->eval_has_exception); 29279 if (!m->async_evaluation) { 29280 JSValue value; 29281 assert(m->status == JS_MODULE_STATUS_EVALUATED); 29282 value = JS_UNDEFINED; 29283 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, 29284 1, (JSValueConst *)&value); 29285 JS_FreeValue(ctx, ret_val); 29286 } 29287 assert(stack_top == NULL); 29288 } 29289 return JS_DupValue(ctx, m->promise); 29290 } 29291 29292 static __exception JSAtom js_parse_from_clause(JSParseState *s) 29293 { 29294 JSAtom module_name; 29295 if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { 29296 js_parse_error(s, "from clause expected"); 29297 return JS_ATOM_NULL; 29298 } 29299 if (next_token(s)) 29300 return JS_ATOM_NULL; 29301 if (s->token.val != TOK_STRING) { 29302 js_parse_error(s, "string expected"); 29303 return JS_ATOM_NULL; 29304 } 29305 module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str); 29306 if (module_name == JS_ATOM_NULL) 29307 return JS_ATOM_NULL; 29308 if (next_token(s)) { 29309 JS_FreeAtom(s->ctx, module_name); 29310 return JS_ATOM_NULL; 29311 } 29312 return module_name; 29313 } 29314 29315 static __exception int js_parse_export(JSParseState *s) 29316 { 29317 JSContext *ctx = s->ctx; 29318 JSModuleDef *m = s->cur_func->module; 29319 JSAtom local_name, export_name; 29320 int first_export, idx, i, tok; 29321 JSAtom module_name; 29322 JSExportEntry *me; 29323 29324 if (next_token(s)) 29325 return -1; 29326 29327 tok = s->token.val; 29328 if (tok == TOK_CLASS) { 29329 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED); 29330 } else if (tok == TOK_FUNCTION || 29331 (token_is_pseudo_keyword(s, JS_ATOM_async) && 29332 peek_token(s, TRUE) == TOK_FUNCTION)) { 29333 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, 29334 JS_FUNC_NORMAL, JS_ATOM_NULL, 29335 s->token.ptr, s->token.line_num, 29336 JS_PARSE_EXPORT_NAMED, NULL); 29337 } 29338 29339 if (next_token(s)) 29340 return -1; 29341 29342 switch(tok) { 29343 case '{': 29344 first_export = m->export_entries_count; 29345 while (s->token.val != '}') { 29346 if (!token_is_ident(s->token.val)) { 29347 js_parse_error(s, "identifier expected"); 29348 return -1; 29349 } 29350 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29351 export_name = JS_ATOM_NULL; 29352 if (next_token(s)) 29353 goto fail; 29354 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 29355 if (next_token(s)) 29356 goto fail; 29357 if (!token_is_ident(s->token.val)) { 29358 js_parse_error(s, "identifier expected"); 29359 goto fail; 29360 } 29361 export_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29362 if (next_token(s)) { 29363 fail: 29364 JS_FreeAtom(ctx, local_name); 29365 fail1: 29366 JS_FreeAtom(ctx, export_name); 29367 return -1; 29368 } 29369 } else { 29370 export_name = JS_DupAtom(ctx, local_name); 29371 } 29372 me = add_export_entry(s, m, local_name, export_name, 29373 JS_EXPORT_TYPE_LOCAL); 29374 JS_FreeAtom(ctx, local_name); 29375 JS_FreeAtom(ctx, export_name); 29376 if (!me) 29377 return -1; 29378 if (s->token.val != ',') 29379 break; 29380 if (next_token(s)) 29381 return -1; 29382 } 29383 if (js_parse_expect(s, '}')) 29384 return -1; 29385 if (token_is_pseudo_keyword(s, JS_ATOM_from)) { 29386 module_name = js_parse_from_clause(s); 29387 if (module_name == JS_ATOM_NULL) 29388 return -1; 29389 idx = add_req_module_entry(ctx, m, module_name); 29390 JS_FreeAtom(ctx, module_name); 29391 if (idx < 0) 29392 return -1; 29393 for(i = first_export; i < m->export_entries_count; i++) { 29394 me = &m->export_entries[i]; 29395 me->export_type = JS_EXPORT_TYPE_INDIRECT; 29396 me->u.req_module_idx = idx; 29397 } 29398 } 29399 break; 29400 case '*': 29401 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 29402 /* export ns from */ 29403 if (next_token(s)) 29404 return -1; 29405 if (!token_is_ident(s->token.val)) { 29406 js_parse_error(s, "identifier expected"); 29407 return -1; 29408 } 29409 export_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29410 if (next_token(s)) 29411 goto fail1; 29412 module_name = js_parse_from_clause(s); 29413 if (module_name == JS_ATOM_NULL) 29414 goto fail1; 29415 idx = add_req_module_entry(ctx, m, module_name); 29416 JS_FreeAtom(ctx, module_name); 29417 if (idx < 0) 29418 goto fail1; 29419 me = add_export_entry(s, m, JS_ATOM__star_, export_name, 29420 JS_EXPORT_TYPE_INDIRECT); 29421 JS_FreeAtom(ctx, export_name); 29422 if (!me) 29423 return -1; 29424 me->u.req_module_idx = idx; 29425 } else { 29426 module_name = js_parse_from_clause(s); 29427 if (module_name == JS_ATOM_NULL) 29428 return -1; 29429 idx = add_req_module_entry(ctx, m, module_name); 29430 JS_FreeAtom(ctx, module_name); 29431 if (idx < 0) 29432 return -1; 29433 if (add_star_export_entry(ctx, m, idx) < 0) 29434 return -1; 29435 } 29436 break; 29437 case TOK_DEFAULT: 29438 if (s->token.val == TOK_CLASS) { 29439 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT); 29440 } else if (s->token.val == TOK_FUNCTION || 29441 (token_is_pseudo_keyword(s, JS_ATOM_async) && 29442 peek_token(s, TRUE) == TOK_FUNCTION)) { 29443 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT, 29444 JS_FUNC_NORMAL, JS_ATOM_NULL, 29445 s->token.ptr, s->token.line_num, 29446 JS_PARSE_EXPORT_DEFAULT, NULL); 29447 } else { 29448 if (js_parse_assign_expr(s)) 29449 return -1; 29450 } 29451 /* set the name of anonymous functions */ 29452 set_object_name(s, JS_ATOM_default); 29453 29454 /* store the value in the _default_ global variable and export 29455 it */ 29456 local_name = JS_ATOM__default_; 29457 if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0) 29458 return -1; 29459 emit_op(s, OP_scope_put_var_init); 29460 emit_atom(s, local_name); 29461 emit_u16(s, 0); 29462 29463 if (!add_export_entry(s, m, local_name, JS_ATOM_default, 29464 JS_EXPORT_TYPE_LOCAL)) 29465 return -1; 29466 break; 29467 case TOK_VAR: 29468 case TOK_LET: 29469 case TOK_CONST: 29470 return js_parse_var(s, TRUE, tok, TRUE); 29471 default: 29472 return js_parse_error(s, "invalid export syntax"); 29473 } 29474 return js_parse_expect_semi(s); 29475 } 29476 29477 static int add_closure_var(JSContext *ctx, JSFunctionDef *s, 29478 BOOL is_local, BOOL is_arg, 29479 int var_idx, JSAtom var_name, 29480 BOOL is_const, BOOL is_lexical, 29481 JSVarKindEnum var_kind); 29482 29483 static int add_import(JSParseState *s, JSModuleDef *m, 29484 JSAtom local_name, JSAtom import_name) 29485 { 29486 JSContext *ctx = s->ctx; 29487 int i, var_idx; 29488 JSImportEntry *mi; 29489 BOOL is_local; 29490 29491 if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval) 29492 return js_parse_error(s, "invalid import binding"); 29493 29494 if (local_name != JS_ATOM_default) { 29495 for (i = 0; i < s->cur_func->closure_var_count; i++) { 29496 if (s->cur_func->closure_var[i].var_name == local_name) 29497 return js_parse_error(s, "duplicate import binding"); 29498 } 29499 } 29500 29501 is_local = (import_name == JS_ATOM__star_); 29502 var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE, 29503 m->import_entries_count, 29504 local_name, TRUE, TRUE, FALSE); 29505 if (var_idx < 0) 29506 return -1; 29507 if (js_resize_array(ctx, (void **)&m->import_entries, 29508 sizeof(JSImportEntry), 29509 &m->import_entries_size, 29510 m->import_entries_count + 1)) 29511 return -1; 29512 mi = &m->import_entries[m->import_entries_count++]; 29513 mi->import_name = JS_DupAtom(ctx, import_name); 29514 mi->var_idx = var_idx; 29515 return 0; 29516 } 29517 29518 static __exception int js_parse_import(JSParseState *s) 29519 { 29520 JSContext *ctx = s->ctx; 29521 JSModuleDef *m = s->cur_func->module; 29522 JSAtom local_name, import_name, module_name; 29523 int first_import, i, idx; 29524 29525 if (next_token(s)) 29526 return -1; 29527 29528 first_import = m->import_entries_count; 29529 if (s->token.val == TOK_STRING) { 29530 module_name = JS_ValueToAtom(ctx, s->token.u.str.str); 29531 if (module_name == JS_ATOM_NULL) 29532 return -1; 29533 if (next_token(s)) { 29534 JS_FreeAtom(ctx, module_name); 29535 return -1; 29536 } 29537 } else { 29538 if (s->token.val == TOK_IDENT) { 29539 if (s->token.u.ident.is_reserved) { 29540 return js_parse_error_reserved_identifier(s); 29541 } 29542 /* "default" import */ 29543 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29544 import_name = JS_ATOM_default; 29545 if (next_token(s)) 29546 goto fail; 29547 if (add_import(s, m, local_name, import_name)) 29548 goto fail; 29549 JS_FreeAtom(ctx, local_name); 29550 29551 if (s->token.val != ',') 29552 goto end_import_clause; 29553 if (next_token(s)) 29554 return -1; 29555 } 29556 29557 if (s->token.val == '*') { 29558 /* name space import */ 29559 if (next_token(s)) 29560 return -1; 29561 if (!token_is_pseudo_keyword(s, JS_ATOM_as)) 29562 return js_parse_error(s, "expecting 'as'"); 29563 if (next_token(s)) 29564 return -1; 29565 if (!token_is_ident(s->token.val)) { 29566 js_parse_error(s, "identifier expected"); 29567 return -1; 29568 } 29569 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29570 import_name = JS_ATOM__star_; 29571 if (next_token(s)) 29572 goto fail; 29573 if (add_import(s, m, local_name, import_name)) 29574 goto fail; 29575 JS_FreeAtom(ctx, local_name); 29576 } else if (s->token.val == '{') { 29577 if (next_token(s)) 29578 return -1; 29579 29580 while (s->token.val != '}') { 29581 if (!token_is_ident(s->token.val)) { 29582 js_parse_error(s, "identifier expected"); 29583 return -1; 29584 } 29585 import_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29586 local_name = JS_ATOM_NULL; 29587 if (next_token(s)) 29588 goto fail; 29589 if (token_is_pseudo_keyword(s, JS_ATOM_as)) { 29590 if (next_token(s)) 29591 goto fail; 29592 if (!token_is_ident(s->token.val)) { 29593 js_parse_error(s, "identifier expected"); 29594 goto fail; 29595 } 29596 local_name = JS_DupAtom(ctx, s->token.u.ident.atom); 29597 if (next_token(s)) { 29598 fail: 29599 JS_FreeAtom(ctx, local_name); 29600 JS_FreeAtom(ctx, import_name); 29601 return -1; 29602 } 29603 } else { 29604 local_name = JS_DupAtom(ctx, import_name); 29605 } 29606 if (add_import(s, m, local_name, import_name)) 29607 goto fail; 29608 JS_FreeAtom(ctx, local_name); 29609 JS_FreeAtom(ctx, import_name); 29610 if (s->token.val != ',') 29611 break; 29612 if (next_token(s)) 29613 return -1; 29614 } 29615 if (js_parse_expect(s, '}')) 29616 return -1; 29617 } 29618 end_import_clause: 29619 module_name = js_parse_from_clause(s); 29620 if (module_name == JS_ATOM_NULL) 29621 return -1; 29622 } 29623 idx = add_req_module_entry(ctx, m, module_name); 29624 JS_FreeAtom(ctx, module_name); 29625 if (idx < 0) 29626 return -1; 29627 for(i = first_import; i < m->import_entries_count; i++) 29628 m->import_entries[i].req_module_idx = idx; 29629 29630 return js_parse_expect_semi(s); 29631 } 29632 29633 static __exception int js_parse_source_element(JSParseState *s) 29634 { 29635 JSFunctionDef *fd = s->cur_func; 29636 int tok; 29637 29638 if (s->token.val == TOK_FUNCTION || 29639 (token_is_pseudo_keyword(s, JS_ATOM_async) && 29640 peek_token(s, TRUE) == TOK_FUNCTION)) { 29641 if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT, 29642 JS_FUNC_NORMAL, JS_ATOM_NULL, 29643 s->token.ptr, s->token.line_num)) 29644 return -1; 29645 } else if (s->token.val == TOK_EXPORT && fd->module) { 29646 if (js_parse_export(s)) 29647 return -1; 29648 } else if (s->token.val == TOK_IMPORT && fd->module && 29649 ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) { 29650 /* the peek_token is needed to avoid confusion with ImportCall 29651 (dynamic import) or import.meta */ 29652 if (js_parse_import(s)) 29653 return -1; 29654 } else { 29655 if (js_parse_statement_or_decl(s, DECL_MASK_ALL)) 29656 return -1; 29657 } 29658 return 0; 29659 } 29660 29661 static JSFunctionDef *js_new_function_def(JSContext *ctx, 29662 JSFunctionDef *parent, 29663 BOOL is_eval, 29664 BOOL is_func_expr, 29665 const char *filename, int line_num) 29666 { 29667 JSFunctionDef *fd; 29668 29669 fd = js_mallocz(ctx, sizeof(*fd)); 29670 if (!fd) 29671 return NULL; 29672 29673 fd->ctx = ctx; 29674 init_list_head(&fd->child_list); 29675 29676 /* insert in parent list */ 29677 fd->parent = parent; 29678 fd->parent_cpool_idx = -1; 29679 if (parent) { 29680 list_add_tail(&fd->link, &parent->child_list); 29681 fd->js_mode = parent->js_mode; 29682 fd->parent_scope_level = parent->scope_level; 29683 } 29684 29685 fd->is_eval = is_eval; 29686 fd->is_func_expr = is_func_expr; 29687 js_dbuf_init(ctx, &fd->byte_code); 29688 fd->last_opcode_pos = -1; 29689 fd->func_name = JS_ATOM_NULL; 29690 fd->var_object_idx = -1; 29691 fd->arg_var_object_idx = -1; 29692 fd->arguments_var_idx = -1; 29693 fd->arguments_arg_idx = -1; 29694 fd->func_var_idx = -1; 29695 fd->eval_ret_idx = -1; 29696 fd->this_var_idx = -1; 29697 fd->new_target_var_idx = -1; 29698 fd->this_active_func_var_idx = -1; 29699 fd->home_object_var_idx = -1; 29700 29701 /* XXX: should distinguish arg, var and var object and body scopes */ 29702 fd->scopes = fd->def_scope_array; 29703 fd->scope_size = countof(fd->def_scope_array); 29704 fd->scope_count = 1; 29705 fd->scopes[0].first = -1; 29706 fd->scopes[0].parent = -1; 29707 fd->scope_level = 0; /* 0: var/arg scope */ 29708 fd->scope_first = -1; 29709 fd->body_scope = -1; 29710 29711 fd->filename = JS_NewAtom(ctx, filename); 29712 fd->line_num = line_num; 29713 29714 js_dbuf_init(ctx, &fd->pc2line); 29715 //fd->pc2line_last_line_num = line_num; 29716 //fd->pc2line_last_pc = 0; 29717 fd->last_opcode_line_num = line_num; 29718 29719 return fd; 29720 } 29721 29722 static void free_bytecode_atoms(JSRuntime *rt, 29723 const uint8_t *bc_buf, int bc_len, 29724 BOOL use_short_opcodes) 29725 { 29726 int pos, len, op; 29727 JSAtom atom; 29728 const JSOpCode *oi; 29729 29730 pos = 0; 29731 while (pos < bc_len) { 29732 op = bc_buf[pos]; 29733 if (use_short_opcodes) 29734 oi = &short_opcode_info(op); 29735 else 29736 oi = &opcode_info[op]; 29737 29738 len = oi->size; 29739 switch(oi->fmt) { 29740 case OP_FMT_atom: 29741 case OP_FMT_atom_u8: 29742 case OP_FMT_atom_u16: 29743 case OP_FMT_atom_label_u8: 29744 case OP_FMT_atom_label_u16: 29745 atom = get_u32(bc_buf + pos + 1); 29746 JS_FreeAtomRT(rt, atom); 29747 break; 29748 default: 29749 break; 29750 } 29751 pos += len; 29752 } 29753 } 29754 29755 static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd) 29756 { 29757 int i; 29758 struct list_head *el, *el1; 29759 29760 /* free the child functions */ 29761 list_for_each_safe(el, el1, &fd->child_list) { 29762 JSFunctionDef *fd1; 29763 fd1 = list_entry(el, JSFunctionDef, link); 29764 js_free_function_def(ctx, fd1); 29765 } 29766 29767 free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size, 29768 fd->use_short_opcodes); 29769 dbuf_free(&fd->byte_code); 29770 js_free(ctx, fd->jump_slots); 29771 js_free(ctx, fd->label_slots); 29772 js_free(ctx, fd->line_number_slots); 29773 29774 for(i = 0; i < fd->cpool_count; i++) { 29775 JS_FreeValue(ctx, fd->cpool[i]); 29776 } 29777 js_free(ctx, fd->cpool); 29778 29779 JS_FreeAtom(ctx, fd->func_name); 29780 29781 for(i = 0; i < fd->var_count; i++) { 29782 JS_FreeAtom(ctx, fd->vars[i].var_name); 29783 } 29784 js_free(ctx, fd->vars); 29785 for(i = 0; i < fd->arg_count; i++) { 29786 JS_FreeAtom(ctx, fd->args[i].var_name); 29787 } 29788 js_free(ctx, fd->args); 29789 29790 for(i = 0; i < fd->global_var_count; i++) { 29791 JS_FreeAtom(ctx, fd->global_vars[i].var_name); 29792 } 29793 js_free(ctx, fd->global_vars); 29794 29795 for(i = 0; i < fd->closure_var_count; i++) { 29796 JSClosureVar *cv = &fd->closure_var[i]; 29797 JS_FreeAtom(ctx, cv->var_name); 29798 } 29799 js_free(ctx, fd->closure_var); 29800 29801 if (fd->scopes != fd->def_scope_array) 29802 js_free(ctx, fd->scopes); 29803 29804 JS_FreeAtom(ctx, fd->filename); 29805 dbuf_free(&fd->pc2line); 29806 29807 js_free(ctx, fd->source); 29808 29809 if (fd->parent) { 29810 /* remove in parent list */ 29811 list_del(&fd->link); 29812 } 29813 js_free(ctx, fd); 29814 } 29815 29816 #ifdef DUMP_BYTECODE 29817 static const char *skip_lines(const char *p, int n) { 29818 while (n-- > 0 && *p) { 29819 while (*p && *p++ != '\n') 29820 continue; 29821 } 29822 return p; 29823 } 29824 29825 static void print_lines(const char *source, int line, int line1) { 29826 const char *s = source; 29827 const char *p = skip_lines(s, line); 29828 if (*p) { 29829 while (line++ < line1) { 29830 p = skip_lines(s = p, 1); 29831 printf(";; %.*s", (int)(p - s), s); 29832 if (!*p) { 29833 if (p[-1] != '\n') 29834 printf("\n"); 29835 break; 29836 } 29837 } 29838 } 29839 } 29840 29841 static void dump_byte_code(JSContext *ctx, int pass, 29842 const uint8_t *tab, int len, 29843 const JSVarDef *args, int arg_count, 29844 const JSVarDef *vars, int var_count, 29845 const JSClosureVar *closure_var, int closure_var_count, 29846 const JSValue *cpool, uint32_t cpool_count, 29847 const char *source, int line_num, 29848 const LabelSlot *label_slots, JSFunctionBytecode *b) 29849 { 29850 const JSOpCode *oi; 29851 int pos, pos_next, op, size, idx, addr, line, line1, in_source; 29852 uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits)); 29853 BOOL use_short_opcodes = (b != NULL); 29854 29855 /* scan for jump targets */ 29856 for (pos = 0; pos < len; pos = pos_next) { 29857 op = tab[pos]; 29858 if (use_short_opcodes) 29859 oi = &short_opcode_info(op); 29860 else 29861 oi = &opcode_info[op]; 29862 pos_next = pos + oi->size; 29863 if (op < OP_COUNT) { 29864 switch (oi->fmt) { 29865 #if SHORT_OPCODES 29866 case OP_FMT_label8: 29867 pos++; 29868 addr = (int8_t)tab[pos]; 29869 goto has_addr; 29870 case OP_FMT_label16: 29871 pos++; 29872 addr = (int16_t)get_u16(tab + pos); 29873 goto has_addr; 29874 #endif 29875 case OP_FMT_atom_label_u8: 29876 case OP_FMT_atom_label_u16: 29877 pos += 4; 29878 /* fall thru */ 29879 case OP_FMT_label: 29880 case OP_FMT_label_u16: 29881 pos++; 29882 addr = get_u32(tab + pos); 29883 goto has_addr; 29884 has_addr: 29885 if (pass == 1) 29886 addr = label_slots[addr].pos; 29887 if (pass == 2) 29888 addr = label_slots[addr].pos2; 29889 if (pass == 3) 29890 addr += pos; 29891 if (addr >= 0 && addr < len) 29892 bits[addr] |= 1; 29893 break; 29894 } 29895 } 29896 } 29897 in_source = 0; 29898 if (source) { 29899 /* Always print first line: needed if single line */ 29900 print_lines(source, 0, 1); 29901 in_source = 1; 29902 } 29903 line1 = line = 1; 29904 pos = 0; 29905 while (pos < len) { 29906 op = tab[pos]; 29907 if (source) { 29908 if (b) { 29909 line1 = find_line_num(ctx, b, pos) - line_num + 1; 29910 } else if (op == OP_line_num) { 29911 line1 = get_u32(tab + pos + 1) - line_num + 1; 29912 } 29913 if (line1 > line) { 29914 if (!in_source) 29915 printf("\n"); 29916 in_source = 1; 29917 print_lines(source, line, line1); 29918 line = line1; 29919 //bits[pos] |= 2; 29920 } 29921 } 29922 if (in_source) 29923 printf("\n"); 29924 in_source = 0; 29925 if (op >= OP_COUNT) { 29926 printf("invalid opcode (0x%02x)\n", op); 29927 pos++; 29928 continue; 29929 } 29930 if (use_short_opcodes) 29931 oi = &short_opcode_info(op); 29932 else 29933 oi = &opcode_info[op]; 29934 size = oi->size; 29935 if (pos + size > len) { 29936 printf("truncated opcode (0x%02x)\n", op); 29937 break; 29938 } 29939 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16) 29940 { 29941 int i, x, x0; 29942 x = x0 = printf("%5d ", pos); 29943 for (i = 0; i < size; i++) { 29944 if (i == 6) { 29945 printf("\n%*s", x = x0, ""); 29946 } 29947 x += printf(" %02X", tab[pos + i]); 29948 } 29949 printf("%*s", x0 + 20 - x, ""); 29950 } 29951 #endif 29952 if (bits[pos]) { 29953 printf("%5d: ", pos); 29954 } else { 29955 printf(" "); 29956 } 29957 printf("%s", oi->name); 29958 pos++; 29959 switch(oi->fmt) { 29960 case OP_FMT_none_int: 29961 printf(" %d", op - OP_push_0); 29962 break; 29963 case OP_FMT_npopx: 29964 printf(" %d", op - OP_call0); 29965 break; 29966 case OP_FMT_u8: 29967 printf(" %u", get_u8(tab + pos)); 29968 break; 29969 case OP_FMT_i8: 29970 printf(" %d", get_i8(tab + pos)); 29971 break; 29972 case OP_FMT_u16: 29973 case OP_FMT_npop: 29974 printf(" %u", get_u16(tab + pos)); 29975 break; 29976 case OP_FMT_npop_u16: 29977 printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2)); 29978 break; 29979 case OP_FMT_i16: 29980 printf(" %d", get_i16(tab + pos)); 29981 break; 29982 case OP_FMT_i32: 29983 printf(" %d", get_i32(tab + pos)); 29984 break; 29985 case OP_FMT_u32: 29986 printf(" %u", get_u32(tab + pos)); 29987 break; 29988 #if SHORT_OPCODES 29989 case OP_FMT_label8: 29990 addr = get_i8(tab + pos); 29991 goto has_addr1; 29992 case OP_FMT_label16: 29993 addr = get_i16(tab + pos); 29994 goto has_addr1; 29995 #endif 29996 case OP_FMT_label: 29997 addr = get_u32(tab + pos); 29998 goto has_addr1; 29999 has_addr1: 30000 if (pass == 1) 30001 printf(" %u:%u", addr, label_slots[addr].pos); 30002 if (pass == 2) 30003 printf(" %u:%u", addr, label_slots[addr].pos2); 30004 if (pass == 3) 30005 printf(" %u", addr + pos); 30006 break; 30007 case OP_FMT_label_u16: 30008 addr = get_u32(tab + pos); 30009 if (pass == 1) 30010 printf(" %u:%u", addr, label_slots[addr].pos); 30011 if (pass == 2) 30012 printf(" %u:%u", addr, label_slots[addr].pos2); 30013 if (pass == 3) 30014 printf(" %u", addr + pos); 30015 printf(",%u", get_u16(tab + pos + 4)); 30016 break; 30017 #if SHORT_OPCODES 30018 case OP_FMT_const8: 30019 idx = get_u8(tab + pos); 30020 goto has_pool_idx; 30021 #endif 30022 case OP_FMT_const: 30023 idx = get_u32(tab + pos); 30024 goto has_pool_idx; 30025 has_pool_idx: 30026 printf(" %u: ", idx); 30027 if (idx < cpool_count) { 30028 JS_DumpValue(ctx, cpool[idx]); 30029 } 30030 break; 30031 case OP_FMT_atom: 30032 printf(" "); 30033 print_atom(ctx, get_u32(tab + pos)); 30034 break; 30035 case OP_FMT_atom_u8: 30036 printf(" "); 30037 print_atom(ctx, get_u32(tab + pos)); 30038 printf(",%d", get_u8(tab + pos + 4)); 30039 break; 30040 case OP_FMT_atom_u16: 30041 printf(" "); 30042 print_atom(ctx, get_u32(tab + pos)); 30043 printf(",%d", get_u16(tab + pos + 4)); 30044 break; 30045 case OP_FMT_atom_label_u8: 30046 case OP_FMT_atom_label_u16: 30047 printf(" "); 30048 print_atom(ctx, get_u32(tab + pos)); 30049 addr = get_u32(tab + pos + 4); 30050 if (pass == 1) 30051 printf(",%u:%u", addr, label_slots[addr].pos); 30052 if (pass == 2) 30053 printf(",%u:%u", addr, label_slots[addr].pos2); 30054 if (pass == 3) 30055 printf(",%u", addr + pos + 4); 30056 if (oi->fmt == OP_FMT_atom_label_u8) 30057 printf(",%u", get_u8(tab + pos + 8)); 30058 else 30059 printf(",%u", get_u16(tab + pos + 8)); 30060 break; 30061 case OP_FMT_none_loc: 30062 idx = (op - OP_get_loc0) % 4; 30063 goto has_loc; 30064 case OP_FMT_loc8: 30065 idx = get_u8(tab + pos); 30066 goto has_loc; 30067 case OP_FMT_loc: 30068 idx = get_u16(tab + pos); 30069 has_loc: 30070 printf(" %d: ", idx); 30071 if (idx < var_count) { 30072 print_atom(ctx, vars[idx].var_name); 30073 } 30074 break; 30075 case OP_FMT_none_arg: 30076 idx = (op - OP_get_arg0) % 4; 30077 goto has_arg; 30078 case OP_FMT_arg: 30079 idx = get_u16(tab + pos); 30080 has_arg: 30081 printf(" %d: ", idx); 30082 if (idx < arg_count) { 30083 print_atom(ctx, args[idx].var_name); 30084 } 30085 break; 30086 case OP_FMT_none_var_ref: 30087 idx = (op - OP_get_var_ref0) % 4; 30088 goto has_var_ref; 30089 case OP_FMT_var_ref: 30090 idx = get_u16(tab + pos); 30091 has_var_ref: 30092 printf(" %d: ", idx); 30093 if (idx < closure_var_count) { 30094 print_atom(ctx, closure_var[idx].var_name); 30095 } 30096 break; 30097 default: 30098 break; 30099 } 30100 printf("\n"); 30101 pos += oi->size - 1; 30102 } 30103 if (source) { 30104 if (!in_source) 30105 printf("\n"); 30106 print_lines(source, line, INT32_MAX); 30107 } 30108 js_free(ctx, bits); 30109 } 30110 30111 static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len, 30112 int line_num) 30113 { 30114 const uint8_t *p_end, *p_next, *p; 30115 int pc, v; 30116 unsigned int op; 30117 30118 if (len <= 0) 30119 return; 30120 30121 printf("%5s %5s\n", "PC", "LINE"); 30122 30123 p = buf; 30124 p_end = buf + len; 30125 pc = 0; 30126 while (p < p_end) { 30127 op = *p++; 30128 if (op == 0) { 30129 v = unicode_from_utf8(p, p_end - p, &p_next); 30130 if (v < 0) 30131 goto fail; 30132 pc += v; 30133 p = p_next; 30134 v = unicode_from_utf8(p, p_end - p, &p_next); 30135 if (v < 0) { 30136 fail: 30137 printf("invalid pc2line encode pos=%d\n", (int)(p - buf)); 30138 return; 30139 } 30140 if (!(v & 1)) { 30141 v = v >> 1; 30142 } else { 30143 v = -(v >> 1) - 1; 30144 } 30145 line_num += v; 30146 p = p_next; 30147 } else { 30148 op -= PC2LINE_OP_FIRST; 30149 pc += (op / PC2LINE_RANGE); 30150 line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE; 30151 } 30152 printf("%5d %5d\n", pc, line_num); 30153 } 30154 } 30155 30156 static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b) 30157 { 30158 int i; 30159 char atom_buf[ATOM_GET_STR_BUF_SIZE]; 30160 const char *str; 30161 30162 if (b->has_debug && b->debug.filename != JS_ATOM_NULL) { 30163 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename); 30164 printf("%s:%d: ", str, b->debug.line_num); 30165 } 30166 30167 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name); 30168 printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str); 30169 if (b->js_mode) { 30170 printf(" mode:"); 30171 if (b->js_mode & JS_MODE_STRICT) 30172 printf(" strict"); 30173 #ifdef CONFIG_BIGNUM 30174 if (b->js_mode & JS_MODE_MATH) 30175 printf(" math"); 30176 #endif 30177 printf("\n"); 30178 } 30179 if (b->arg_count && b->vardefs) { 30180 printf(" args:"); 30181 for(i = 0; i < b->arg_count; i++) { 30182 printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), 30183 b->vardefs[i].var_name)); 30184 } 30185 printf("\n"); 30186 } 30187 if (b->var_count && b->vardefs) { 30188 printf(" locals:\n"); 30189 for(i = 0; i < b->var_count; i++) { 30190 JSVarDef *vd = &b->vardefs[b->arg_count + i]; 30191 printf("%5d: %s %s", i, 30192 vd->var_kind == JS_VAR_CATCH ? "catch" : 30193 (vd->var_kind == JS_VAR_FUNCTION_DECL || 30194 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" : 30195 vd->is_const ? "const" : 30196 vd->is_lexical ? "let" : "var", 30197 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name)); 30198 if (vd->scope_level) 30199 printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next); 30200 printf("\n"); 30201 } 30202 } 30203 if (b->closure_var_count) { 30204 printf(" closure vars:\n"); 30205 for(i = 0; i < b->closure_var_count; i++) { 30206 JSClosureVar *cv = &b->closure_var[i]; 30207 printf("%5d: %s %s:%s%d %s\n", i, 30208 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name), 30209 cv->is_local ? "local" : "parent", 30210 cv->is_arg ? "arg" : "loc", cv->var_idx, 30211 cv->is_const ? "const" : 30212 cv->is_lexical ? "let" : "var"); 30213 } 30214 } 30215 printf(" stack_size: %d\n", b->stack_size); 30216 printf(" opcodes:\n"); 30217 dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len, 30218 b->vardefs, b->arg_count, 30219 b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count, 30220 b->closure_var, b->closure_var_count, 30221 b->cpool, b->cpool_count, 30222 b->has_debug ? b->debug.source : NULL, 30223 b->has_debug ? b->debug.line_num : -1, NULL, b); 30224 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32) 30225 if (b->has_debug) 30226 dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num); 30227 #endif 30228 printf("\n"); 30229 } 30230 #endif 30231 30232 static int add_closure_var(JSContext *ctx, JSFunctionDef *s, 30233 BOOL is_local, BOOL is_arg, 30234 int var_idx, JSAtom var_name, 30235 BOOL is_const, BOOL is_lexical, 30236 JSVarKindEnum var_kind) 30237 { 30238 JSClosureVar *cv; 30239 30240 /* the closure variable indexes are currently stored on 16 bits */ 30241 if (s->closure_var_count >= JS_MAX_LOCAL_VARS) { 30242 JS_ThrowInternalError(ctx, "too many closure variables"); 30243 return -1; 30244 } 30245 30246 if (js_resize_array(ctx, (void **)&s->closure_var, 30247 sizeof(s->closure_var[0]), 30248 &s->closure_var_size, s->closure_var_count + 1)) 30249 return -1; 30250 cv = &s->closure_var[s->closure_var_count++]; 30251 cv->is_local = is_local; 30252 cv->is_arg = is_arg; 30253 cv->is_const = is_const; 30254 cv->is_lexical = is_lexical; 30255 cv->var_kind = var_kind; 30256 cv->var_idx = var_idx; 30257 cv->var_name = JS_DupAtom(ctx, var_name); 30258 return s->closure_var_count - 1; 30259 } 30260 30261 static int find_closure_var(JSContext *ctx, JSFunctionDef *s, 30262 JSAtom var_name) 30263 { 30264 int i; 30265 for(i = 0; i < s->closure_var_count; i++) { 30266 JSClosureVar *cv = &s->closure_var[i]; 30267 if (cv->var_name == var_name) 30268 return i; 30269 } 30270 return -1; 30271 } 30272 30273 /* 'fd' must be a parent of 's'. Create in 's' a closure referencing a 30274 local variable (is_local = TRUE) or a closure (is_local = FALSE) in 30275 'fd' */ 30276 static int get_closure_var2(JSContext *ctx, JSFunctionDef *s, 30277 JSFunctionDef *fd, BOOL is_local, 30278 BOOL is_arg, int var_idx, JSAtom var_name, 30279 BOOL is_const, BOOL is_lexical, 30280 JSVarKindEnum var_kind) 30281 { 30282 int i; 30283 30284 if (fd != s->parent) { 30285 var_idx = get_closure_var2(ctx, s->parent, fd, is_local, 30286 is_arg, var_idx, var_name, 30287 is_const, is_lexical, var_kind); 30288 if (var_idx < 0) 30289 return -1; 30290 is_local = FALSE; 30291 } 30292 for(i = 0; i < s->closure_var_count; i++) { 30293 JSClosureVar *cv = &s->closure_var[i]; 30294 if (cv->var_idx == var_idx && cv->is_arg == is_arg && 30295 cv->is_local == is_local) 30296 return i; 30297 } 30298 return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name, 30299 is_const, is_lexical, var_kind); 30300 } 30301 30302 static int get_closure_var(JSContext *ctx, JSFunctionDef *s, 30303 JSFunctionDef *fd, BOOL is_arg, 30304 int var_idx, JSAtom var_name, 30305 BOOL is_const, BOOL is_lexical, 30306 JSVarKindEnum var_kind) 30307 { 30308 return get_closure_var2(ctx, s, fd, TRUE, is_arg, 30309 var_idx, var_name, is_const, is_lexical, 30310 var_kind); 30311 } 30312 30313 static int get_with_scope_opcode(int op) 30314 { 30315 if (op == OP_scope_get_var_undef) 30316 return OP_with_get_var; 30317 else 30318 return OP_with_get_var + (op - OP_scope_get_var); 30319 } 30320 30321 static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos) 30322 { 30323 int opcode = bc_buf[pos]; 30324 return (bc_buf[pos + 1] == OP_put_ref_value && 30325 (opcode == OP_insert3 || 30326 opcode == OP_perm4 || 30327 opcode == OP_nop || 30328 opcode == OP_rot3l)); 30329 } 30330 30331 static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos) 30332 { 30333 int opcode = bc_buf[pos]; 30334 return (bc_buf[pos + 1] == OP_put_ref_value && 30335 (opcode == OP_insert3 || 30336 opcode == OP_perm4 || 30337 opcode == OP_nop || 30338 opcode == OP_rot3l)); 30339 } 30340 30341 static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s, 30342 DynBuf *bc, uint8_t *bc_buf, 30343 LabelSlot *ls, int pos_next, 30344 int get_op, int var_idx) 30345 { 30346 int label_pos, end_pos, pos; 30347 30348 /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)` 30349 but only if expr does not modify `a`. 30350 should scan the code between pos_next and label_pos 30351 for operations that can potentially change `a`: 30352 OP_scope_make_ref(a), function calls, jumps and gosub. 30353 */ 30354 /* replace the reference get/put with normal variable 30355 accesses */ 30356 if (bc_buf[pos_next] == OP_get_ref_value) { 30357 dbuf_putc(bc, get_op); 30358 dbuf_put_u16(bc, var_idx); 30359 pos_next++; 30360 } 30361 /* remove the OP_label to make room for replacement */ 30362 /* label should have a refcount of 0 anyway */ 30363 /* XXX: should avoid this patch by inserting nops in phase 1 */ 30364 label_pos = ls->pos; 30365 pos = label_pos - 5; 30366 assert(bc_buf[pos] == OP_label); 30367 /* label points to an instruction pair: 30368 - insert3 / put_ref_value 30369 - perm4 / put_ref_value 30370 - rot3l / put_ref_value 30371 - nop / put_ref_value 30372 */ 30373 end_pos = label_pos + 2; 30374 if (bc_buf[label_pos] == OP_insert3) 30375 bc_buf[pos++] = OP_dup; 30376 bc_buf[pos] = get_op + 1; 30377 put_u16(bc_buf + pos + 1, var_idx); 30378 pos += 3; 30379 /* pad with OP_nop */ 30380 while (pos < end_pos) 30381 bc_buf[pos++] = OP_nop; 30382 return pos_next; 30383 } 30384 30385 static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s, 30386 DynBuf *bc, uint8_t *bc_buf, 30387 LabelSlot *ls, int pos_next, 30388 JSAtom var_name) 30389 { 30390 int label_pos, end_pos, pos, op; 30391 BOOL is_strict; 30392 is_strict = ((s->js_mode & JS_MODE_STRICT) != 0); 30393 30394 /* replace the reference get/put with normal variable 30395 accesses */ 30396 if (is_strict) { 30397 /* need to check if the variable exists before evaluating the right 30398 expression */ 30399 /* XXX: need an extra OP_true if destructuring an array */ 30400 dbuf_putc(bc, OP_check_var); 30401 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30402 } else { 30403 /* XXX: need 2 extra OP_true if destructuring an array */ 30404 } 30405 if (bc_buf[pos_next] == OP_get_ref_value) { 30406 dbuf_putc(bc, OP_get_var); 30407 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30408 pos_next++; 30409 } 30410 /* remove the OP_label to make room for replacement */ 30411 /* label should have a refcount of 0 anyway */ 30412 /* XXX: should have emitted several OP_nop to avoid this kludge */ 30413 label_pos = ls->pos; 30414 pos = label_pos - 5; 30415 assert(bc_buf[pos] == OP_label); 30416 end_pos = label_pos + 2; 30417 op = bc_buf[label_pos]; 30418 if (is_strict) { 30419 if (op != OP_nop) { 30420 switch(op) { 30421 case OP_insert3: 30422 op = OP_insert2; 30423 break; 30424 case OP_perm4: 30425 op = OP_perm3; 30426 break; 30427 case OP_rot3l: 30428 op = OP_swap; 30429 break; 30430 default: 30431 abort(); 30432 } 30433 bc_buf[pos++] = op; 30434 } 30435 } else { 30436 if (op == OP_insert3) 30437 bc_buf[pos++] = OP_dup; 30438 } 30439 if (is_strict) { 30440 bc_buf[pos] = OP_put_var_strict; 30441 /* XXX: need 1 extra OP_drop if destructuring an array */ 30442 } else { 30443 bc_buf[pos] = OP_put_var; 30444 /* XXX: need 2 extra OP_drop if destructuring an array */ 30445 } 30446 put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name)); 30447 pos += 5; 30448 /* pad with OP_nop */ 30449 while (pos < end_pos) 30450 bc_buf[pos++] = OP_nop; 30451 return pos_next; 30452 } 30453 30454 static int add_var_this(JSContext *ctx, JSFunctionDef *fd) 30455 { 30456 int idx; 30457 idx = add_var(ctx, fd, JS_ATOM_this); 30458 if (idx >= 0 && fd->is_derived_class_constructor) { 30459 JSVarDef *vd = &fd->vars[idx]; 30460 /* XXX: should have is_this flag or var type */ 30461 vd->is_lexical = 1; /* used to trigger 'uninitialized' checks 30462 in a derived class constructor */ 30463 } 30464 return idx; 30465 } 30466 30467 static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s, 30468 JSAtom var_name) 30469 { 30470 int var_idx; 30471 30472 if (!s->has_this_binding) 30473 return -1; 30474 switch(var_name) { 30475 case JS_ATOM_home_object: 30476 /* 'home_object' pseudo variable */ 30477 if (s->home_object_var_idx < 0) 30478 s->home_object_var_idx = add_var(ctx, s, var_name); 30479 var_idx = s->home_object_var_idx; 30480 break; 30481 case JS_ATOM_this_active_func: 30482 /* 'this.active_func' pseudo variable */ 30483 if (s->this_active_func_var_idx < 0) 30484 s->this_active_func_var_idx = add_var(ctx, s, var_name); 30485 var_idx = s->this_active_func_var_idx; 30486 break; 30487 case JS_ATOM_new_target: 30488 /* 'new.target' pseudo variable */ 30489 if (s->new_target_var_idx < 0) 30490 s->new_target_var_idx = add_var(ctx, s, var_name); 30491 var_idx = s->new_target_var_idx; 30492 break; 30493 case JS_ATOM_this: 30494 /* 'this' pseudo variable */ 30495 if (s->this_var_idx < 0) 30496 s->this_var_idx = add_var_this(ctx, s); 30497 var_idx = s->this_var_idx; 30498 break; 30499 default: 30500 var_idx = -1; 30501 break; 30502 } 30503 return var_idx; 30504 } 30505 30506 /* test if 'var_name' is in the variable object on the stack. If is it 30507 the case, handle it and jump to 'label_done' */ 30508 static void var_object_test(JSContext *ctx, JSFunctionDef *s, 30509 JSAtom var_name, int op, DynBuf *bc, 30510 int *plabel_done, BOOL is_with) 30511 { 30512 dbuf_putc(bc, get_with_scope_opcode(op)); 30513 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30514 *plabel_done = new_label_fd(s, *plabel_done); 30515 dbuf_put_u32(bc, *plabel_done); 30516 dbuf_putc(bc, is_with); 30517 update_label(s, *plabel_done, 1); 30518 s->jump_size++; 30519 } 30520 30521 /* return the position of the next opcode */ 30522 static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, 30523 JSAtom var_name, int scope_level, int op, 30524 DynBuf *bc, uint8_t *bc_buf, 30525 LabelSlot *ls, int pos_next) 30526 { 30527 int idx, var_idx, is_put; 30528 int label_done; 30529 JSFunctionDef *fd; 30530 JSVarDef *vd; 30531 BOOL is_pseudo_var, is_arg_scope; 30532 30533 label_done = -1; 30534 30535 /* XXX: could be simpler to use a specific function to 30536 resolve the pseudo variables */ 30537 is_pseudo_var = (var_name == JS_ATOM_home_object || 30538 var_name == JS_ATOM_this_active_func || 30539 var_name == JS_ATOM_new_target || 30540 var_name == JS_ATOM_this); 30541 30542 /* resolve local scoped variables */ 30543 var_idx = -1; 30544 for (idx = s->scopes[scope_level].first; idx >= 0;) { 30545 vd = &s->vars[idx]; 30546 if (vd->var_name == var_name) { 30547 if (op == OP_scope_put_var || op == OP_scope_make_ref) { 30548 if (vd->is_const) { 30549 dbuf_putc(bc, OP_throw_error); 30550 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30551 dbuf_putc(bc, JS_THROW_VAR_RO); 30552 goto done; 30553 } 30554 } 30555 var_idx = idx; 30556 break; 30557 } else 30558 if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { 30559 dbuf_putc(bc, OP_get_loc); 30560 dbuf_put_u16(bc, idx); 30561 var_object_test(ctx, s, var_name, op, bc, &label_done, 1); 30562 } 30563 idx = vd->scope_next; 30564 } 30565 is_arg_scope = (idx == ARG_SCOPE_END); 30566 if (var_idx < 0) { 30567 /* argument scope: variables are not visible but pseudo 30568 variables are visible */ 30569 if (!is_arg_scope) { 30570 var_idx = find_var(ctx, s, var_name); 30571 } 30572 30573 if (var_idx < 0 && is_pseudo_var) 30574 var_idx = resolve_pseudo_var(ctx, s, var_name); 30575 30576 if (var_idx < 0 && var_name == JS_ATOM_arguments && 30577 s->has_arguments_binding) { 30578 /* 'arguments' pseudo variable */ 30579 var_idx = add_arguments_var(ctx, s); 30580 } 30581 if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) { 30582 /* add a new variable with the function name */ 30583 var_idx = add_func_var(ctx, s, var_name); 30584 } 30585 } 30586 if (var_idx >= 0) { 30587 if ((op == OP_scope_put_var || op == OP_scope_make_ref) && 30588 !(var_idx & ARGUMENT_VAR_OFFSET) && 30589 s->vars[var_idx].is_const) { 30590 /* only happens when assigning a function expression name 30591 in strict mode */ 30592 dbuf_putc(bc, OP_throw_error); 30593 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30594 dbuf_putc(bc, JS_THROW_VAR_RO); 30595 goto done; 30596 } 30597 /* OP_scope_put_var_init is only used to initialize a 30598 lexical variable, so it is never used in a with or var object. It 30599 can be used with a closure (module global variable case). */ 30600 switch (op) { 30601 case OP_scope_make_ref: 30602 if (!(var_idx & ARGUMENT_VAR_OFFSET) && 30603 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) { 30604 /* Create a dummy object reference for the func_var */ 30605 dbuf_putc(bc, OP_object); 30606 dbuf_putc(bc, OP_get_loc); 30607 dbuf_put_u16(bc, var_idx); 30608 dbuf_putc(bc, OP_define_field); 30609 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30610 dbuf_putc(bc, OP_push_atom_value); 30611 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30612 } else 30613 if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) { 30614 int get_op; 30615 if (var_idx & ARGUMENT_VAR_OFFSET) { 30616 get_op = OP_get_arg; 30617 var_idx -= ARGUMENT_VAR_OFFSET; 30618 } else { 30619 if (s->vars[var_idx].is_lexical) 30620 get_op = OP_get_loc_check; 30621 else 30622 get_op = OP_get_loc; 30623 } 30624 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, 30625 pos_next, get_op, var_idx); 30626 } else { 30627 /* Create a dummy object with a named slot that is 30628 a reference to the local variable */ 30629 if (var_idx & ARGUMENT_VAR_OFFSET) { 30630 dbuf_putc(bc, OP_make_arg_ref); 30631 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30632 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); 30633 } else { 30634 dbuf_putc(bc, OP_make_loc_ref); 30635 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30636 dbuf_put_u16(bc, var_idx); 30637 } 30638 } 30639 break; 30640 case OP_scope_get_ref: 30641 dbuf_putc(bc, OP_undefined); 30642 /* fall thru */ 30643 case OP_scope_get_var_checkthis: 30644 case OP_scope_get_var_undef: 30645 case OP_scope_get_var: 30646 case OP_scope_put_var: 30647 case OP_scope_put_var_init: 30648 is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init); 30649 if (var_idx & ARGUMENT_VAR_OFFSET) { 30650 dbuf_putc(bc, OP_get_arg + is_put); 30651 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET); 30652 } else { 30653 if (is_put) { 30654 if (s->vars[var_idx].is_lexical) { 30655 if (op == OP_scope_put_var_init) { 30656 /* 'this' can only be initialized once */ 30657 if (var_name == JS_ATOM_this) 30658 dbuf_putc(bc, OP_put_loc_check_init); 30659 else 30660 dbuf_putc(bc, OP_put_loc); 30661 } else { 30662 dbuf_putc(bc, OP_put_loc_check); 30663 } 30664 } else { 30665 dbuf_putc(bc, OP_put_loc); 30666 } 30667 } else { 30668 if (s->vars[var_idx].is_lexical) { 30669 if (op == OP_scope_get_var_checkthis) { 30670 /* only used for 'this' return in derived class constructors */ 30671 dbuf_putc(bc, OP_get_loc_checkthis); 30672 } else { 30673 dbuf_putc(bc, OP_get_loc_check); 30674 } 30675 } else { 30676 dbuf_putc(bc, OP_get_loc); 30677 } 30678 } 30679 dbuf_put_u16(bc, var_idx); 30680 } 30681 break; 30682 case OP_scope_delete_var: 30683 dbuf_putc(bc, OP_push_false); 30684 break; 30685 } 30686 goto done; 30687 } 30688 /* check eval object */ 30689 if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) { 30690 dbuf_putc(bc, OP_get_loc); 30691 dbuf_put_u16(bc, s->var_object_idx); 30692 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 30693 } 30694 /* check eval object in argument scope */ 30695 if (s->arg_var_object_idx >= 0 && !is_pseudo_var) { 30696 dbuf_putc(bc, OP_get_loc); 30697 dbuf_put_u16(bc, s->arg_var_object_idx); 30698 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 30699 } 30700 30701 /* check parent scopes */ 30702 for (fd = s; fd->parent;) { 30703 scope_level = fd->parent_scope_level; 30704 fd = fd->parent; 30705 for (idx = fd->scopes[scope_level].first; idx >= 0;) { 30706 vd = &fd->vars[idx]; 30707 if (vd->var_name == var_name) { 30708 if (op == OP_scope_put_var || op == OP_scope_make_ref) { 30709 if (vd->is_const) { 30710 dbuf_putc(bc, OP_throw_error); 30711 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30712 dbuf_putc(bc, JS_THROW_VAR_RO); 30713 goto done; 30714 } 30715 } 30716 var_idx = idx; 30717 break; 30718 } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { 30719 vd->is_captured = 1; 30720 idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL); 30721 if (idx >= 0) { 30722 dbuf_putc(bc, OP_get_var_ref); 30723 dbuf_put_u16(bc, idx); 30724 var_object_test(ctx, s, var_name, op, bc, &label_done, 1); 30725 } 30726 } 30727 idx = vd->scope_next; 30728 } 30729 is_arg_scope = (idx == ARG_SCOPE_END); 30730 if (var_idx >= 0) 30731 break; 30732 30733 if (!is_arg_scope) { 30734 var_idx = find_var(ctx, fd, var_name); 30735 if (var_idx >= 0) 30736 break; 30737 } 30738 if (is_pseudo_var) { 30739 var_idx = resolve_pseudo_var(ctx, fd, var_name); 30740 if (var_idx >= 0) 30741 break; 30742 } 30743 if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) { 30744 var_idx = add_arguments_var(ctx, fd); 30745 break; 30746 } 30747 if (fd->is_func_expr && fd->func_name == var_name) { 30748 /* add a new variable with the function name */ 30749 var_idx = add_func_var(ctx, fd, var_name); 30750 break; 30751 } 30752 30753 /* check eval object */ 30754 if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) { 30755 vd = &fd->vars[fd->var_object_idx]; 30756 vd->is_captured = 1; 30757 idx = get_closure_var(ctx, s, fd, FALSE, 30758 fd->var_object_idx, vd->var_name, 30759 FALSE, FALSE, JS_VAR_NORMAL); 30760 dbuf_putc(bc, OP_get_var_ref); 30761 dbuf_put_u16(bc, idx); 30762 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 30763 } 30764 30765 /* check eval object in argument scope */ 30766 if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) { 30767 vd = &fd->vars[fd->arg_var_object_idx]; 30768 vd->is_captured = 1; 30769 idx = get_closure_var(ctx, s, fd, FALSE, 30770 fd->arg_var_object_idx, vd->var_name, 30771 FALSE, FALSE, JS_VAR_NORMAL); 30772 dbuf_putc(bc, OP_get_var_ref); 30773 dbuf_put_u16(bc, idx); 30774 var_object_test(ctx, s, var_name, op, bc, &label_done, 0); 30775 } 30776 30777 if (fd->is_eval) 30778 break; /* it it necessarily the top level function */ 30779 } 30780 30781 /* check direct eval scope (in the closure of the eval function 30782 which is necessarily at the top level) */ 30783 if (!fd) 30784 fd = s; 30785 if (var_idx < 0 && fd->is_eval) { 30786 int idx1; 30787 for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) { 30788 JSClosureVar *cv = &fd->closure_var[idx1]; 30789 if (var_name == cv->var_name) { 30790 if (fd != s) { 30791 idx = get_closure_var2(ctx, s, fd, 30792 FALSE, 30793 cv->is_arg, idx1, 30794 cv->var_name, cv->is_const, 30795 cv->is_lexical, cv->var_kind); 30796 } else { 30797 idx = idx1; 30798 } 30799 goto has_idx; 30800 } else if ((cv->var_name == JS_ATOM__var_ || 30801 cv->var_name == JS_ATOM__arg_var_ || 30802 cv->var_name == JS_ATOM__with_) && !is_pseudo_var) { 30803 int is_with = (cv->var_name == JS_ATOM__with_); 30804 if (fd != s) { 30805 idx = get_closure_var2(ctx, s, fd, 30806 FALSE, 30807 cv->is_arg, idx1, 30808 cv->var_name, FALSE, FALSE, 30809 JS_VAR_NORMAL); 30810 } else { 30811 idx = idx1; 30812 } 30813 dbuf_putc(bc, OP_get_var_ref); 30814 dbuf_put_u16(bc, idx); 30815 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with); 30816 } 30817 } 30818 } 30819 30820 if (var_idx >= 0) { 30821 /* find the corresponding closure variable */ 30822 if (var_idx & ARGUMENT_VAR_OFFSET) { 30823 fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1; 30824 idx = get_closure_var(ctx, s, fd, 30825 TRUE, var_idx - ARGUMENT_VAR_OFFSET, 30826 var_name, FALSE, FALSE, JS_VAR_NORMAL); 30827 } else { 30828 fd->vars[var_idx].is_captured = 1; 30829 idx = get_closure_var(ctx, s, fd, 30830 FALSE, var_idx, 30831 var_name, 30832 fd->vars[var_idx].is_const, 30833 fd->vars[var_idx].is_lexical, 30834 fd->vars[var_idx].var_kind); 30835 } 30836 if (idx >= 0) { 30837 has_idx: 30838 if ((op == OP_scope_put_var || op == OP_scope_make_ref) && 30839 s->closure_var[idx].is_const) { 30840 dbuf_putc(bc, OP_throw_error); 30841 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30842 dbuf_putc(bc, JS_THROW_VAR_RO); 30843 goto done; 30844 } 30845 switch (op) { 30846 case OP_scope_make_ref: 30847 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) { 30848 /* Create a dummy object reference for the func_var */ 30849 dbuf_putc(bc, OP_object); 30850 dbuf_putc(bc, OP_get_var_ref); 30851 dbuf_put_u16(bc, idx); 30852 dbuf_putc(bc, OP_define_field); 30853 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30854 dbuf_putc(bc, OP_push_atom_value); 30855 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30856 } else 30857 if (label_done == -1 && 30858 can_opt_put_ref_value(bc_buf, ls->pos)) { 30859 int get_op; 30860 if (s->closure_var[idx].is_lexical) 30861 get_op = OP_get_var_ref_check; 30862 else 30863 get_op = OP_get_var_ref; 30864 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls, 30865 pos_next, 30866 get_op, idx); 30867 } else { 30868 /* Create a dummy object with a named slot that is 30869 a reference to the closure variable */ 30870 dbuf_putc(bc, OP_make_var_ref_ref); 30871 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30872 dbuf_put_u16(bc, idx); 30873 } 30874 break; 30875 case OP_scope_get_ref: 30876 /* XXX: should create a dummy object with a named slot that is 30877 a reference to the closure variable */ 30878 dbuf_putc(bc, OP_undefined); 30879 /* fall thru */ 30880 case OP_scope_get_var_undef: 30881 case OP_scope_get_var: 30882 case OP_scope_put_var: 30883 case OP_scope_put_var_init: 30884 is_put = (op == OP_scope_put_var || 30885 op == OP_scope_put_var_init); 30886 if (is_put) { 30887 if (s->closure_var[idx].is_lexical) { 30888 if (op == OP_scope_put_var_init) { 30889 /* 'this' can only be initialized once */ 30890 if (var_name == JS_ATOM_this) 30891 dbuf_putc(bc, OP_put_var_ref_check_init); 30892 else 30893 dbuf_putc(bc, OP_put_var_ref); 30894 } else { 30895 dbuf_putc(bc, OP_put_var_ref_check); 30896 } 30897 } else { 30898 dbuf_putc(bc, OP_put_var_ref); 30899 } 30900 } else { 30901 if (s->closure_var[idx].is_lexical) { 30902 dbuf_putc(bc, OP_get_var_ref_check); 30903 } else { 30904 dbuf_putc(bc, OP_get_var_ref); 30905 } 30906 } 30907 dbuf_put_u16(bc, idx); 30908 break; 30909 case OP_scope_delete_var: 30910 dbuf_putc(bc, OP_push_false); 30911 break; 30912 } 30913 goto done; 30914 } 30915 } 30916 30917 /* global variable access */ 30918 30919 switch (op) { 30920 case OP_scope_make_ref: 30921 if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) { 30922 pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls, 30923 pos_next, var_name); 30924 } else { 30925 dbuf_putc(bc, OP_make_var_ref); 30926 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30927 } 30928 break; 30929 case OP_scope_get_ref: 30930 /* XXX: should create a dummy object with a named slot that is 30931 a reference to the global variable */ 30932 dbuf_putc(bc, OP_undefined); 30933 dbuf_putc(bc, OP_get_var); 30934 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30935 break; 30936 case OP_scope_get_var_undef: 30937 case OP_scope_get_var: 30938 case OP_scope_put_var: 30939 dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef)); 30940 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30941 break; 30942 case OP_scope_put_var_init: 30943 dbuf_putc(bc, OP_put_var_init); 30944 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30945 break; 30946 case OP_scope_delete_var: 30947 dbuf_putc(bc, OP_delete_var); 30948 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 30949 break; 30950 } 30951 done: 30952 if (label_done >= 0) { 30953 dbuf_putc(bc, OP_label); 30954 dbuf_put_u32(bc, label_done); 30955 s->label_slots[label_done].pos2 = bc->size; 30956 } 30957 return pos_next; 30958 } 30959 30960 /* search in all scopes */ 30961 static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd, 30962 JSAtom name, int scope_level) 30963 { 30964 int idx; 30965 30966 idx = fd->scopes[scope_level].first; 30967 while (idx >= 0) { 30968 if (fd->vars[idx].var_name == name) 30969 return idx; 30970 idx = fd->vars[idx].scope_next; 30971 } 30972 return -1; 30973 } 30974 30975 static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx) 30976 { 30977 /* if the field is not initialized, the error is catched when 30978 accessing it */ 30979 if (is_ref) 30980 dbuf_putc(bc, OP_get_var_ref); 30981 else 30982 dbuf_putc(bc, OP_get_loc); 30983 dbuf_put_u16(bc, idx); 30984 } 30985 30986 static int resolve_scope_private_field1(JSContext *ctx, 30987 BOOL *pis_ref, int *pvar_kind, 30988 JSFunctionDef *s, 30989 JSAtom var_name, int scope_level) 30990 { 30991 int idx, var_kind; 30992 JSFunctionDef *fd; 30993 BOOL is_ref; 30994 30995 fd = s; 30996 is_ref = FALSE; 30997 for(;;) { 30998 idx = find_private_class_field_all(ctx, fd, var_name, scope_level); 30999 if (idx >= 0) { 31000 var_kind = fd->vars[idx].var_kind; 31001 if (is_ref) { 31002 idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name, 31003 TRUE, TRUE, JS_VAR_NORMAL); 31004 if (idx < 0) 31005 return -1; 31006 } 31007 break; 31008 } 31009 scope_level = fd->parent_scope_level; 31010 if (!fd->parent) { 31011 if (fd->is_eval) { 31012 /* closure of the eval function (top level) */ 31013 for (idx = 0; idx < fd->closure_var_count; idx++) { 31014 JSClosureVar *cv = &fd->closure_var[idx]; 31015 if (cv->var_name == var_name) { 31016 var_kind = cv->var_kind; 31017 is_ref = TRUE; 31018 if (fd != s) { 31019 idx = get_closure_var2(ctx, s, fd, 31020 FALSE, 31021 cv->is_arg, idx, 31022 cv->var_name, cv->is_const, 31023 cv->is_lexical, 31024 cv->var_kind); 31025 if (idx < 0) 31026 return -1; 31027 } 31028 goto done; 31029 } 31030 } 31031 } 31032 /* XXX: no line number info */ 31033 JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'", 31034 var_name); 31035 return -1; 31036 } else { 31037 fd = fd->parent; 31038 } 31039 is_ref = TRUE; 31040 } 31041 done: 31042 *pis_ref = is_ref; 31043 *pvar_kind = var_kind; 31044 return idx; 31045 } 31046 31047 /* return 0 if OK or -1 if the private field could not be resolved */ 31048 static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, 31049 JSAtom var_name, int scope_level, int op, 31050 DynBuf *bc) 31051 { 31052 int idx, var_kind; 31053 BOOL is_ref; 31054 31055 idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s, 31056 var_name, scope_level); 31057 if (idx < 0) 31058 return -1; 31059 assert(var_kind != JS_VAR_NORMAL); 31060 switch (op) { 31061 case OP_scope_get_private_field: 31062 case OP_scope_get_private_field2: 31063 switch(var_kind) { 31064 case JS_VAR_PRIVATE_FIELD: 31065 if (op == OP_scope_get_private_field2) 31066 dbuf_putc(bc, OP_dup); 31067 get_loc_or_ref(bc, is_ref, idx); 31068 dbuf_putc(bc, OP_get_private_field); 31069 break; 31070 case JS_VAR_PRIVATE_METHOD: 31071 get_loc_or_ref(bc, is_ref, idx); 31072 dbuf_putc(bc, OP_check_brand); 31073 if (op != OP_scope_get_private_field2) 31074 dbuf_putc(bc, OP_nip); 31075 break; 31076 case JS_VAR_PRIVATE_GETTER: 31077 case JS_VAR_PRIVATE_GETTER_SETTER: 31078 if (op == OP_scope_get_private_field2) 31079 dbuf_putc(bc, OP_dup); 31080 get_loc_or_ref(bc, is_ref, idx); 31081 dbuf_putc(bc, OP_check_brand); 31082 dbuf_putc(bc, OP_call_method); 31083 dbuf_put_u16(bc, 0); 31084 break; 31085 case JS_VAR_PRIVATE_SETTER: 31086 /* XXX: add clearer error message */ 31087 dbuf_putc(bc, OP_throw_error); 31088 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 31089 dbuf_putc(bc, JS_THROW_VAR_RO); 31090 break; 31091 default: 31092 abort(); 31093 } 31094 break; 31095 case OP_scope_put_private_field: 31096 switch(var_kind) { 31097 case JS_VAR_PRIVATE_FIELD: 31098 get_loc_or_ref(bc, is_ref, idx); 31099 dbuf_putc(bc, OP_put_private_field); 31100 break; 31101 case JS_VAR_PRIVATE_METHOD: 31102 case JS_VAR_PRIVATE_GETTER: 31103 /* XXX: add clearer error message */ 31104 dbuf_putc(bc, OP_throw_error); 31105 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name)); 31106 dbuf_putc(bc, JS_THROW_VAR_RO); 31107 break; 31108 case JS_VAR_PRIVATE_SETTER: 31109 case JS_VAR_PRIVATE_GETTER_SETTER: 31110 { 31111 JSAtom setter_name = get_private_setter_name(ctx, var_name); 31112 if (setter_name == JS_ATOM_NULL) 31113 return -1; 31114 idx = resolve_scope_private_field1(ctx, &is_ref, 31115 &var_kind, s, 31116 setter_name, scope_level); 31117 JS_FreeAtom(ctx, setter_name); 31118 if (idx < 0) 31119 return -1; 31120 assert(var_kind == JS_VAR_PRIVATE_SETTER); 31121 get_loc_or_ref(bc, is_ref, idx); 31122 dbuf_putc(bc, OP_swap); 31123 /* obj func value */ 31124 dbuf_putc(bc, OP_rot3r); 31125 /* value obj func */ 31126 dbuf_putc(bc, OP_check_brand); 31127 dbuf_putc(bc, OP_rot3l); 31128 /* obj func value */ 31129 dbuf_putc(bc, OP_call_method); 31130 dbuf_put_u16(bc, 1); 31131 dbuf_putc(bc, OP_drop); 31132 } 31133 break; 31134 default: 31135 abort(); 31136 } 31137 break; 31138 case OP_scope_in_private_field: 31139 get_loc_or_ref(bc, is_ref, idx); 31140 dbuf_putc(bc, OP_private_in); 31141 break; 31142 default: 31143 abort(); 31144 } 31145 return 0; 31146 } 31147 31148 static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s, 31149 int scope_level) 31150 { 31151 int idx; 31152 JSVarDef *vd; 31153 31154 for (idx = s->scopes[scope_level].first; idx >= 0;) { 31155 vd = &s->vars[idx]; 31156 vd->is_captured = 1; 31157 idx = vd->scope_next; 31158 } 31159 } 31160 31161 /* XXX: should handle the argument scope generically */ 31162 static BOOL is_var_in_arg_scope(const JSVarDef *vd) 31163 { 31164 return (vd->var_name == JS_ATOM_home_object || 31165 vd->var_name == JS_ATOM_this_active_func || 31166 vd->var_name == JS_ATOM_new_target || 31167 vd->var_name == JS_ATOM_this || 31168 vd->var_name == JS_ATOM__arg_var_ || 31169 vd->var_kind == JS_VAR_FUNCTION_NAME); 31170 } 31171 31172 static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) 31173 { 31174 JSFunctionDef *fd; 31175 JSVarDef *vd; 31176 int i, scope_level, scope_idx; 31177 BOOL has_arguments_binding, has_this_binding, is_arg_scope; 31178 31179 /* in non strict mode, variables are created in the caller's 31180 environment object */ 31181 if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) { 31182 s->var_object_idx = add_var(ctx, s, JS_ATOM__var_); 31183 if (s->has_parameter_expressions) { 31184 /* an additional variable object is needed for the 31185 argument scope */ 31186 s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_); 31187 } 31188 } 31189 31190 /* eval can potentially use 'arguments' so we must define it */ 31191 has_this_binding = s->has_this_binding; 31192 if (has_this_binding) { 31193 if (s->this_var_idx < 0) 31194 s->this_var_idx = add_var_this(ctx, s); 31195 if (s->new_target_var_idx < 0) 31196 s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target); 31197 if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0) 31198 s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func); 31199 if (s->has_home_object && s->home_object_var_idx < 0) 31200 s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object); 31201 } 31202 has_arguments_binding = s->has_arguments_binding; 31203 if (has_arguments_binding) { 31204 add_arguments_var(ctx, s); 31205 /* also add an arguments binding in the argument scope to 31206 raise an error if a direct eval in the argument scope tries 31207 to redefine it */ 31208 if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT)) 31209 add_arguments_arg(ctx, s); 31210 } 31211 if (s->is_func_expr && s->func_name != JS_ATOM_NULL) 31212 add_func_var(ctx, s, s->func_name); 31213 31214 /* eval can use all the variables of the enclosing functions, so 31215 they must be all put in the closure. The closure variables are 31216 ordered by scope. It works only because no closure are created 31217 before. */ 31218 assert(s->is_eval || s->closure_var_count == 0); 31219 31220 /* XXX: inefficient, but eval performance is less critical */ 31221 fd = s; 31222 for(;;) { 31223 scope_level = fd->parent_scope_level; 31224 fd = fd->parent; 31225 if (!fd) 31226 break; 31227 /* add 'this' if it was not previously added */ 31228 if (!has_this_binding && fd->has_this_binding) { 31229 if (fd->this_var_idx < 0) 31230 fd->this_var_idx = add_var_this(ctx, fd); 31231 if (fd->new_target_var_idx < 0) 31232 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target); 31233 if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0) 31234 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func); 31235 if (fd->has_home_object && fd->home_object_var_idx < 0) 31236 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object); 31237 has_this_binding = TRUE; 31238 } 31239 /* add 'arguments' if it was not previously added */ 31240 if (!has_arguments_binding && fd->has_arguments_binding) { 31241 add_arguments_var(ctx, fd); 31242 has_arguments_binding = TRUE; 31243 } 31244 /* add function name */ 31245 if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL) 31246 add_func_var(ctx, fd, fd->func_name); 31247 31248 /* add lexical variables */ 31249 scope_idx = fd->scopes[scope_level].first; 31250 while (scope_idx >= 0) { 31251 vd = &fd->vars[scope_idx]; 31252 vd->is_captured = 1; 31253 get_closure_var(ctx, s, fd, FALSE, scope_idx, 31254 vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind); 31255 scope_idx = vd->scope_next; 31256 } 31257 is_arg_scope = (scope_idx == ARG_SCOPE_END); 31258 if (!is_arg_scope) { 31259 /* add unscoped variables */ 31260 /* XXX: propagate is_const and var_kind too ? */ 31261 for(i = 0; i < fd->arg_count; i++) { 31262 vd = &fd->args[i]; 31263 if (vd->var_name != JS_ATOM_NULL) { 31264 get_closure_var(ctx, s, fd, 31265 TRUE, i, vd->var_name, FALSE, 31266 vd->is_lexical, JS_VAR_NORMAL); 31267 } 31268 } 31269 for(i = 0; i < fd->var_count; i++) { 31270 vd = &fd->vars[i]; 31271 /* do not close top level last result */ 31272 if (vd->scope_level == 0 && 31273 vd->var_name != JS_ATOM__ret_ && 31274 vd->var_name != JS_ATOM_NULL) { 31275 get_closure_var(ctx, s, fd, 31276 FALSE, i, vd->var_name, FALSE, 31277 vd->is_lexical, JS_VAR_NORMAL); 31278 } 31279 } 31280 } else { 31281 for(i = 0; i < fd->var_count; i++) { 31282 vd = &fd->vars[i]; 31283 /* do not close top level last result */ 31284 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { 31285 get_closure_var(ctx, s, fd, 31286 FALSE, i, vd->var_name, FALSE, 31287 vd->is_lexical, JS_VAR_NORMAL); 31288 } 31289 } 31290 } 31291 if (fd->is_eval) { 31292 int idx; 31293 /* add direct eval variables (we are necessarily at the 31294 top level) */ 31295 for (idx = 0; idx < fd->closure_var_count; idx++) { 31296 JSClosureVar *cv = &fd->closure_var[idx]; 31297 get_closure_var2(ctx, s, fd, 31298 FALSE, cv->is_arg, 31299 idx, cv->var_name, cv->is_const, 31300 cv->is_lexical, cv->var_kind); 31301 } 31302 } 31303 } 31304 } 31305 31306 static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, 31307 JSVarDef *vd, int var_idx) 31308 { 31309 cv->is_local = TRUE; 31310 cv->is_arg = FALSE; 31311 cv->is_const = vd->is_const; 31312 cv->is_lexical = vd->is_lexical; 31313 cv->var_kind = vd->var_kind; 31314 cv->var_idx = var_idx; 31315 cv->var_name = JS_DupAtom(ctx, vd->var_name); 31316 } 31317 31318 /* for direct eval compilation: add references to the variables of the 31319 calling function */ 31320 static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, 31321 JSFunctionBytecode *b, int scope_idx) 31322 { 31323 int i, count; 31324 JSVarDef *vd; 31325 BOOL is_arg_scope; 31326 31327 count = b->arg_count + b->var_count + b->closure_var_count; 31328 s->closure_var = NULL; 31329 s->closure_var_count = 0; 31330 s->closure_var_size = count; 31331 if (count == 0) 31332 return 0; 31333 s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count); 31334 if (!s->closure_var) 31335 return -1; 31336 /* Add lexical variables in scope at the point of evaluation */ 31337 for (i = scope_idx; i >= 0;) { 31338 vd = &b->vardefs[b->arg_count + i]; 31339 if (vd->scope_level > 0) { 31340 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 31341 set_closure_from_var(ctx, cv, vd, i); 31342 } 31343 i = vd->scope_next; 31344 } 31345 is_arg_scope = (i == ARG_SCOPE_END); 31346 if (!is_arg_scope) { 31347 /* Add argument variables */ 31348 for(i = 0; i < b->arg_count; i++) { 31349 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 31350 vd = &b->vardefs[i]; 31351 cv->is_local = TRUE; 31352 cv->is_arg = TRUE; 31353 cv->is_const = FALSE; 31354 cv->is_lexical = FALSE; 31355 cv->var_kind = JS_VAR_NORMAL; 31356 cv->var_idx = i; 31357 cv->var_name = JS_DupAtom(ctx, vd->var_name); 31358 } 31359 /* Add local non lexical variables */ 31360 for(i = 0; i < b->var_count; i++) { 31361 vd = &b->vardefs[b->arg_count + i]; 31362 if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) { 31363 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 31364 set_closure_from_var(ctx, cv, vd, i); 31365 } 31366 } 31367 } else { 31368 /* only add pseudo variables */ 31369 for(i = 0; i < b->var_count; i++) { 31370 vd = &b->vardefs[b->arg_count + i]; 31371 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { 31372 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 31373 set_closure_from_var(ctx, cv, vd, i); 31374 } 31375 } 31376 } 31377 for(i = 0; i < b->closure_var_count; i++) { 31378 JSClosureVar *cv0 = &b->closure_var[i]; 31379 JSClosureVar *cv = &s->closure_var[s->closure_var_count++]; 31380 cv->is_local = FALSE; 31381 cv->is_arg = cv0->is_arg; 31382 cv->is_const = cv0->is_const; 31383 cv->is_lexical = cv0->is_lexical; 31384 cv->var_kind = cv0->var_kind; 31385 cv->var_idx = i; 31386 cv->var_name = JS_DupAtom(ctx, cv0->var_name); 31387 } 31388 return 0; 31389 } 31390 31391 typedef struct CodeContext { 31392 const uint8_t *bc_buf; /* code buffer */ 31393 int bc_len; /* length of the code buffer */ 31394 int pos; /* position past the matched code pattern */ 31395 int line_num; /* last visited OP_line_num parameter or -1 */ 31396 int op; 31397 int idx; 31398 int label; 31399 int val; 31400 JSAtom atom; 31401 } CodeContext; 31402 31403 #define M2(op1, op2) ((op1) | ((op2) << 8)) 31404 #define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16)) 31405 #define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24)) 31406 31407 static BOOL code_match(CodeContext *s, int pos, ...) 31408 { 31409 const uint8_t *tab = s->bc_buf; 31410 int op, len, op1, line_num, pos_next; 31411 va_list ap; 31412 BOOL ret = FALSE; 31413 31414 line_num = -1; 31415 va_start(ap, pos); 31416 31417 for(;;) { 31418 op1 = va_arg(ap, int); 31419 if (op1 == -1) { 31420 s->pos = pos; 31421 s->line_num = line_num; 31422 ret = TRUE; 31423 break; 31424 } 31425 for (;;) { 31426 if (pos >= s->bc_len) 31427 goto done; 31428 op = tab[pos]; 31429 len = opcode_info[op].size; 31430 pos_next = pos + len; 31431 if (pos_next > s->bc_len) 31432 goto done; 31433 if (op == OP_line_num) { 31434 line_num = get_u32(tab + pos + 1); 31435 pos = pos_next; 31436 } else { 31437 break; 31438 } 31439 } 31440 if (op != op1) { 31441 if (op1 == (uint8_t)op1 || !op) 31442 break; 31443 if (op != (uint8_t)op1 31444 && op != (uint8_t)(op1 >> 8) 31445 && op != (uint8_t)(op1 >> 16) 31446 && op != (uint8_t)(op1 >> 24)) { 31447 break; 31448 } 31449 s->op = op; 31450 } 31451 31452 pos++; 31453 switch(opcode_info[op].fmt) { 31454 case OP_FMT_loc8: 31455 case OP_FMT_u8: 31456 { 31457 int idx = tab[pos]; 31458 int arg = va_arg(ap, int); 31459 if (arg == -1) { 31460 s->idx = idx; 31461 } else { 31462 if (arg != idx) 31463 goto done; 31464 } 31465 break; 31466 } 31467 case OP_FMT_u16: 31468 case OP_FMT_npop: 31469 case OP_FMT_loc: 31470 case OP_FMT_arg: 31471 case OP_FMT_var_ref: 31472 { 31473 int idx = get_u16(tab + pos); 31474 int arg = va_arg(ap, int); 31475 if (arg == -1) { 31476 s->idx = idx; 31477 } else { 31478 if (arg != idx) 31479 goto done; 31480 } 31481 break; 31482 } 31483 case OP_FMT_i32: 31484 case OP_FMT_u32: 31485 case OP_FMT_label: 31486 case OP_FMT_const: 31487 { 31488 s->label = get_u32(tab + pos); 31489 break; 31490 } 31491 case OP_FMT_label_u16: 31492 { 31493 s->label = get_u32(tab + pos); 31494 s->val = get_u16(tab + pos + 4); 31495 break; 31496 } 31497 case OP_FMT_atom: 31498 { 31499 s->atom = get_u32(tab + pos); 31500 break; 31501 } 31502 case OP_FMT_atom_u8: 31503 { 31504 s->atom = get_u32(tab + pos); 31505 s->val = get_u8(tab + pos + 4); 31506 break; 31507 } 31508 case OP_FMT_atom_u16: 31509 { 31510 s->atom = get_u32(tab + pos); 31511 s->val = get_u16(tab + pos + 4); 31512 break; 31513 } 31514 case OP_FMT_atom_label_u8: 31515 { 31516 s->atom = get_u32(tab + pos); 31517 s->label = get_u32(tab + pos + 4); 31518 s->val = get_u8(tab + pos + 8); 31519 break; 31520 } 31521 default: 31522 break; 31523 } 31524 pos = pos_next; 31525 } 31526 done: 31527 va_end(ap); 31528 return ret; 31529 } 31530 31531 static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc) 31532 { 31533 int i, idx, label_next = -1; 31534 31535 /* add the hoisted functions in arguments and local variables */ 31536 for(i = 0; i < s->arg_count; i++) { 31537 JSVarDef *vd = &s->args[i]; 31538 if (vd->func_pool_idx >= 0) { 31539 dbuf_putc(bc, OP_fclosure); 31540 dbuf_put_u32(bc, vd->func_pool_idx); 31541 dbuf_putc(bc, OP_put_arg); 31542 dbuf_put_u16(bc, i); 31543 } 31544 } 31545 for(i = 0; i < s->var_count; i++) { 31546 JSVarDef *vd = &s->vars[i]; 31547 if (vd->scope_level == 0 && vd->func_pool_idx >= 0) { 31548 dbuf_putc(bc, OP_fclosure); 31549 dbuf_put_u32(bc, vd->func_pool_idx); 31550 dbuf_putc(bc, OP_put_loc); 31551 dbuf_put_u16(bc, i); 31552 } 31553 } 31554 31555 /* the module global variables must be initialized before 31556 evaluating the module so that the exported functions are 31557 visible if there are cyclic module references */ 31558 if (s->module) { 31559 label_next = new_label_fd(s, -1); 31560 31561 /* if 'this' is true, initialize the global variables and return */ 31562 dbuf_putc(bc, OP_push_this); 31563 dbuf_putc(bc, OP_if_false); 31564 dbuf_put_u32(bc, label_next); 31565 update_label(s, label_next, 1); 31566 s->jump_size++; 31567 } 31568 31569 /* add the global variables (only happens if s->is_global_var is 31570 true) */ 31571 for(i = 0; i < s->global_var_count; i++) { 31572 JSGlobalVar *hf = &s->global_vars[i]; 31573 int has_closure = 0; 31574 BOOL force_init = hf->force_init; 31575 /* we are in an eval, so the closure contains all the 31576 enclosing variables */ 31577 /* If the outer function has a variable environment, 31578 create a property for the variable there */ 31579 for(idx = 0; idx < s->closure_var_count; idx++) { 31580 JSClosureVar *cv = &s->closure_var[idx]; 31581 if (cv->var_name == hf->var_name) { 31582 has_closure = 2; 31583 force_init = FALSE; 31584 break; 31585 } 31586 if (cv->var_name == JS_ATOM__var_ || 31587 cv->var_name == JS_ATOM__arg_var_) { 31588 dbuf_putc(bc, OP_get_var_ref); 31589 dbuf_put_u16(bc, idx); 31590 has_closure = 1; 31591 force_init = TRUE; 31592 break; 31593 } 31594 } 31595 if (!has_closure) { 31596 int flags; 31597 31598 flags = 0; 31599 if (s->eval_type != JS_EVAL_TYPE_GLOBAL) 31600 flags |= JS_PROP_CONFIGURABLE; 31601 if (hf->cpool_idx >= 0 && !hf->is_lexical) { 31602 /* global function definitions need a specific handling */ 31603 dbuf_putc(bc, OP_fclosure); 31604 dbuf_put_u32(bc, hf->cpool_idx); 31605 31606 dbuf_putc(bc, OP_define_func); 31607 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); 31608 dbuf_putc(bc, flags); 31609 31610 goto done_global_var; 31611 } else { 31612 if (hf->is_lexical) { 31613 flags |= DEFINE_GLOBAL_LEX_VAR; 31614 if (!hf->is_const) 31615 flags |= JS_PROP_WRITABLE; 31616 } 31617 dbuf_putc(bc, OP_define_var); 31618 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); 31619 dbuf_putc(bc, flags); 31620 } 31621 } 31622 if (hf->cpool_idx >= 0 || force_init) { 31623 if (hf->cpool_idx >= 0) { 31624 dbuf_putc(bc, OP_fclosure); 31625 dbuf_put_u32(bc, hf->cpool_idx); 31626 if (hf->var_name == JS_ATOM__default_) { 31627 /* set default export function name */ 31628 dbuf_putc(bc, OP_set_name); 31629 dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default)); 31630 } 31631 } else { 31632 dbuf_putc(bc, OP_undefined); 31633 } 31634 if (has_closure == 2) { 31635 dbuf_putc(bc, OP_put_var_ref); 31636 dbuf_put_u16(bc, idx); 31637 } else if (has_closure == 1) { 31638 dbuf_putc(bc, OP_define_field); 31639 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); 31640 dbuf_putc(bc, OP_drop); 31641 } else { 31642 /* XXX: Check if variable is writable and enumerable */ 31643 dbuf_putc(bc, OP_put_var); 31644 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name)); 31645 } 31646 } 31647 done_global_var: 31648 JS_FreeAtom(ctx, hf->var_name); 31649 } 31650 31651 if (s->module) { 31652 dbuf_putc(bc, OP_return_undef); 31653 31654 dbuf_putc(bc, OP_label); 31655 dbuf_put_u32(bc, label_next); 31656 s->label_slots[label_next].pos2 = bc->size; 31657 } 31658 31659 js_free(ctx, s->global_vars); 31660 s->global_vars = NULL; 31661 s->global_var_count = 0; 31662 s->global_var_size = 0; 31663 } 31664 31665 static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len, 31666 int pos, int *linep) 31667 { 31668 int op, len, label; 31669 31670 for (; pos < bc_len; pos += len) { 31671 op = bc_buf[pos]; 31672 len = opcode_info[op].size; 31673 if (op == OP_line_num) { 31674 *linep = get_u32(bc_buf + pos + 1); 31675 } else 31676 if (op == OP_label) { 31677 label = get_u32(bc_buf + pos + 1); 31678 if (update_label(s, label, 0) > 0) 31679 break; 31680 #if 0 31681 if (s->label_slots[label].first_reloc) { 31682 printf("line %d: unreferenced label %d:%d has relocations\n", 31683 *linep, label, s->label_slots[label].pos2); 31684 } 31685 #endif 31686 assert(s->label_slots[label].first_reloc == NULL); 31687 } else { 31688 /* XXX: output a warning for unreachable code? */ 31689 JSAtom atom; 31690 switch(opcode_info[op].fmt) { 31691 case OP_FMT_label: 31692 case OP_FMT_label_u16: 31693 label = get_u32(bc_buf + pos + 1); 31694 update_label(s, label, -1); 31695 break; 31696 case OP_FMT_atom_label_u8: 31697 case OP_FMT_atom_label_u16: 31698 label = get_u32(bc_buf + pos + 5); 31699 update_label(s, label, -1); 31700 /* fall thru */ 31701 case OP_FMT_atom: 31702 case OP_FMT_atom_u8: 31703 case OP_FMT_atom_u16: 31704 atom = get_u32(bc_buf + pos + 1); 31705 JS_FreeAtom(s->ctx, atom); 31706 break; 31707 default: 31708 break; 31709 } 31710 } 31711 } 31712 return pos; 31713 } 31714 31715 static int get_label_pos(JSFunctionDef *s, int label) 31716 { 31717 int i, pos; 31718 for (i = 0; i < 20; i++) { 31719 pos = s->label_slots[label].pos; 31720 for (;;) { 31721 switch (s->byte_code.buf[pos]) { 31722 case OP_line_num: 31723 case OP_label: 31724 pos += 5; 31725 continue; 31726 case OP_goto: 31727 label = get_u32(s->byte_code.buf + pos + 1); 31728 break; 31729 default: 31730 return pos; 31731 } 31732 break; 31733 } 31734 } 31735 return pos; 31736 } 31737 31738 /* convert global variable accesses to local variables or closure 31739 variables when necessary */ 31740 static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) 31741 { 31742 int pos, pos_next, bc_len, op, len, i, idx, line_num; 31743 uint8_t *bc_buf; 31744 JSAtom var_name; 31745 DynBuf bc_out; 31746 CodeContext cc; 31747 int scope; 31748 31749 cc.bc_buf = bc_buf = s->byte_code.buf; 31750 cc.bc_len = bc_len = s->byte_code.size; 31751 js_dbuf_init(ctx, &bc_out); 31752 31753 /* first pass for runtime checks (must be done before the 31754 variables are created) */ 31755 for(i = 0; i < s->global_var_count; i++) { 31756 JSGlobalVar *hf = &s->global_vars[i]; 31757 int flags; 31758 31759 /* check if global variable (XXX: simplify) */ 31760 for(idx = 0; idx < s->closure_var_count; idx++) { 31761 JSClosureVar *cv = &s->closure_var[idx]; 31762 if (cv->var_name == hf->var_name) { 31763 if (s->eval_type == JS_EVAL_TYPE_DIRECT && 31764 cv->is_lexical) { 31765 /* Check if a lexical variable is 31766 redefined as 'var'. XXX: Could abort 31767 compilation here, but for consistency 31768 with the other checks, we delay the 31769 error generation. */ 31770 dbuf_putc(&bc_out, OP_throw_error); 31771 dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); 31772 dbuf_putc(&bc_out, JS_THROW_VAR_REDECL); 31773 } 31774 goto next; 31775 } 31776 if (cv->var_name == JS_ATOM__var_ || 31777 cv->var_name == JS_ATOM__arg_var_) 31778 goto next; 31779 } 31780 31781 dbuf_putc(&bc_out, OP_check_define_var); 31782 dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name)); 31783 flags = 0; 31784 if (hf->is_lexical) 31785 flags |= DEFINE_GLOBAL_LEX_VAR; 31786 if (hf->cpool_idx >= 0) 31787 flags |= DEFINE_GLOBAL_FUNC_VAR; 31788 dbuf_putc(&bc_out, flags); 31789 next: ; 31790 } 31791 31792 line_num = 0; /* avoid warning */ 31793 for (pos = 0; pos < bc_len; pos = pos_next) { 31794 op = bc_buf[pos]; 31795 len = opcode_info[op].size; 31796 pos_next = pos + len; 31797 switch(op) { 31798 case OP_line_num: 31799 line_num = get_u32(bc_buf + pos + 1); 31800 s->line_number_size++; 31801 goto no_change; 31802 31803 case OP_eval: /* convert scope index to adjusted variable index */ 31804 { 31805 int call_argc = get_u16(bc_buf + pos + 1); 31806 scope = get_u16(bc_buf + pos + 1 + 2); 31807 mark_eval_captured_variables(ctx, s, scope); 31808 dbuf_putc(&bc_out, op); 31809 dbuf_put_u16(&bc_out, call_argc); 31810 dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); 31811 } 31812 break; 31813 case OP_apply_eval: /* convert scope index to adjusted variable index */ 31814 scope = get_u16(bc_buf + pos + 1); 31815 mark_eval_captured_variables(ctx, s, scope); 31816 dbuf_putc(&bc_out, op); 31817 dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); 31818 break; 31819 case OP_scope_get_var_checkthis: 31820 case OP_scope_get_var_undef: 31821 case OP_scope_get_var: 31822 case OP_scope_put_var: 31823 case OP_scope_delete_var: 31824 case OP_scope_get_ref: 31825 case OP_scope_put_var_init: 31826 var_name = get_u32(bc_buf + pos + 1); 31827 scope = get_u16(bc_buf + pos + 5); 31828 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, 31829 NULL, NULL, pos_next); 31830 JS_FreeAtom(ctx, var_name); 31831 break; 31832 case OP_scope_make_ref: 31833 { 31834 int label; 31835 LabelSlot *ls; 31836 var_name = get_u32(bc_buf + pos + 1); 31837 label = get_u32(bc_buf + pos + 5); 31838 scope = get_u16(bc_buf + pos + 9); 31839 ls = &s->label_slots[label]; 31840 ls->ref_count--; /* always remove label reference */ 31841 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out, 31842 bc_buf, ls, pos_next); 31843 JS_FreeAtom(ctx, var_name); 31844 } 31845 break; 31846 case OP_scope_get_private_field: 31847 case OP_scope_get_private_field2: 31848 case OP_scope_put_private_field: 31849 case OP_scope_in_private_field: 31850 { 31851 int ret; 31852 var_name = get_u32(bc_buf + pos + 1); 31853 scope = get_u16(bc_buf + pos + 5); 31854 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out); 31855 if (ret < 0) 31856 goto fail; 31857 JS_FreeAtom(ctx, var_name); 31858 } 31859 break; 31860 case OP_gosub: 31861 s->jump_size++; 31862 if (OPTIMIZE) { 31863 /* remove calls to empty finalizers */ 31864 int label; 31865 LabelSlot *ls; 31866 31867 label = get_u32(bc_buf + pos + 1); 31868 assert(label >= 0 && label < s->label_count); 31869 ls = &s->label_slots[label]; 31870 if (code_match(&cc, ls->pos, OP_ret, -1)) { 31871 ls->ref_count--; 31872 break; 31873 } 31874 } 31875 goto no_change; 31876 case OP_drop: 31877 if (0) { 31878 /* remove drops before return_undef */ 31879 /* do not perform this optimization in pass2 because 31880 it breaks patterns recognised in resolve_labels */ 31881 int pos1 = pos_next; 31882 int line1 = line_num; 31883 while (code_match(&cc, pos1, OP_drop, -1)) { 31884 if (cc.line_num >= 0) line1 = cc.line_num; 31885 pos1 = cc.pos; 31886 } 31887 if (code_match(&cc, pos1, OP_return_undef, -1)) { 31888 pos_next = pos1; 31889 if (line1 != -1 && line1 != line_num) { 31890 line_num = line1; 31891 s->line_number_size++; 31892 dbuf_putc(&bc_out, OP_line_num); 31893 dbuf_put_u32(&bc_out, line_num); 31894 } 31895 break; 31896 } 31897 } 31898 goto no_change; 31899 case OP_insert3: 31900 if (OPTIMIZE) { 31901 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */ 31902 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) { 31903 dbuf_putc(&bc_out, cc.op); 31904 pos_next = cc.pos; 31905 if (cc.line_num != -1 && cc.line_num != line_num) { 31906 line_num = cc.line_num; 31907 s->line_number_size++; 31908 dbuf_putc(&bc_out, OP_line_num); 31909 dbuf_put_u32(&bc_out, line_num); 31910 } 31911 break; 31912 } 31913 } 31914 goto no_change; 31915 31916 case OP_goto: 31917 s->jump_size++; 31918 /* fall thru */ 31919 case OP_tail_call: 31920 case OP_tail_call_method: 31921 case OP_return: 31922 case OP_return_undef: 31923 case OP_throw: 31924 case OP_throw_error: 31925 case OP_ret: 31926 if (OPTIMIZE) { 31927 /* remove dead code */ 31928 int line = -1; 31929 dbuf_put(&bc_out, bc_buf + pos, len); 31930 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line); 31931 pos_next = pos; 31932 if (pos < bc_len && line >= 0 && line_num != line) { 31933 line_num = line; 31934 s->line_number_size++; 31935 dbuf_putc(&bc_out, OP_line_num); 31936 dbuf_put_u32(&bc_out, line_num); 31937 } 31938 break; 31939 } 31940 goto no_change; 31941 31942 case OP_label: 31943 { 31944 int label; 31945 LabelSlot *ls; 31946 31947 label = get_u32(bc_buf + pos + 1); 31948 assert(label >= 0 && label < s->label_count); 31949 ls = &s->label_slots[label]; 31950 ls->pos2 = bc_out.size + opcode_info[op].size; 31951 } 31952 goto no_change; 31953 31954 case OP_enter_scope: 31955 { 31956 int scope_idx, scope = get_u16(bc_buf + pos + 1); 31957 31958 if (scope == s->body_scope) { 31959 instantiate_hoisted_definitions(ctx, s, &bc_out); 31960 } 31961 31962 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { 31963 JSVarDef *vd = &s->vars[scope_idx]; 31964 if (vd->scope_level == scope) { 31965 if (scope_idx != s->arguments_arg_idx) { 31966 if (vd->var_kind == JS_VAR_FUNCTION_DECL || 31967 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) { 31968 /* Initialize lexical variable upon entering scope */ 31969 dbuf_putc(&bc_out, OP_fclosure); 31970 dbuf_put_u32(&bc_out, vd->func_pool_idx); 31971 dbuf_putc(&bc_out, OP_put_loc); 31972 dbuf_put_u16(&bc_out, scope_idx); 31973 } else { 31974 /* XXX: should check if variable can be used 31975 before initialization */ 31976 dbuf_putc(&bc_out, OP_set_loc_uninitialized); 31977 dbuf_put_u16(&bc_out, scope_idx); 31978 } 31979 } 31980 scope_idx = vd->scope_next; 31981 } else { 31982 break; 31983 } 31984 } 31985 } 31986 break; 31987 31988 case OP_leave_scope: 31989 { 31990 int scope_idx, scope = get_u16(bc_buf + pos + 1); 31991 31992 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) { 31993 JSVarDef *vd = &s->vars[scope_idx]; 31994 if (vd->scope_level == scope) { 31995 if (vd->is_captured) { 31996 dbuf_putc(&bc_out, OP_close_loc); 31997 dbuf_put_u16(&bc_out, scope_idx); 31998 } 31999 scope_idx = vd->scope_next; 32000 } else { 32001 break; 32002 } 32003 } 32004 } 32005 break; 32006 32007 case OP_set_name: 32008 { 32009 /* remove dummy set_name opcodes */ 32010 JSAtom name = get_u32(bc_buf + pos + 1); 32011 if (name == JS_ATOM_NULL) 32012 break; 32013 } 32014 goto no_change; 32015 32016 case OP_if_false: 32017 case OP_if_true: 32018 case OP_catch: 32019 s->jump_size++; 32020 goto no_change; 32021 32022 case OP_dup: 32023 if (OPTIMIZE) { 32024 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */ 32025 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */ 32026 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) { 32027 int lab0, lab1, op1, pos1, line1, pos2; 32028 lab0 = lab1 = cc.label; 32029 assert(lab1 >= 0 && lab1 < s->label_count); 32030 op1 = cc.op; 32031 pos1 = cc.pos; 32032 line1 = cc.line_num; 32033 while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) { 32034 lab1 = cc.label; 32035 } 32036 if (code_match(&cc, pos2, op1, -1)) { 32037 s->jump_size++; 32038 update_label(s, lab0, -1); 32039 update_label(s, cc.label, +1); 32040 dbuf_putc(&bc_out, op1); 32041 dbuf_put_u32(&bc_out, cc.label); 32042 pos_next = pos1; 32043 if (line1 != -1 && line1 != line_num) { 32044 line_num = line1; 32045 s->line_number_size++; 32046 dbuf_putc(&bc_out, OP_line_num); 32047 dbuf_put_u32(&bc_out, line_num); 32048 } 32049 break; 32050 } 32051 } 32052 } 32053 goto no_change; 32054 32055 case OP_nop: 32056 /* remove erased code */ 32057 break; 32058 case OP_set_class_name: 32059 /* only used during parsing */ 32060 break; 32061 32062 case OP_get_field_opt_chain: /* equivalent to OP_get_field */ 32063 { 32064 JSAtom name = get_u32(bc_buf + pos + 1); 32065 dbuf_putc(&bc_out, OP_get_field); 32066 dbuf_put_u32(&bc_out, name); 32067 } 32068 break; 32069 case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */ 32070 dbuf_putc(&bc_out, OP_get_array_el); 32071 break; 32072 32073 default: 32074 no_change: 32075 dbuf_put(&bc_out, bc_buf + pos, len); 32076 break; 32077 } 32078 } 32079 32080 /* set the new byte code */ 32081 dbuf_free(&s->byte_code); 32082 s->byte_code = bc_out; 32083 if (dbuf_error(&s->byte_code)) { 32084 JS_ThrowOutOfMemory(ctx); 32085 return -1; 32086 } 32087 return 0; 32088 fail: 32089 /* continue the copy to keep the atom refcounts consistent */ 32090 /* XXX: find a better solution ? */ 32091 for (; pos < bc_len; pos = pos_next) { 32092 op = bc_buf[pos]; 32093 len = opcode_info[op].size; 32094 pos_next = pos + len; 32095 dbuf_put(&bc_out, bc_buf + pos, len); 32096 } 32097 dbuf_free(&s->byte_code); 32098 s->byte_code = bc_out; 32099 return -1; 32100 } 32101 32102 /* the pc2line table gives a line number for each PC value */ 32103 static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num) 32104 { 32105 if (s->line_number_slots != NULL 32106 && s->line_number_count < s->line_number_size 32107 && pc >= s->line_number_last_pc 32108 && line_num != s->line_number_last) { 32109 s->line_number_slots[s->line_number_count].pc = pc; 32110 s->line_number_slots[s->line_number_count].line_num = line_num; 32111 s->line_number_count++; 32112 s->line_number_last_pc = pc; 32113 s->line_number_last = line_num; 32114 } 32115 } 32116 32117 static void compute_pc2line_info(JSFunctionDef *s) 32118 { 32119 if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) { 32120 int last_line_num = s->line_num; 32121 uint32_t last_pc = 0; 32122 int i; 32123 32124 js_dbuf_init(s->ctx, &s->pc2line); 32125 for (i = 0; i < s->line_number_count; i++) { 32126 uint32_t pc = s->line_number_slots[i].pc; 32127 int line_num = s->line_number_slots[i].line_num; 32128 int diff_pc, diff_line; 32129 32130 if (line_num < 0) 32131 continue; 32132 32133 diff_pc = pc - last_pc; 32134 diff_line = line_num - last_line_num; 32135 if (diff_line == 0 || diff_pc < 0) 32136 continue; 32137 32138 if (diff_line >= PC2LINE_BASE && 32139 diff_line < PC2LINE_BASE + PC2LINE_RANGE && 32140 diff_pc <= PC2LINE_DIFF_PC_MAX) { 32141 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) + 32142 diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST); 32143 } else { 32144 /* longer encoding */ 32145 dbuf_putc(&s->pc2line, 0); 32146 dbuf_put_leb128(&s->pc2line, diff_pc); 32147 dbuf_put_sleb128(&s->pc2line, diff_line); 32148 } 32149 last_pc = pc; 32150 last_line_num = line_num; 32151 } 32152 } 32153 } 32154 32155 static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size) 32156 { 32157 RelocEntry *re; 32158 re = js_malloc(ctx, sizeof(*re)); 32159 if (!re) 32160 return NULL; 32161 re->addr = addr; 32162 re->size = size; 32163 re->next = ls->first_reloc; 32164 ls->first_reloc = re; 32165 return re; 32166 } 32167 32168 static BOOL code_has_label(CodeContext *s, int pos, int label) 32169 { 32170 while (pos < s->bc_len) { 32171 int op = s->bc_buf[pos]; 32172 if (op == OP_line_num) { 32173 pos += 5; 32174 continue; 32175 } 32176 if (op == OP_label) { 32177 int lab = get_u32(s->bc_buf + pos + 1); 32178 if (lab == label) 32179 return TRUE; 32180 pos += 5; 32181 continue; 32182 } 32183 if (op == OP_goto) { 32184 int lab = get_u32(s->bc_buf + pos + 1); 32185 if (lab == label) 32186 return TRUE; 32187 } 32188 break; 32189 } 32190 return FALSE; 32191 } 32192 32193 /* return the target label, following the OP_goto jumps 32194 the first opcode at destination is stored in *pop 32195 */ 32196 static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline) 32197 { 32198 int i, pos, op; 32199 32200 update_label(s, label, -1); 32201 for (i = 0; i < 10; i++) { 32202 assert(label >= 0 && label < s->label_count); 32203 pos = s->label_slots[label].pos2; 32204 for (;;) { 32205 switch(op = s->byte_code.buf[pos]) { 32206 case OP_line_num: 32207 if (pline) 32208 *pline = get_u32(s->byte_code.buf + pos + 1); 32209 /* fall thru */ 32210 case OP_label: 32211 pos += opcode_info[op].size; 32212 continue; 32213 case OP_goto: 32214 label = get_u32(s->byte_code.buf + pos + 1); 32215 break; 32216 case OP_drop: 32217 /* ignore drop opcodes if followed by OP_return_undef */ 32218 while (s->byte_code.buf[++pos] == OP_drop) 32219 continue; 32220 if (s->byte_code.buf[pos] == OP_return_undef) 32221 op = OP_return_undef; 32222 /* fall thru */ 32223 default: 32224 goto done; 32225 } 32226 break; 32227 } 32228 } 32229 /* cycle detected, could issue a warning */ 32230 done: 32231 *pop = op; 32232 update_label(s, label, +1); 32233 return label; 32234 } 32235 32236 static void push_short_int(DynBuf *bc_out, int val) 32237 { 32238 #if SHORT_OPCODES 32239 if (val >= -1 && val <= 7) { 32240 dbuf_putc(bc_out, OP_push_0 + val); 32241 return; 32242 } 32243 if (val == (int8_t)val) { 32244 dbuf_putc(bc_out, OP_push_i8); 32245 dbuf_putc(bc_out, val); 32246 return; 32247 } 32248 if (val == (int16_t)val) { 32249 dbuf_putc(bc_out, OP_push_i16); 32250 dbuf_put_u16(bc_out, val); 32251 return; 32252 } 32253 #endif 32254 dbuf_putc(bc_out, OP_push_i32); 32255 dbuf_put_u32(bc_out, val); 32256 } 32257 32258 static void put_short_code(DynBuf *bc_out, int op, int idx) 32259 { 32260 #if SHORT_OPCODES 32261 if (idx < 4) { 32262 switch (op) { 32263 case OP_get_loc: 32264 dbuf_putc(bc_out, OP_get_loc0 + idx); 32265 return; 32266 case OP_put_loc: 32267 dbuf_putc(bc_out, OP_put_loc0 + idx); 32268 return; 32269 case OP_set_loc: 32270 dbuf_putc(bc_out, OP_set_loc0 + idx); 32271 return; 32272 case OP_get_arg: 32273 dbuf_putc(bc_out, OP_get_arg0 + idx); 32274 return; 32275 case OP_put_arg: 32276 dbuf_putc(bc_out, OP_put_arg0 + idx); 32277 return; 32278 case OP_set_arg: 32279 dbuf_putc(bc_out, OP_set_arg0 + idx); 32280 return; 32281 case OP_get_var_ref: 32282 dbuf_putc(bc_out, OP_get_var_ref0 + idx); 32283 return; 32284 case OP_put_var_ref: 32285 dbuf_putc(bc_out, OP_put_var_ref0 + idx); 32286 return; 32287 case OP_set_var_ref: 32288 dbuf_putc(bc_out, OP_set_var_ref0 + idx); 32289 return; 32290 case OP_call: 32291 dbuf_putc(bc_out, OP_call0 + idx); 32292 return; 32293 } 32294 } 32295 if (idx < 256) { 32296 switch (op) { 32297 case OP_get_loc: 32298 dbuf_putc(bc_out, OP_get_loc8); 32299 dbuf_putc(bc_out, idx); 32300 return; 32301 case OP_put_loc: 32302 dbuf_putc(bc_out, OP_put_loc8); 32303 dbuf_putc(bc_out, idx); 32304 return; 32305 case OP_set_loc: 32306 dbuf_putc(bc_out, OP_set_loc8); 32307 dbuf_putc(bc_out, idx); 32308 return; 32309 } 32310 } 32311 #endif 32312 dbuf_putc(bc_out, op); 32313 dbuf_put_u16(bc_out, idx); 32314 } 32315 32316 /* peephole optimizations and resolve goto/labels */ 32317 static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) 32318 { 32319 int pos, pos_next, bc_len, op, op1, len, i, line_num; 32320 const uint8_t *bc_buf; 32321 DynBuf bc_out; 32322 LabelSlot *label_slots, *ls; 32323 RelocEntry *re, *re_next; 32324 CodeContext cc; 32325 int label; 32326 #if SHORT_OPCODES 32327 JumpSlot *jp; 32328 #endif 32329 32330 label_slots = s->label_slots; 32331 32332 line_num = s->line_num; 32333 32334 cc.bc_buf = bc_buf = s->byte_code.buf; 32335 cc.bc_len = bc_len = s->byte_code.size; 32336 js_dbuf_init(ctx, &bc_out); 32337 32338 #if SHORT_OPCODES 32339 if (s->jump_size) { 32340 s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size); 32341 if (s->jump_slots == NULL) 32342 return -1; 32343 } 32344 #endif 32345 /* XXX: Should skip this phase if not generating SHORT_OPCODES */ 32346 if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) { 32347 s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size); 32348 if (s->line_number_slots == NULL) 32349 return -1; 32350 s->line_number_last = s->line_num; 32351 s->line_number_last_pc = 0; 32352 } 32353 32354 /* initialize the 'home_object' variable if needed */ 32355 if (s->home_object_var_idx >= 0) { 32356 dbuf_putc(&bc_out, OP_special_object); 32357 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT); 32358 put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx); 32359 } 32360 /* initialize the 'this.active_func' variable if needed */ 32361 if (s->this_active_func_var_idx >= 0) { 32362 dbuf_putc(&bc_out, OP_special_object); 32363 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); 32364 put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx); 32365 } 32366 /* initialize the 'new.target' variable if needed */ 32367 if (s->new_target_var_idx >= 0) { 32368 dbuf_putc(&bc_out, OP_special_object); 32369 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET); 32370 put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx); 32371 } 32372 /* initialize the 'this' variable if needed. In a derived class 32373 constructor, this is initially uninitialized. */ 32374 if (s->this_var_idx >= 0) { 32375 if (s->is_derived_class_constructor) { 32376 dbuf_putc(&bc_out, OP_set_loc_uninitialized); 32377 dbuf_put_u16(&bc_out, s->this_var_idx); 32378 } else { 32379 dbuf_putc(&bc_out, OP_push_this); 32380 put_short_code(&bc_out, OP_put_loc, s->this_var_idx); 32381 } 32382 } 32383 /* initialize the 'arguments' variable if needed */ 32384 if (s->arguments_var_idx >= 0) { 32385 if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) { 32386 dbuf_putc(&bc_out, OP_special_object); 32387 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS); 32388 } else { 32389 dbuf_putc(&bc_out, OP_special_object); 32390 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS); 32391 } 32392 if (s->arguments_arg_idx >= 0) 32393 put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx); 32394 put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx); 32395 } 32396 /* initialize a reference to the current function if needed */ 32397 if (s->func_var_idx >= 0) { 32398 dbuf_putc(&bc_out, OP_special_object); 32399 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC); 32400 put_short_code(&bc_out, OP_put_loc, s->func_var_idx); 32401 } 32402 /* initialize the variable environment object if needed */ 32403 if (s->var_object_idx >= 0) { 32404 dbuf_putc(&bc_out, OP_special_object); 32405 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); 32406 put_short_code(&bc_out, OP_put_loc, s->var_object_idx); 32407 } 32408 if (s->arg_var_object_idx >= 0) { 32409 dbuf_putc(&bc_out, OP_special_object); 32410 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT); 32411 put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx); 32412 } 32413 32414 for (pos = 0; pos < bc_len; pos = pos_next) { 32415 int val; 32416 op = bc_buf[pos]; 32417 len = opcode_info[op].size; 32418 pos_next = pos + len; 32419 switch(op) { 32420 case OP_line_num: 32421 /* line number info (for debug). We put it in a separate 32422 compressed table to reduce memory usage and get better 32423 performance */ 32424 line_num = get_u32(bc_buf + pos + 1); 32425 break; 32426 32427 case OP_label: 32428 { 32429 label = get_u32(bc_buf + pos + 1); 32430 assert(label >= 0 && label < s->label_count); 32431 ls = &label_slots[label]; 32432 assert(ls->addr == -1); 32433 ls->addr = bc_out.size; 32434 /* resolve the relocation entries */ 32435 for(re = ls->first_reloc; re != NULL; re = re_next) { 32436 int diff = ls->addr - re->addr; 32437 re_next = re->next; 32438 switch (re->size) { 32439 case 4: 32440 put_u32(bc_out.buf + re->addr, diff); 32441 break; 32442 case 2: 32443 assert(diff == (int16_t)diff); 32444 put_u16(bc_out.buf + re->addr, diff); 32445 break; 32446 case 1: 32447 assert(diff == (int8_t)diff); 32448 put_u8(bc_out.buf + re->addr, diff); 32449 break; 32450 } 32451 js_free(ctx, re); 32452 } 32453 ls->first_reloc = NULL; 32454 } 32455 break; 32456 32457 case OP_call: 32458 case OP_call_method: 32459 { 32460 /* detect and transform tail calls */ 32461 int argc; 32462 argc = get_u16(bc_buf + pos + 1); 32463 if (code_match(&cc, pos_next, OP_return, -1)) { 32464 if (cc.line_num >= 0) line_num = cc.line_num; 32465 add_pc2line_info(s, bc_out.size, line_num); 32466 put_short_code(&bc_out, op + 1, argc); 32467 pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num); 32468 break; 32469 } 32470 add_pc2line_info(s, bc_out.size, line_num); 32471 put_short_code(&bc_out, op, argc); 32472 break; 32473 } 32474 goto no_change; 32475 32476 case OP_return: 32477 case OP_return_undef: 32478 case OP_return_async: 32479 case OP_throw: 32480 case OP_throw_error: 32481 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 32482 goto no_change; 32483 32484 case OP_goto: 32485 label = get_u32(bc_buf + pos + 1); 32486 has_goto: 32487 if (OPTIMIZE) { 32488 int line1 = -1; 32489 /* Use custom matcher because multiple labels can follow */ 32490 label = find_jump_target(s, label, &op1, &line1); 32491 if (code_has_label(&cc, pos_next, label)) { 32492 /* jump to next instruction: remove jump */ 32493 update_label(s, label, -1); 32494 break; 32495 } 32496 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) { 32497 /* jump to return/throw: remove jump, append return/throw */ 32498 /* updating the line number obfuscates assembly listing */ 32499 //if (line1 >= 0) line_num = line1; 32500 update_label(s, label, -1); 32501 add_pc2line_info(s, bc_out.size, line_num); 32502 dbuf_putc(&bc_out, op1); 32503 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 32504 break; 32505 } 32506 /* XXX: should duplicate single instructions followed by goto or return */ 32507 /* For example, can match one of these followed by return: 32508 push_i32 / push_const / push_atom_value / get_var / 32509 undefined / null / push_false / push_true / get_ref_value / 32510 get_loc / get_arg / get_var_ref 32511 */ 32512 } 32513 goto has_label; 32514 32515 case OP_gosub: 32516 label = get_u32(bc_buf + pos + 1); 32517 if (0 && OPTIMIZE) { 32518 label = find_jump_target(s, label, &op1, NULL); 32519 if (op1 == OP_ret) { 32520 update_label(s, label, -1); 32521 /* empty finally clause: remove gosub */ 32522 break; 32523 } 32524 } 32525 goto has_label; 32526 32527 case OP_catch: 32528 label = get_u32(bc_buf + pos + 1); 32529 goto has_label; 32530 32531 case OP_if_true: 32532 case OP_if_false: 32533 label = get_u32(bc_buf + pos + 1); 32534 if (OPTIMIZE) { 32535 label = find_jump_target(s, label, &op1, NULL); 32536 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */ 32537 if (code_has_label(&cc, pos_next, label)) { 32538 update_label(s, label, -1); 32539 dbuf_putc(&bc_out, OP_drop); 32540 break; 32541 } 32542 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */ 32543 if (code_match(&cc, pos_next, OP_goto, -1)) { 32544 int pos1 = cc.pos; 32545 int line1 = cc.line_num; 32546 if (code_has_label(&cc, pos1, label)) { 32547 if (line1 >= 0) line_num = line1; 32548 pos_next = pos1; 32549 update_label(s, label, -1); 32550 label = cc.label; 32551 op ^= OP_if_true ^ OP_if_false; 32552 } 32553 } 32554 } 32555 has_label: 32556 add_pc2line_info(s, bc_out.size, line_num); 32557 if (op == OP_goto) { 32558 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num); 32559 } 32560 assert(label >= 0 && label < s->label_count); 32561 ls = &label_slots[label]; 32562 #if SHORT_OPCODES 32563 jp = &s->jump_slots[s->jump_count++]; 32564 jp->op = op; 32565 jp->size = 4; 32566 jp->pos = bc_out.size + 1; 32567 jp->label = label; 32568 32569 if (ls->addr == -1) { 32570 int diff = ls->pos2 - pos - 1; 32571 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { 32572 jp->size = 1; 32573 jp->op = OP_if_false8 + (op - OP_if_false); 32574 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); 32575 dbuf_putc(&bc_out, 0); 32576 if (!add_reloc(ctx, ls, bc_out.size - 1, 1)) 32577 goto fail; 32578 break; 32579 } 32580 if (diff < 32768 && op == OP_goto) { 32581 jp->size = 2; 32582 jp->op = OP_goto16; 32583 dbuf_putc(&bc_out, OP_goto16); 32584 dbuf_put_u16(&bc_out, 0); 32585 if (!add_reloc(ctx, ls, bc_out.size - 2, 2)) 32586 goto fail; 32587 break; 32588 } 32589 } else { 32590 int diff = ls->addr - bc_out.size - 1; 32591 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) { 32592 jp->size = 1; 32593 jp->op = OP_if_false8 + (op - OP_if_false); 32594 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false)); 32595 dbuf_putc(&bc_out, diff); 32596 break; 32597 } 32598 if (diff == (int16_t)diff && op == OP_goto) { 32599 jp->size = 2; 32600 jp->op = OP_goto16; 32601 dbuf_putc(&bc_out, OP_goto16); 32602 dbuf_put_u16(&bc_out, diff); 32603 break; 32604 } 32605 } 32606 #endif 32607 dbuf_putc(&bc_out, op); 32608 dbuf_put_u32(&bc_out, ls->addr - bc_out.size); 32609 if (ls->addr == -1) { 32610 /* unresolved yet: create a new relocation entry */ 32611 if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) 32612 goto fail; 32613 } 32614 break; 32615 case OP_with_get_var: 32616 case OP_with_put_var: 32617 case OP_with_delete_var: 32618 case OP_with_make_ref: 32619 case OP_with_get_ref: 32620 case OP_with_get_ref_undef: 32621 { 32622 JSAtom atom; 32623 int is_with; 32624 32625 atom = get_u32(bc_buf + pos + 1); 32626 label = get_u32(bc_buf + pos + 5); 32627 is_with = bc_buf[pos + 9]; 32628 if (OPTIMIZE) { 32629 label = find_jump_target(s, label, &op1, NULL); 32630 } 32631 assert(label >= 0 && label < s->label_count); 32632 ls = &label_slots[label]; 32633 add_pc2line_info(s, bc_out.size, line_num); 32634 #if SHORT_OPCODES 32635 jp = &s->jump_slots[s->jump_count++]; 32636 jp->op = op; 32637 jp->size = 4; 32638 jp->pos = bc_out.size + 5; 32639 jp->label = label; 32640 #endif 32641 dbuf_putc(&bc_out, op); 32642 dbuf_put_u32(&bc_out, atom); 32643 dbuf_put_u32(&bc_out, ls->addr - bc_out.size); 32644 if (ls->addr == -1) { 32645 /* unresolved yet: create a new relocation entry */ 32646 if (!add_reloc(ctx, ls, bc_out.size - 4, 4)) 32647 goto fail; 32648 } 32649 dbuf_putc(&bc_out, is_with); 32650 } 32651 break; 32652 32653 case OP_drop: 32654 if (OPTIMIZE) { 32655 /* remove useless drops before return */ 32656 if (code_match(&cc, pos_next, OP_return_undef, -1)) { 32657 if (cc.line_num >= 0) line_num = cc.line_num; 32658 break; 32659 } 32660 } 32661 goto no_change; 32662 32663 case OP_null: 32664 #if SHORT_OPCODES 32665 if (OPTIMIZE) { 32666 /* transform null strict_eq into is_null */ 32667 if (code_match(&cc, pos_next, OP_strict_eq, -1)) { 32668 if (cc.line_num >= 0) line_num = cc.line_num; 32669 add_pc2line_info(s, bc_out.size, line_num); 32670 dbuf_putc(&bc_out, OP_is_null); 32671 pos_next = cc.pos; 32672 break; 32673 } 32674 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */ 32675 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { 32676 if (cc.line_num >= 0) line_num = cc.line_num; 32677 add_pc2line_info(s, bc_out.size, line_num); 32678 dbuf_putc(&bc_out, OP_is_null); 32679 pos_next = cc.pos; 32680 label = cc.label; 32681 op = cc.op ^ OP_if_false ^ OP_if_true; 32682 goto has_label; 32683 } 32684 } 32685 #endif 32686 /* fall thru */ 32687 case OP_push_false: 32688 case OP_push_true: 32689 if (OPTIMIZE) { 32690 val = (op == OP_push_true); 32691 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 32692 has_constant_test: 32693 if (cc.line_num >= 0) line_num = cc.line_num; 32694 if (val == cc.op - OP_if_false) { 32695 /* transform null if_false(l1) -> goto l1 */ 32696 /* transform false if_false(l1) -> goto l1 */ 32697 /* transform true if_true(l1) -> goto l1 */ 32698 pos_next = cc.pos; 32699 op = OP_goto; 32700 label = cc.label; 32701 goto has_goto; 32702 } else { 32703 /* transform null if_true(l1) -> nop */ 32704 /* transform false if_true(l1) -> nop */ 32705 /* transform true if_false(l1) -> nop */ 32706 pos_next = cc.pos; 32707 update_label(s, cc.label, -1); 32708 break; 32709 } 32710 } 32711 } 32712 goto no_change; 32713 32714 case OP_push_i32: 32715 if (OPTIMIZE) { 32716 /* transform i32(val) neg -> i32(-val) */ 32717 val = get_i32(bc_buf + pos + 1); 32718 if ((val != INT32_MIN && val != 0) 32719 && code_match(&cc, pos_next, OP_neg, -1)) { 32720 if (cc.line_num >= 0) line_num = cc.line_num; 32721 if (code_match(&cc, cc.pos, OP_drop, -1)) { 32722 if (cc.line_num >= 0) line_num = cc.line_num; 32723 } else { 32724 add_pc2line_info(s, bc_out.size, line_num); 32725 push_short_int(&bc_out, -val); 32726 } 32727 pos_next = cc.pos; 32728 break; 32729 } 32730 /* remove push/drop pairs generated by the parser */ 32731 if (code_match(&cc, pos_next, OP_drop, -1)) { 32732 if (cc.line_num >= 0) line_num = cc.line_num; 32733 pos_next = cc.pos; 32734 break; 32735 } 32736 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */ 32737 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 32738 val = (val != 0); 32739 goto has_constant_test; 32740 } 32741 add_pc2line_info(s, bc_out.size, line_num); 32742 push_short_int(&bc_out, val); 32743 break; 32744 } 32745 goto no_change; 32746 32747 #if SHORT_OPCODES 32748 case OP_push_const: 32749 case OP_fclosure: 32750 if (OPTIMIZE) { 32751 int idx = get_u32(bc_buf + pos + 1); 32752 if (idx < 256) { 32753 add_pc2line_info(s, bc_out.size, line_num); 32754 dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const); 32755 dbuf_putc(&bc_out, idx); 32756 break; 32757 } 32758 } 32759 goto no_change; 32760 32761 case OP_get_field: 32762 if (OPTIMIZE) { 32763 JSAtom atom = get_u32(bc_buf + pos + 1); 32764 if (atom == JS_ATOM_length) { 32765 JS_FreeAtom(ctx, atom); 32766 add_pc2line_info(s, bc_out.size, line_num); 32767 dbuf_putc(&bc_out, OP_get_length); 32768 break; 32769 } 32770 } 32771 goto no_change; 32772 #endif 32773 case OP_push_atom_value: 32774 if (OPTIMIZE) { 32775 JSAtom atom = get_u32(bc_buf + pos + 1); 32776 /* remove push/drop pairs generated by the parser */ 32777 if (code_match(&cc, pos_next, OP_drop, -1)) { 32778 JS_FreeAtom(ctx, atom); 32779 if (cc.line_num >= 0) line_num = cc.line_num; 32780 pos_next = cc.pos; 32781 break; 32782 } 32783 #if SHORT_OPCODES 32784 if (atom == JS_ATOM_empty_string) { 32785 JS_FreeAtom(ctx, atom); 32786 add_pc2line_info(s, bc_out.size, line_num); 32787 dbuf_putc(&bc_out, OP_push_empty_string); 32788 break; 32789 } 32790 #endif 32791 } 32792 goto no_change; 32793 32794 case OP_to_propkey: 32795 case OP_to_propkey2: 32796 if (OPTIMIZE) { 32797 /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */ 32798 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1) 32799 || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1) 32800 || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) { 32801 break; 32802 } 32803 } 32804 goto no_change; 32805 32806 case OP_undefined: 32807 if (OPTIMIZE) { 32808 /* remove push/drop pairs generated by the parser */ 32809 if (code_match(&cc, pos_next, OP_drop, -1)) { 32810 if (cc.line_num >= 0) line_num = cc.line_num; 32811 pos_next = cc.pos; 32812 break; 32813 } 32814 /* transform undefined return -> return_undefined */ 32815 if (code_match(&cc, pos_next, OP_return, -1)) { 32816 if (cc.line_num >= 0) line_num = cc.line_num; 32817 add_pc2line_info(s, bc_out.size, line_num); 32818 dbuf_putc(&bc_out, OP_return_undef); 32819 pos_next = cc.pos; 32820 break; 32821 } 32822 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */ 32823 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) { 32824 val = 0; 32825 goto has_constant_test; 32826 } 32827 #if SHORT_OPCODES 32828 /* transform undefined strict_eq -> is_undefined */ 32829 if (code_match(&cc, pos_next, OP_strict_eq, -1)) { 32830 if (cc.line_num >= 0) line_num = cc.line_num; 32831 add_pc2line_info(s, bc_out.size, line_num); 32832 dbuf_putc(&bc_out, OP_is_undefined); 32833 pos_next = cc.pos; 32834 break; 32835 } 32836 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */ 32837 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) { 32838 if (cc.line_num >= 0) line_num = cc.line_num; 32839 add_pc2line_info(s, bc_out.size, line_num); 32840 dbuf_putc(&bc_out, OP_is_undefined); 32841 pos_next = cc.pos; 32842 label = cc.label; 32843 op = cc.op ^ OP_if_false ^ OP_if_true; 32844 goto has_label; 32845 } 32846 #endif 32847 } 32848 goto no_change; 32849 32850 case OP_insert2: 32851 if (OPTIMIZE) { 32852 /* Transformation: 32853 insert2 put_field(a) drop -> put_field(a) 32854 insert2 put_var_strict(a) drop -> put_var_strict(a) 32855 */ 32856 if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { 32857 if (cc.line_num >= 0) line_num = cc.line_num; 32858 add_pc2line_info(s, bc_out.size, line_num); 32859 dbuf_putc(&bc_out, cc.op); 32860 dbuf_put_u32(&bc_out, cc.atom); 32861 pos_next = cc.pos; 32862 break; 32863 } 32864 } 32865 goto no_change; 32866 32867 case OP_dup: 32868 if (OPTIMIZE) { 32869 /* Transformation: dup put_x(n) drop -> put_x(n) */ 32870 int op1, line2 = -1; 32871 /* Transformation: dup put_x(n) -> set_x(n) */ 32872 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) { 32873 if (cc.line_num >= 0) line_num = cc.line_num; 32874 op1 = cc.op + 1; /* put_x -> set_x */ 32875 pos_next = cc.pos; 32876 if (code_match(&cc, cc.pos, OP_drop, -1)) { 32877 if (cc.line_num >= 0) line_num = cc.line_num; 32878 op1 -= 1; /* set_x drop -> put_x */ 32879 pos_next = cc.pos; 32880 if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) { 32881 line2 = cc.line_num; /* delay line number update */ 32882 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ 32883 pos_next = cc.pos; 32884 } 32885 } 32886 add_pc2line_info(s, bc_out.size, line_num); 32887 put_short_code(&bc_out, op1, cc.idx); 32888 if (line2 >= 0) line_num = line2; 32889 break; 32890 } 32891 } 32892 goto no_change; 32893 32894 case OP_get_loc: 32895 if (OPTIMIZE) { 32896 /* transformation: 32897 get_loc(n) post_dec put_loc(n) drop -> dec_loc(n) 32898 get_loc(n) post_inc put_loc(n) drop -> inc_loc(n) 32899 get_loc(n) dec dup put_loc(n) drop -> dec_loc(n) 32900 get_loc(n) inc dup put_loc(n) drop -> inc_loc(n) 32901 */ 32902 int idx; 32903 idx = get_u16(bc_buf + pos + 1); 32904 if (idx >= 256) 32905 goto no_change; 32906 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) || 32907 code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) { 32908 if (cc.line_num >= 0) line_num = cc.line_num; 32909 add_pc2line_info(s, bc_out.size, line_num); 32910 dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc); 32911 dbuf_putc(&bc_out, idx); 32912 pos_next = cc.pos; 32913 break; 32914 } 32915 /* transformation: 32916 get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n) 32917 */ 32918 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { 32919 if (cc.line_num >= 0) line_num = cc.line_num; 32920 add_pc2line_info(s, bc_out.size, line_num); 32921 #if SHORT_OPCODES 32922 if (cc.atom == JS_ATOM_empty_string) { 32923 JS_FreeAtom(ctx, cc.atom); 32924 dbuf_putc(&bc_out, OP_push_empty_string); 32925 } else 32926 #endif 32927 { 32928 dbuf_putc(&bc_out, OP_push_atom_value); 32929 dbuf_put_u32(&bc_out, cc.atom); 32930 } 32931 dbuf_putc(&bc_out, OP_add_loc); 32932 dbuf_putc(&bc_out, idx); 32933 pos_next = cc.pos; 32934 break; 32935 } 32936 /* transformation: 32937 get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n) 32938 */ 32939 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) { 32940 if (cc.line_num >= 0) line_num = cc.line_num; 32941 add_pc2line_info(s, bc_out.size, line_num); 32942 push_short_int(&bc_out, cc.label); 32943 dbuf_putc(&bc_out, OP_add_loc); 32944 dbuf_putc(&bc_out, idx); 32945 pos_next = cc.pos; 32946 break; 32947 } 32948 /* transformation: XXX: also do these: 32949 get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n) 32950 get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n) 32951 get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n) 32952 */ 32953 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)) { 32954 if (cc.line_num >= 0) line_num = cc.line_num; 32955 add_pc2line_info(s, bc_out.size, line_num); 32956 put_short_code(&bc_out, cc.op, cc.idx); 32957 dbuf_putc(&bc_out, OP_add_loc); 32958 dbuf_putc(&bc_out, idx); 32959 pos_next = cc.pos; 32960 break; 32961 } 32962 add_pc2line_info(s, bc_out.size, line_num); 32963 put_short_code(&bc_out, op, idx); 32964 break; 32965 } 32966 goto no_change; 32967 #if SHORT_OPCODES 32968 case OP_get_arg: 32969 case OP_get_var_ref: 32970 if (OPTIMIZE) { 32971 int idx; 32972 idx = get_u16(bc_buf + pos + 1); 32973 add_pc2line_info(s, bc_out.size, line_num); 32974 put_short_code(&bc_out, op, idx); 32975 break; 32976 } 32977 goto no_change; 32978 #endif 32979 case OP_put_loc: 32980 case OP_put_arg: 32981 case OP_put_var_ref: 32982 if (OPTIMIZE) { 32983 /* transformation: put_x(n) get_x(n) -> set_x(n) */ 32984 int idx; 32985 idx = get_u16(bc_buf + pos + 1); 32986 if (code_match(&cc, pos_next, op - 1, idx, -1)) { 32987 if (cc.line_num >= 0) line_num = cc.line_num; 32988 add_pc2line_info(s, bc_out.size, line_num); 32989 put_short_code(&bc_out, op + 1, idx); 32990 pos_next = cc.pos; 32991 break; 32992 } 32993 add_pc2line_info(s, bc_out.size, line_num); 32994 put_short_code(&bc_out, op, idx); 32995 break; 32996 } 32997 goto no_change; 32998 32999 case OP_post_inc: 33000 case OP_post_dec: 33001 if (OPTIMIZE) { 33002 /* transformation: 33003 post_inc put_x drop -> inc put_x 33004 post_inc perm3 put_field drop -> inc put_field 33005 post_inc perm3 put_var_strict drop -> inc put_var_strict 33006 post_inc perm4 put_array_el drop -> inc put_array_el 33007 */ 33008 int op1, idx; 33009 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) { 33010 if (cc.line_num >= 0) line_num = cc.line_num; 33011 op1 = cc.op; 33012 idx = cc.idx; 33013 pos_next = cc.pos; 33014 if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) { 33015 if (cc.line_num >= 0) line_num = cc.line_num; 33016 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */ 33017 pos_next = cc.pos; 33018 } 33019 add_pc2line_info(s, bc_out.size, line_num); 33020 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 33021 put_short_code(&bc_out, op1, idx); 33022 break; 33023 } 33024 if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) { 33025 if (cc.line_num >= 0) line_num = cc.line_num; 33026 add_pc2line_info(s, bc_out.size, line_num); 33027 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 33028 dbuf_putc(&bc_out, cc.op); 33029 dbuf_put_u32(&bc_out, cc.atom); 33030 pos_next = cc.pos; 33031 break; 33032 } 33033 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) { 33034 if (cc.line_num >= 0) line_num = cc.line_num; 33035 add_pc2line_info(s, bc_out.size, line_num); 33036 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec)); 33037 dbuf_putc(&bc_out, OP_put_array_el); 33038 pos_next = cc.pos; 33039 break; 33040 } 33041 } 33042 goto no_change; 33043 33044 #if SHORT_OPCODES 33045 case OP_typeof: 33046 if (OPTIMIZE) { 33047 /* simplify typeof tests */ 33048 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) { 33049 if (cc.line_num >= 0) line_num = cc.line_num; 33050 int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq; 33051 int op2 = -1; 33052 switch (cc.atom) { 33053 case JS_ATOM_undefined: 33054 op2 = OP_typeof_is_undefined; 33055 break; 33056 case JS_ATOM_function: 33057 op2 = OP_typeof_is_function; 33058 break; 33059 } 33060 if (op2 >= 0) { 33061 /* transform typeof(s) == "<type>" into is_<type> */ 33062 if (op1 == OP_strict_eq) { 33063 add_pc2line_info(s, bc_out.size, line_num); 33064 dbuf_putc(&bc_out, op2); 33065 JS_FreeAtom(ctx, cc.atom); 33066 pos_next = cc.pos; 33067 break; 33068 } 33069 if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) { 33070 /* transform typeof(s) != "<type>" if_false into is_<type> if_true */ 33071 if (cc.line_num >= 0) line_num = cc.line_num; 33072 add_pc2line_info(s, bc_out.size, line_num); 33073 dbuf_putc(&bc_out, op2); 33074 JS_FreeAtom(ctx, cc.atom); 33075 pos_next = cc.pos; 33076 label = cc.label; 33077 op = OP_if_true; 33078 goto has_label; 33079 } 33080 } 33081 } 33082 } 33083 goto no_change; 33084 #endif 33085 33086 default: 33087 no_change: 33088 add_pc2line_info(s, bc_out.size, line_num); 33089 dbuf_put(&bc_out, bc_buf + pos, len); 33090 break; 33091 } 33092 } 33093 33094 /* check that there were no missing labels */ 33095 for(i = 0; i < s->label_count; i++) { 33096 assert(label_slots[i].first_reloc == NULL); 33097 } 33098 #if SHORT_OPCODES 33099 if (OPTIMIZE) { 33100 /* more jump optimizations */ 33101 int patch_offsets = 0; 33102 for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) { 33103 LabelSlot *ls; 33104 JumpSlot *jp1; 33105 int j, pos, diff, delta; 33106 33107 delta = 3; 33108 switch (op = jp->op) { 33109 case OP_goto16: 33110 delta = 1; 33111 /* fall thru */ 33112 case OP_if_false: 33113 case OP_if_true: 33114 case OP_goto: 33115 pos = jp->pos; 33116 diff = s->label_slots[jp->label].addr - pos; 33117 if (diff >= -128 && diff <= 127 + delta) { 33118 //put_u8(bc_out.buf + pos, diff); 33119 jp->size = 1; 33120 if (op == OP_goto16) { 33121 bc_out.buf[pos - 1] = jp->op = OP_goto8; 33122 } else { 33123 bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); 33124 } 33125 goto shrink; 33126 } else 33127 if (diff == (int16_t)diff && op == OP_goto) { 33128 //put_u16(bc_out.buf + pos, diff); 33129 jp->size = 2; 33130 delta = 2; 33131 bc_out.buf[pos - 1] = jp->op = OP_goto16; 33132 shrink: 33133 /* XXX: should reduce complexity, using 2 finger copy scheme */ 33134 memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta, 33135 bc_out.size - pos - jp->size - delta); 33136 bc_out.size -= delta; 33137 patch_offsets++; 33138 for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) { 33139 if (ls->addr > pos) 33140 ls->addr -= delta; 33141 } 33142 for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) { 33143 if (jp1->pos > pos) 33144 jp1->pos -= delta; 33145 } 33146 for (j = 0; j < s->line_number_count; j++) { 33147 if (s->line_number_slots[j].pc > pos) 33148 s->line_number_slots[j].pc -= delta; 33149 } 33150 continue; 33151 } 33152 break; 33153 } 33154 } 33155 if (patch_offsets) { 33156 JumpSlot *jp1; 33157 int j; 33158 for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) { 33159 int diff1 = s->label_slots[jp1->label].addr - jp1->pos; 33160 switch (jp1->size) { 33161 case 1: 33162 put_u8(bc_out.buf + jp1->pos, diff1); 33163 break; 33164 case 2: 33165 put_u16(bc_out.buf + jp1->pos, diff1); 33166 break; 33167 case 4: 33168 put_u32(bc_out.buf + jp1->pos, diff1); 33169 break; 33170 } 33171 } 33172 } 33173 } 33174 js_free(ctx, s->jump_slots); 33175 s->jump_slots = NULL; 33176 #endif 33177 js_free(ctx, s->label_slots); 33178 s->label_slots = NULL; 33179 /* XXX: should delay until copying to runtime bytecode function */ 33180 compute_pc2line_info(s); 33181 js_free(ctx, s->line_number_slots); 33182 s->line_number_slots = NULL; 33183 /* set the new byte code */ 33184 dbuf_free(&s->byte_code); 33185 s->byte_code = bc_out; 33186 s->use_short_opcodes = TRUE; 33187 if (dbuf_error(&s->byte_code)) { 33188 JS_ThrowOutOfMemory(ctx); 33189 return -1; 33190 } 33191 return 0; 33192 fail: 33193 /* XXX: not safe */ 33194 dbuf_free(&bc_out); 33195 return -1; 33196 } 33197 33198 /* compute the maximum stack size needed by the function */ 33199 33200 typedef struct StackSizeState { 33201 int bc_len; 33202 int stack_len_max; 33203 uint16_t *stack_level_tab; 33204 int32_t *catch_pos_tab; 33205 int *pc_stack; 33206 int pc_stack_len; 33207 int pc_stack_size; 33208 } StackSizeState; 33209 33210 /* 'op' is only used for error indication */ 33211 static __exception int ss_check(JSContext *ctx, StackSizeState *s, 33212 int pos, int op, int stack_len, int catch_pos) 33213 { 33214 if ((unsigned)pos >= s->bc_len) { 33215 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); 33216 return -1; 33217 } 33218 if (stack_len > s->stack_len_max) { 33219 s->stack_len_max = stack_len; 33220 if (s->stack_len_max > JS_STACK_SIZE_MAX) { 33221 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); 33222 return -1; 33223 } 33224 } 33225 if (s->stack_level_tab[pos] != 0xffff) { 33226 /* already explored: check that the stack size is consistent */ 33227 if (s->stack_level_tab[pos] != stack_len) { 33228 JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)", 33229 s->stack_level_tab[pos], stack_len, pos); 33230 return -1; 33231 } else if (s->catch_pos_tab[pos] != catch_pos) { 33232 JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)", 33233 s->catch_pos_tab[pos], catch_pos, pos); 33234 return -1; 33235 } else { 33236 return 0; 33237 } 33238 } 33239 33240 /* mark as explored and store the stack size */ 33241 s->stack_level_tab[pos] = stack_len; 33242 s->catch_pos_tab[pos] = catch_pos; 33243 33244 /* queue the new PC to explore */ 33245 if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), 33246 &s->pc_stack_size, s->pc_stack_len + 1)) 33247 return -1; 33248 s->pc_stack[s->pc_stack_len++] = pos; 33249 return 0; 33250 } 33251 33252 static __exception int compute_stack_size(JSContext *ctx, 33253 JSFunctionDef *fd, 33254 int *pstack_size) 33255 { 33256 StackSizeState s_s, *s = &s_s; 33257 int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; 33258 const JSOpCode *oi; 33259 const uint8_t *bc_buf; 33260 33261 bc_buf = fd->byte_code.buf; 33262 s->bc_len = fd->byte_code.size; 33263 /* bc_len > 0 */ 33264 s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) * 33265 s->bc_len); 33266 if (!s->stack_level_tab) 33267 return -1; 33268 for(i = 0; i < s->bc_len; i++) 33269 s->stack_level_tab[i] = 0xffff; 33270 s->pc_stack = NULL; 33271 s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * 33272 s->bc_len); 33273 if (!s->catch_pos_tab) 33274 goto fail; 33275 33276 s->stack_len_max = 0; 33277 s->pc_stack_len = 0; 33278 s->pc_stack_size = 0; 33279 33280 /* breadth-first graph exploration */ 33281 if (ss_check(ctx, s, 0, OP_invalid, 0, -1)) 33282 goto fail; 33283 33284 while (s->pc_stack_len > 0) { 33285 pos = s->pc_stack[--s->pc_stack_len]; 33286 stack_len = s->stack_level_tab[pos]; 33287 catch_pos = s->catch_pos_tab[pos]; 33288 op = bc_buf[pos]; 33289 if (op == 0 || op >= OP_COUNT) { 33290 JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); 33291 goto fail; 33292 } 33293 oi = &short_opcode_info(op); 33294 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64) 33295 printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos); 33296 #endif 33297 pos_next = pos + oi->size; 33298 if (pos_next > s->bc_len) { 33299 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); 33300 goto fail; 33301 } 33302 n_pop = oi->n_pop; 33303 /* call pops a variable number of arguments */ 33304 if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) { 33305 n_pop += get_u16(bc_buf + pos + 1); 33306 } else { 33307 #if SHORT_OPCODES 33308 if (oi->fmt == OP_FMT_npopx) { 33309 n_pop += op - OP_call0; 33310 } 33311 #endif 33312 } 33313 33314 if (stack_len < n_pop) { 33315 JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos); 33316 goto fail; 33317 } 33318 stack_len += oi->n_push - n_pop; 33319 if (stack_len > s->stack_len_max) { 33320 s->stack_len_max = stack_len; 33321 if (s->stack_len_max > JS_STACK_SIZE_MAX) { 33322 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos); 33323 goto fail; 33324 } 33325 } 33326 switch(op) { 33327 case OP_tail_call: 33328 case OP_tail_call_method: 33329 case OP_return: 33330 case OP_return_undef: 33331 case OP_return_async: 33332 case OP_throw: 33333 case OP_throw_error: 33334 case OP_ret: 33335 goto done_insn; 33336 case OP_goto: 33337 diff = get_u32(bc_buf + pos + 1); 33338 pos_next = pos + 1 + diff; 33339 break; 33340 #if SHORT_OPCODES 33341 case OP_goto16: 33342 diff = (int16_t)get_u16(bc_buf + pos + 1); 33343 pos_next = pos + 1 + diff; 33344 break; 33345 case OP_goto8: 33346 diff = (int8_t)bc_buf[pos + 1]; 33347 pos_next = pos + 1 + diff; 33348 break; 33349 case OP_if_true8: 33350 case OP_if_false8: 33351 diff = (int8_t)bc_buf[pos + 1]; 33352 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 33353 goto fail; 33354 break; 33355 #endif 33356 case OP_if_true: 33357 case OP_if_false: 33358 diff = get_u32(bc_buf + pos + 1); 33359 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 33360 goto fail; 33361 break; 33362 case OP_gosub: 33363 diff = get_u32(bc_buf + pos + 1); 33364 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos)) 33365 goto fail; 33366 break; 33367 case OP_with_get_var: 33368 case OP_with_delete_var: 33369 diff = get_u32(bc_buf + pos + 5); 33370 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos)) 33371 goto fail; 33372 break; 33373 case OP_with_make_ref: 33374 case OP_with_get_ref: 33375 case OP_with_get_ref_undef: 33376 diff = get_u32(bc_buf + pos + 5); 33377 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos)) 33378 goto fail; 33379 break; 33380 case OP_with_put_var: 33381 diff = get_u32(bc_buf + pos + 5); 33382 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos)) 33383 goto fail; 33384 break; 33385 case OP_catch: 33386 diff = get_u32(bc_buf + pos + 1); 33387 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) 33388 goto fail; 33389 catch_pos = pos; 33390 break; 33391 case OP_for_of_start: 33392 case OP_for_await_of_start: 33393 catch_pos = pos; 33394 break; 33395 /* we assume the catch offset entry is only removed with 33396 some op codes */ 33397 case OP_drop: 33398 catch_level = stack_len; 33399 goto check_catch; 33400 case OP_nip: 33401 catch_level = stack_len - 1; 33402 goto check_catch; 33403 case OP_nip1: 33404 catch_level = stack_len - 1; 33405 goto check_catch; 33406 case OP_iterator_close: 33407 catch_level = stack_len + 2; 33408 check_catch: 33409 /* Note: for for_of_start/for_await_of_start we consider 33410 the catch offset is on the first stack entry instead of 33411 the thirst */ 33412 if (catch_pos >= 0) { 33413 int level; 33414 level = s->stack_level_tab[catch_pos]; 33415 if (bc_buf[catch_pos] != OP_catch) 33416 level++; /* for_of_start, for_wait_of_start */ 33417 /* catch_level = stack_level before op_catch is executed ? */ 33418 if (catch_level == level) { 33419 catch_pos = s->catch_pos_tab[catch_pos]; 33420 } 33421 } 33422 break; 33423 case OP_nip_catch: 33424 if (catch_pos < 0) { 33425 JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos); 33426 goto fail; 33427 } 33428 stack_len = s->stack_level_tab[catch_pos]; 33429 if (bc_buf[catch_pos] != OP_catch) 33430 stack_len++; /* for_of_start, for_wait_of_start */ 33431 stack_len++; /* no stack overflow is possible by construction */ 33432 catch_pos = s->catch_pos_tab[catch_pos]; 33433 break; 33434 default: 33435 break; 33436 } 33437 if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos)) 33438 goto fail; 33439 done_insn: ; 33440 } 33441 js_free(ctx, s->pc_stack); 33442 js_free(ctx, s->catch_pos_tab); 33443 js_free(ctx, s->stack_level_tab); 33444 *pstack_size = s->stack_len_max; 33445 return 0; 33446 fail: 33447 js_free(ctx, s->pc_stack); 33448 js_free(ctx, s->catch_pos_tab); 33449 js_free(ctx, s->stack_level_tab); 33450 *pstack_size = 0; 33451 return -1; 33452 } 33453 33454 static int add_module_variables(JSContext *ctx, JSFunctionDef *fd) 33455 { 33456 int i, idx; 33457 JSModuleDef *m = fd->module; 33458 JSExportEntry *me; 33459 JSGlobalVar *hf; 33460 33461 /* The imported global variables were added as closure variables 33462 in js_parse_import(). We add here the module global 33463 variables. */ 33464 33465 for(i = 0; i < fd->global_var_count; i++) { 33466 hf = &fd->global_vars[i]; 33467 if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const, 33468 hf->is_lexical, FALSE) < 0) 33469 return -1; 33470 } 33471 33472 /* resolve the variable names of the local exports */ 33473 for(i = 0; i < m->export_entries_count; i++) { 33474 me = &m->export_entries[i]; 33475 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 33476 idx = find_closure_var(ctx, fd, me->local_name); 33477 if (idx < 0) { 33478 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist", 33479 me->local_name); 33480 return -1; 33481 } 33482 me->u.local.var_idx = idx; 33483 } 33484 } 33485 return 0; 33486 } 33487 33488 /* create a function object from a function definition. The function 33489 definition is freed. All the child functions are also created. It 33490 must be done this way to resolve all the variables. */ 33491 static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) 33492 { 33493 JSValue func_obj; 33494 JSFunctionBytecode *b; 33495 struct list_head *el, *el1; 33496 int stack_size, scope, idx; 33497 int function_size, byte_code_offset, cpool_offset; 33498 int closure_var_offset, vardefs_offset; 33499 33500 /* recompute scope linkage */ 33501 for (scope = 0; scope < fd->scope_count; scope++) { 33502 fd->scopes[scope].first = -1; 33503 } 33504 if (fd->has_parameter_expressions) { 33505 /* special end of variable list marker for the argument scope */ 33506 fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END; 33507 } 33508 for (idx = 0; idx < fd->var_count; idx++) { 33509 JSVarDef *vd = &fd->vars[idx]; 33510 vd->scope_next = fd->scopes[vd->scope_level].first; 33511 fd->scopes[vd->scope_level].first = idx; 33512 } 33513 for (scope = 2; scope < fd->scope_count; scope++) { 33514 JSVarScope *sd = &fd->scopes[scope]; 33515 if (sd->first < 0) 33516 sd->first = fd->scopes[sd->parent].first; 33517 } 33518 for (idx = 0; idx < fd->var_count; idx++) { 33519 JSVarDef *vd = &fd->vars[idx]; 33520 if (vd->scope_next < 0 && vd->scope_level > 1) { 33521 scope = fd->scopes[vd->scope_level].parent; 33522 vd->scope_next = fd->scopes[scope].first; 33523 } 33524 } 33525 33526 /* if the function contains an eval call, the closure variables 33527 are used to compile the eval and they must be ordered by scope, 33528 so it is necessary to create the closure variables before any 33529 other variable lookup is done. */ 33530 if (fd->has_eval_call) 33531 add_eval_variables(ctx, fd); 33532 33533 /* add the module global variables in the closure */ 33534 if (fd->module) { 33535 if (add_module_variables(ctx, fd)) 33536 goto fail; 33537 } 33538 33539 /* first create all the child functions */ 33540 list_for_each_safe(el, el1, &fd->child_list) { 33541 JSFunctionDef *fd1; 33542 int cpool_idx; 33543 33544 fd1 = list_entry(el, JSFunctionDef, link); 33545 cpool_idx = fd1->parent_cpool_idx; 33546 func_obj = js_create_function(ctx, fd1); 33547 if (JS_IsException(func_obj)) 33548 goto fail; 33549 /* save it in the constant pool */ 33550 assert(cpool_idx >= 0); 33551 fd->cpool[cpool_idx] = func_obj; 33552 } 33553 33554 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4) 33555 if (!(fd->js_mode & JS_MODE_STRIP)) { 33556 printf("pass 1\n"); 33557 dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size, 33558 fd->args, fd->arg_count, fd->vars, fd->var_count, 33559 fd->closure_var, fd->closure_var_count, 33560 fd->cpool, fd->cpool_count, fd->source, fd->line_num, 33561 fd->label_slots, NULL); 33562 printf("\n"); 33563 } 33564 #endif 33565 33566 if (resolve_variables(ctx, fd)) 33567 goto fail; 33568 33569 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2) 33570 if (!(fd->js_mode & JS_MODE_STRIP)) { 33571 printf("pass 2\n"); 33572 dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size, 33573 fd->args, fd->arg_count, fd->vars, fd->var_count, 33574 fd->closure_var, fd->closure_var_count, 33575 fd->cpool, fd->cpool_count, fd->source, fd->line_num, 33576 fd->label_slots, NULL); 33577 printf("\n"); 33578 } 33579 #endif 33580 33581 if (resolve_labels(ctx, fd)) 33582 goto fail; 33583 33584 if (compute_stack_size(ctx, fd, &stack_size) < 0) 33585 goto fail; 33586 33587 if (fd->js_mode & JS_MODE_STRIP) { 33588 function_size = offsetof(JSFunctionBytecode, debug); 33589 } else { 33590 function_size = sizeof(*b); 33591 } 33592 cpool_offset = function_size; 33593 function_size += fd->cpool_count * sizeof(*fd->cpool); 33594 vardefs_offset = function_size; 33595 if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) { 33596 function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs); 33597 } 33598 closure_var_offset = function_size; 33599 function_size += fd->closure_var_count * sizeof(*fd->closure_var); 33600 byte_code_offset = function_size; 33601 function_size += fd->byte_code.size; 33602 33603 b = js_mallocz(ctx, function_size); 33604 if (!b) 33605 goto fail; 33606 b->header.ref_count = 1; 33607 33608 b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset); 33609 b->byte_code_len = fd->byte_code.size; 33610 memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size); 33611 js_free(ctx, fd->byte_code.buf); 33612 fd->byte_code.buf = NULL; 33613 33614 b->func_name = fd->func_name; 33615 if (fd->arg_count + fd->var_count > 0) { 33616 if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) { 33617 /* Strip variable definitions not needed at runtime */ 33618 int i; 33619 for(i = 0; i < fd->var_count; i++) { 33620 JS_FreeAtom(ctx, fd->vars[i].var_name); 33621 } 33622 for(i = 0; i < fd->arg_count; i++) { 33623 JS_FreeAtom(ctx, fd->args[i].var_name); 33624 } 33625 for(i = 0; i < fd->closure_var_count; i++) { 33626 JS_FreeAtom(ctx, fd->closure_var[i].var_name); 33627 fd->closure_var[i].var_name = JS_ATOM_NULL; 33628 } 33629 } else { 33630 b->vardefs = (void *)((uint8_t*)b + vardefs_offset); 33631 memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); 33632 memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); 33633 } 33634 b->var_count = fd->var_count; 33635 b->arg_count = fd->arg_count; 33636 b->defined_arg_count = fd->defined_arg_count; 33637 js_free(ctx, fd->args); 33638 js_free(ctx, fd->vars); 33639 } 33640 b->cpool_count = fd->cpool_count; 33641 if (b->cpool_count) { 33642 b->cpool = (void *)((uint8_t*)b + cpool_offset); 33643 memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool)); 33644 } 33645 js_free(ctx, fd->cpool); 33646 fd->cpool = NULL; 33647 33648 b->stack_size = stack_size; 33649 33650 b->perf_trampoline = NULL; 33651 33652 if (fd->js_mode & JS_MODE_STRIP) { 33653 JS_FreeAtom(ctx, fd->filename); 33654 dbuf_free(&fd->pc2line); // probably useless 33655 } else { 33656 /* XXX: source and pc2line info should be packed at the end of the 33657 JSFunctionBytecode structure, avoiding allocation overhead 33658 */ 33659 b->has_debug = 1; 33660 b->debug.filename = fd->filename; 33661 b->debug.line_num = fd->line_num; 33662 33663 //DynBuf pc2line; 33664 //compute_pc2line_info(fd, &pc2line); 33665 //js_free(ctx, fd->line_number_slots) 33666 b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size); 33667 if (!b->debug.pc2line_buf) 33668 b->debug.pc2line_buf = fd->pc2line.buf; 33669 b->debug.pc2line_len = fd->pc2line.size; 33670 b->debug.source = fd->source; 33671 b->debug.source_len = fd->source_len; 33672 } 33673 if (fd->scopes != fd->def_scope_array) 33674 js_free(ctx, fd->scopes); 33675 33676 b->closure_var_count = fd->closure_var_count; 33677 if (b->closure_var_count) { 33678 b->closure_var = (void *)((uint8_t*)b + closure_var_offset); 33679 memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var)); 33680 } 33681 js_free(ctx, fd->closure_var); 33682 fd->closure_var = NULL; 33683 33684 b->has_prototype = fd->has_prototype; 33685 b->has_simple_parameter_list = fd->has_simple_parameter_list; 33686 b->js_mode = fd->js_mode; 33687 b->is_derived_class_constructor = fd->is_derived_class_constructor; 33688 b->func_kind = fd->func_kind; 33689 b->need_home_object = (fd->home_object_var_idx >= 0 || 33690 fd->need_home_object); 33691 b->new_target_allowed = fd->new_target_allowed; 33692 b->super_call_allowed = fd->super_call_allowed; 33693 b->super_allowed = fd->super_allowed; 33694 b->arguments_allowed = fd->arguments_allowed; 33695 b->backtrace_barrier = fd->backtrace_barrier; 33696 b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT || 33697 fd->eval_type == JS_EVAL_TYPE_INDIRECT); 33698 b->realm = JS_DupContext(ctx); 33699 33700 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); 33701 33702 #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1) 33703 if (!(fd->js_mode & JS_MODE_STRIP)) { 33704 js_dump_function_bytecode(ctx, b); 33705 } 33706 #endif 33707 33708 if (fd->parent) { 33709 /* remove from parent list */ 33710 list_del(&fd->link); 33711 } 33712 33713 js_free(ctx, fd); 33714 return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); 33715 fail: 33716 js_free_function_def(ctx, fd); 33717 return JS_EXCEPTION; 33718 } 33719 33720 static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) 33721 { 33722 int i; 33723 33724 #if 0 33725 { 33726 char buf[ATOM_GET_STR_BUF_SIZE]; 33727 printf("freeing %s\n", 33728 JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); 33729 } 33730 #endif 33731 free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE); 33732 33733 if (b->vardefs) { 33734 for(i = 0; i < b->arg_count + b->var_count; i++) { 33735 JS_FreeAtomRT(rt, b->vardefs[i].var_name); 33736 } 33737 } 33738 for(i = 0; i < b->cpool_count; i++) 33739 JS_FreeValueRT(rt, b->cpool[i]); 33740 33741 for(i = 0; i < b->closure_var_count; i++) { 33742 JSClosureVar *cv = &b->closure_var[i]; 33743 JS_FreeAtomRT(rt, cv->var_name); 33744 } 33745 if (b->realm) 33746 JS_FreeContext(b->realm); 33747 33748 JS_FreeAtomRT(rt, b->func_name); 33749 if (b->has_debug) { 33750 JS_FreeAtomRT(rt, b->debug.filename); 33751 js_free_rt(rt, b->debug.pc2line_buf); 33752 js_free_rt(rt, b->debug.source); 33753 } 33754 33755 remove_gc_object(&b->header); 33756 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) { 33757 list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list); 33758 } else { 33759 js_free_rt(rt, b); 33760 } 33761 } 33762 33763 static __exception int js_parse_directives(JSParseState *s) 33764 { 33765 char str[20]; 33766 JSParsePos pos; 33767 BOOL has_semi; 33768 33769 if (s->token.val != TOK_STRING) 33770 return 0; 33771 33772 js_parse_get_pos(s, &pos); 33773 33774 while(s->token.val == TOK_STRING) { 33775 /* Copy actual source string representation */ 33776 snprintf(str, sizeof str, "%.*s", 33777 (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1); 33778 33779 if (next_token(s)) 33780 return -1; 33781 33782 has_semi = FALSE; 33783 switch (s->token.val) { 33784 case ';': 33785 if (next_token(s)) 33786 return -1; 33787 has_semi = TRUE; 33788 break; 33789 case '}': 33790 case TOK_EOF: 33791 has_semi = TRUE; 33792 break; 33793 case TOK_NUMBER: 33794 case TOK_STRING: 33795 case TOK_TEMPLATE: 33796 case TOK_IDENT: 33797 case TOK_REGEXP: 33798 case TOK_DEC: 33799 case TOK_INC: 33800 case TOK_NULL: 33801 case TOK_FALSE: 33802 case TOK_TRUE: 33803 case TOK_IF: 33804 case TOK_RETURN: 33805 case TOK_VAR: 33806 case TOK_THIS: 33807 case TOK_DELETE: 33808 case TOK_TYPEOF: 33809 case TOK_NEW: 33810 case TOK_DO: 33811 case TOK_WHILE: 33812 case TOK_FOR: 33813 case TOK_SWITCH: 33814 case TOK_THROW: 33815 case TOK_TRY: 33816 case TOK_FUNCTION: 33817 case TOK_DEBUGGER: 33818 case TOK_WITH: 33819 case TOK_CLASS: 33820 case TOK_CONST: 33821 case TOK_ENUM: 33822 case TOK_EXPORT: 33823 case TOK_IMPORT: 33824 case TOK_SUPER: 33825 case TOK_INTERFACE: 33826 case TOK_LET: 33827 case TOK_PACKAGE: 33828 case TOK_PRIVATE: 33829 case TOK_PROTECTED: 33830 case TOK_PUBLIC: 33831 case TOK_STATIC: 33832 /* automatic insertion of ';' */ 33833 if (s->got_lf) 33834 has_semi = TRUE; 33835 break; 33836 default: 33837 break; 33838 } 33839 if (!has_semi) 33840 break; 33841 if (!strcmp(str, "use strict")) { 33842 s->cur_func->has_use_strict = TRUE; 33843 s->cur_func->js_mode |= JS_MODE_STRICT; 33844 } 33845 #if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8) 33846 else if (!strcmp(str, "use strip")) { 33847 s->cur_func->js_mode |= JS_MODE_STRIP; 33848 } 33849 #endif 33850 #ifdef CONFIG_BIGNUM 33851 else if (s->ctx->bignum_ext && !strcmp(str, "use math")) { 33852 s->cur_func->js_mode |= JS_MODE_MATH; 33853 } 33854 #endif 33855 } 33856 return js_parse_seek_token(s, &pos); 33857 } 33858 33859 static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd, 33860 JSAtom func_name) 33861 { 33862 JSAtom name; 33863 int i, idx; 33864 33865 if (fd->js_mode & JS_MODE_STRICT) { 33866 if (!fd->has_simple_parameter_list && fd->has_use_strict) { 33867 return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter"); 33868 } 33869 if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) { 33870 return js_parse_error(s, "invalid function name in strict code"); 33871 } 33872 for (idx = 0; idx < fd->arg_count; idx++) { 33873 name = fd->args[idx].var_name; 33874 33875 if (name == JS_ATOM_eval || name == JS_ATOM_arguments) { 33876 return js_parse_error(s, "invalid argument name in strict code"); 33877 } 33878 } 33879 } 33880 /* check async_generator case */ 33881 if ((fd->js_mode & JS_MODE_STRICT) 33882 || !fd->has_simple_parameter_list 33883 || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC) 33884 || fd->func_type == JS_PARSE_FUNC_ARROW 33885 || fd->func_type == JS_PARSE_FUNC_METHOD) { 33886 for (idx = 0; idx < fd->arg_count; idx++) { 33887 name = fd->args[idx].var_name; 33888 if (name != JS_ATOM_NULL) { 33889 for (i = 0; i < idx; i++) { 33890 if (fd->args[i].var_name == name) 33891 goto duplicate; 33892 } 33893 /* Check if argument name duplicates a destructuring parameter */ 33894 /* XXX: should have a flag for such variables */ 33895 for (i = 0; i < fd->var_count; i++) { 33896 if (fd->vars[i].var_name == name && 33897 fd->vars[i].scope_level == 0) 33898 goto duplicate; 33899 } 33900 } 33901 } 33902 } 33903 return 0; 33904 33905 duplicate: 33906 return js_parse_error(s, "duplicate argument names not allowed in this context"); 33907 } 33908 33909 /* create a function to initialize class fields */ 33910 static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) 33911 { 33912 JSFunctionDef *fd; 33913 33914 fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE, 33915 s->filename, 0); 33916 if (!fd) 33917 return NULL; 33918 fd->func_name = JS_ATOM_NULL; 33919 fd->has_prototype = FALSE; 33920 fd->has_home_object = TRUE; 33921 33922 fd->has_arguments_binding = FALSE; 33923 fd->has_this_binding = TRUE; 33924 fd->is_derived_class_constructor = FALSE; 33925 fd->new_target_allowed = TRUE; 33926 fd->super_call_allowed = FALSE; 33927 fd->super_allowed = fd->has_home_object; 33928 fd->arguments_allowed = FALSE; 33929 33930 fd->func_kind = JS_FUNC_NORMAL; 33931 fd->func_type = JS_PARSE_FUNC_METHOD; 33932 return fd; 33933 } 33934 33935 /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and 33936 JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ 33937 static __exception int js_parse_function_decl2(JSParseState *s, 33938 JSParseFunctionEnum func_type, 33939 JSFunctionKindEnum func_kind, 33940 JSAtom func_name, 33941 const uint8_t *ptr, 33942 int function_line_num, 33943 JSParseExportEnum export_flag, 33944 JSFunctionDef **pfd) 33945 { 33946 JSContext *ctx = s->ctx; 33947 JSFunctionDef *fd = s->cur_func; 33948 BOOL is_expr; 33949 int func_idx, lexical_func_idx = -1; 33950 BOOL has_opt_arg; 33951 BOOL create_func_var = FALSE; 33952 33953 is_expr = (func_type != JS_PARSE_FUNC_STATEMENT && 33954 func_type != JS_PARSE_FUNC_VAR); 33955 33956 if (func_type == JS_PARSE_FUNC_STATEMENT || 33957 func_type == JS_PARSE_FUNC_VAR || 33958 func_type == JS_PARSE_FUNC_EXPR) { 33959 if (func_kind == JS_FUNC_NORMAL && 33960 token_is_pseudo_keyword(s, JS_ATOM_async) && 33961 peek_token(s, TRUE) != '\n') { 33962 if (next_token(s)) 33963 return -1; 33964 func_kind = JS_FUNC_ASYNC; 33965 } 33966 if (next_token(s)) 33967 return -1; 33968 if (s->token.val == '*') { 33969 if (next_token(s)) 33970 return -1; 33971 func_kind |= JS_FUNC_GENERATOR; 33972 } 33973 33974 if (s->token.val == TOK_IDENT) { 33975 if (s->token.u.ident.is_reserved || 33976 (s->token.u.ident.atom == JS_ATOM_yield && 33977 func_type == JS_PARSE_FUNC_EXPR && 33978 (func_kind & JS_FUNC_GENERATOR)) || 33979 (s->token.u.ident.atom == JS_ATOM_await && 33980 ((func_type == JS_PARSE_FUNC_EXPR && 33981 (func_kind & JS_FUNC_ASYNC)) || 33982 func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { 33983 return js_parse_error_reserved_identifier(s); 33984 } 33985 } 33986 if (s->token.val == TOK_IDENT || 33987 (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) || 33988 (s->token.val == TOK_AWAIT && !s->is_module)) && 33989 func_type == JS_PARSE_FUNC_EXPR)) { 33990 func_name = JS_DupAtom(ctx, s->token.u.ident.atom); 33991 if (next_token(s)) { 33992 JS_FreeAtom(ctx, func_name); 33993 return -1; 33994 } 33995 } else { 33996 if (func_type != JS_PARSE_FUNC_EXPR && 33997 export_flag != JS_PARSE_EXPORT_DEFAULT) { 33998 return js_parse_error(s, "function name expected"); 33999 } 34000 } 34001 } else if (func_type != JS_PARSE_FUNC_ARROW) { 34002 func_name = JS_DupAtom(ctx, func_name); 34003 } 34004 34005 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE && 34006 (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) { 34007 JSGlobalVar *hf; 34008 hf = find_global_var(fd, func_name); 34009 /* XXX: should check scope chain */ 34010 if (hf && hf->scope_level == fd->scope_level) { 34011 js_parse_error(s, "invalid redefinition of global identifier in module code"); 34012 JS_FreeAtom(ctx, func_name); 34013 return -1; 34014 } 34015 } 34016 34017 if (func_type == JS_PARSE_FUNC_VAR) { 34018 if (!(fd->js_mode & JS_MODE_STRICT) 34019 && func_kind == JS_FUNC_NORMAL 34020 && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0 34021 && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET)) 34022 && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) { 34023 create_func_var = TRUE; 34024 } 34025 /* Create the lexical name here so that the function closure 34026 contains it */ 34027 if (fd->is_eval && 34028 (fd->eval_type == JS_EVAL_TYPE_GLOBAL || 34029 fd->eval_type == JS_EVAL_TYPE_MODULE) && 34030 fd->scope_level == fd->body_scope) { 34031 /* avoid creating a lexical variable in the global 34032 scope. XXX: check annex B */ 34033 JSGlobalVar *hf; 34034 hf = find_global_var(fd, func_name); 34035 /* XXX: should check scope chain */ 34036 if (hf && hf->scope_level == fd->scope_level) { 34037 js_parse_error(s, "invalid redefinition of global identifier"); 34038 JS_FreeAtom(ctx, func_name); 34039 return -1; 34040 } 34041 } else { 34042 /* Always create a lexical name, fail if at the same scope as 34043 existing name */ 34044 /* Lexical variable will be initialized upon entering scope */ 34045 lexical_func_idx = define_var(s, fd, func_name, 34046 func_kind != JS_FUNC_NORMAL ? 34047 JS_VAR_DEF_NEW_FUNCTION_DECL : 34048 JS_VAR_DEF_FUNCTION_DECL); 34049 if (lexical_func_idx < 0) { 34050 JS_FreeAtom(ctx, func_name); 34051 return -1; 34052 } 34053 } 34054 } 34055 34056 fd = js_new_function_def(ctx, fd, FALSE, is_expr, 34057 s->filename, function_line_num); 34058 if (!fd) { 34059 JS_FreeAtom(ctx, func_name); 34060 return -1; 34061 } 34062 if (pfd) 34063 *pfd = fd; 34064 s->cur_func = fd; 34065 fd->func_name = func_name; 34066 /* XXX: test !fd->is_generator is always false */ 34067 fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT || 34068 func_type == JS_PARSE_FUNC_VAR || 34069 func_type == JS_PARSE_FUNC_EXPR) && 34070 func_kind == JS_FUNC_NORMAL; 34071 fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD || 34072 func_type == JS_PARSE_FUNC_GETTER || 34073 func_type == JS_PARSE_FUNC_SETTER || 34074 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || 34075 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); 34076 fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && 34077 func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT); 34078 fd->has_this_binding = fd->has_arguments_binding; 34079 fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); 34080 if (func_type == JS_PARSE_FUNC_ARROW) { 34081 fd->new_target_allowed = fd->parent->new_target_allowed; 34082 fd->super_call_allowed = fd->parent->super_call_allowed; 34083 fd->super_allowed = fd->parent->super_allowed; 34084 fd->arguments_allowed = fd->parent->arguments_allowed; 34085 } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { 34086 fd->new_target_allowed = TRUE; // although new.target === undefined 34087 fd->super_call_allowed = FALSE; 34088 fd->super_allowed = TRUE; 34089 fd->arguments_allowed = FALSE; 34090 } else { 34091 fd->new_target_allowed = TRUE; 34092 fd->super_call_allowed = fd->is_derived_class_constructor; 34093 fd->super_allowed = fd->has_home_object; 34094 fd->arguments_allowed = TRUE; 34095 } 34096 34097 /* fd->in_function_body == FALSE prevents yield/await during the parsing 34098 of the arguments in generator/async functions. They are parsed as 34099 regular identifiers for other function kinds. */ 34100 fd->func_kind = func_kind; 34101 fd->func_type = func_type; 34102 34103 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || 34104 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { 34105 /* error if not invoked as a constructor */ 34106 emit_op(s, OP_check_ctor); 34107 } 34108 34109 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) { 34110 emit_class_field_init(s); 34111 } 34112 34113 /* parse arguments */ 34114 fd->has_simple_parameter_list = TRUE; 34115 fd->has_parameter_expressions = FALSE; 34116 has_opt_arg = FALSE; 34117 if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) { 34118 JSAtom name; 34119 if (s->token.u.ident.is_reserved) { 34120 js_parse_error_reserved_identifier(s); 34121 goto fail; 34122 } 34123 name = s->token.u.ident.atom; 34124 if (add_arg(ctx, fd, name) < 0) 34125 goto fail; 34126 fd->defined_arg_count = 1; 34127 } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { 34128 if (s->token.val == '(') { 34129 int skip_bits; 34130 /* if there is an '=' inside the parameter list, we 34131 consider there is a parameter expression inside */ 34132 js_parse_skip_parens_token(s, &skip_bits, FALSE); 34133 if (skip_bits & SKIP_HAS_ASSIGNMENT) 34134 fd->has_parameter_expressions = TRUE; 34135 if (next_token(s)) 34136 goto fail; 34137 } else { 34138 if (js_parse_expect(s, '(')) 34139 goto fail; 34140 } 34141 34142 if (fd->has_parameter_expressions) { 34143 fd->scope_level = -1; /* force no parent scope */ 34144 if (push_scope(s) < 0) 34145 return -1; 34146 } 34147 34148 while (s->token.val != ')') { 34149 JSAtom name; 34150 BOOL rest = FALSE; 34151 int idx, has_initializer; 34152 34153 if (s->token.val == TOK_ELLIPSIS) { 34154 fd->has_simple_parameter_list = FALSE; 34155 rest = TRUE; 34156 if (next_token(s)) 34157 goto fail; 34158 } 34159 if (s->token.val == '[' || s->token.val == '{') { 34160 fd->has_simple_parameter_list = FALSE; 34161 if (rest) { 34162 emit_op(s, OP_rest); 34163 emit_u16(s, fd->arg_count); 34164 } else { 34165 /* unnamed arg for destructuring */ 34166 idx = add_arg(ctx, fd, JS_ATOM_NULL); 34167 emit_op(s, OP_get_arg); 34168 emit_u16(s, idx); 34169 } 34170 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE); 34171 if (has_initializer < 0) 34172 goto fail; 34173 if (has_initializer) 34174 has_opt_arg = TRUE; 34175 if (!has_opt_arg) 34176 fd->defined_arg_count++; 34177 } else if (s->token.val == TOK_IDENT) { 34178 if (s->token.u.ident.is_reserved) { 34179 js_parse_error_reserved_identifier(s); 34180 goto fail; 34181 } 34182 name = s->token.u.ident.atom; 34183 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) { 34184 js_parse_error_reserved_identifier(s); 34185 goto fail; 34186 } 34187 if (fd->has_parameter_expressions) { 34188 if (js_parse_check_duplicate_parameter(s, name)) 34189 goto fail; 34190 if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) 34191 goto fail; 34192 } 34193 /* XXX: could avoid allocating an argument if rest is true */ 34194 idx = add_arg(ctx, fd, name); 34195 if (idx < 0) 34196 goto fail; 34197 if (next_token(s)) 34198 goto fail; 34199 if (rest) { 34200 emit_op(s, OP_rest); 34201 emit_u16(s, idx); 34202 if (fd->has_parameter_expressions) { 34203 emit_op(s, OP_dup); 34204 emit_op(s, OP_scope_put_var_init); 34205 emit_atom(s, name); 34206 emit_u16(s, fd->scope_level); 34207 } 34208 emit_op(s, OP_put_arg); 34209 emit_u16(s, idx); 34210 fd->has_simple_parameter_list = FALSE; 34211 has_opt_arg = TRUE; 34212 } else if (s->token.val == '=') { 34213 int label; 34214 34215 fd->has_simple_parameter_list = FALSE; 34216 has_opt_arg = TRUE; 34217 34218 if (next_token(s)) 34219 goto fail; 34220 34221 label = new_label(s); 34222 emit_op(s, OP_get_arg); 34223 emit_u16(s, idx); 34224 emit_op(s, OP_dup); 34225 emit_op(s, OP_undefined); 34226 emit_op(s, OP_strict_eq); 34227 emit_goto(s, OP_if_false, label); 34228 emit_op(s, OP_drop); 34229 if (js_parse_assign_expr(s)) 34230 goto fail; 34231 set_object_name(s, name); 34232 emit_op(s, OP_dup); 34233 emit_op(s, OP_put_arg); 34234 emit_u16(s, idx); 34235 emit_label(s, label); 34236 emit_op(s, OP_scope_put_var_init); 34237 emit_atom(s, name); 34238 emit_u16(s, fd->scope_level); 34239 } else { 34240 if (!has_opt_arg) { 34241 fd->defined_arg_count++; 34242 } 34243 if (fd->has_parameter_expressions) { 34244 /* copy the argument to the argument scope */ 34245 emit_op(s, OP_get_arg); 34246 emit_u16(s, idx); 34247 emit_op(s, OP_scope_put_var_init); 34248 emit_atom(s, name); 34249 emit_u16(s, fd->scope_level); 34250 } 34251 } 34252 } else { 34253 js_parse_error(s, "missing formal parameter"); 34254 goto fail; 34255 } 34256 if (rest && s->token.val != ')') { 34257 js_parse_expect(s, ')'); 34258 goto fail; 34259 } 34260 if (s->token.val == ')') 34261 break; 34262 if (js_parse_expect(s, ',')) 34263 goto fail; 34264 } 34265 if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) || 34266 (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) { 34267 js_parse_error(s, "invalid number of arguments for getter or setter"); 34268 goto fail; 34269 } 34270 } 34271 34272 if (fd->has_parameter_expressions) { 34273 int idx; 34274 34275 /* Copy the variables in the argument scope to the variable 34276 scope (see FunctionDeclarationInstantiation() in spec). The 34277 normal arguments are already present, so no need to copy 34278 them. */ 34279 idx = fd->scopes[fd->scope_level].first; 34280 while (idx >= 0) { 34281 JSVarDef *vd = &fd->vars[idx]; 34282 if (vd->scope_level != fd->scope_level) 34283 break; 34284 if (find_var(ctx, fd, vd->var_name) < 0) { 34285 if (add_var(ctx, fd, vd->var_name) < 0) 34286 goto fail; 34287 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */ 34288 emit_op(s, OP_scope_get_var); 34289 emit_atom(s, vd->var_name); 34290 emit_u16(s, fd->scope_level); 34291 emit_op(s, OP_scope_put_var); 34292 emit_atom(s, vd->var_name); 34293 emit_u16(s, 0); 34294 } 34295 idx = vd->scope_next; 34296 } 34297 34298 /* the argument scope has no parent, hence we don't use pop_scope(s) */ 34299 emit_op(s, OP_leave_scope); 34300 emit_u16(s, fd->scope_level); 34301 34302 /* set the variable scope as the current scope */ 34303 fd->scope_level = 0; 34304 fd->scope_first = fd->scopes[fd->scope_level].first; 34305 } 34306 34307 if (next_token(s)) 34308 goto fail; 34309 34310 /* generator function: yield after the parameters are evaluated */ 34311 if (func_kind == JS_FUNC_GENERATOR || 34312 func_kind == JS_FUNC_ASYNC_GENERATOR) 34313 emit_op(s, OP_initial_yield); 34314 34315 /* in generators, yield expression is forbidden during the parsing 34316 of the arguments */ 34317 fd->in_function_body = TRUE; 34318 push_scope(s); /* enter body scope */ 34319 fd->body_scope = fd->scope_level; 34320 34321 if (s->token.val == TOK_ARROW) { 34322 if (next_token(s)) 34323 goto fail; 34324 34325 if (s->token.val != '{') { 34326 if (js_parse_function_check_names(s, fd, func_name)) 34327 goto fail; 34328 34329 if (js_parse_assign_expr(s)) 34330 goto fail; 34331 34332 if (func_kind != JS_FUNC_NORMAL) 34333 emit_op(s, OP_return_async); 34334 else 34335 emit_op(s, OP_return); 34336 34337 if (!(fd->js_mode & JS_MODE_STRIP)) { 34338 /* save the function source code */ 34339 /* the end of the function source code is after the last 34340 token of the function source stored into s->last_ptr */ 34341 fd->source_len = s->last_ptr - ptr; 34342 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); 34343 if (!fd->source) 34344 goto fail; 34345 } 34346 goto done; 34347 } 34348 } 34349 34350 if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { 34351 if (js_parse_expect(s, '{')) 34352 goto fail; 34353 } 34354 34355 if (js_parse_directives(s)) 34356 goto fail; 34357 34358 /* in strict_mode, check function and argument names */ 34359 if (js_parse_function_check_names(s, fd, func_name)) 34360 goto fail; 34361 34362 while (s->token.val != '}') { 34363 if (js_parse_source_element(s)) 34364 goto fail; 34365 } 34366 if (!(fd->js_mode & JS_MODE_STRIP)) { 34367 /* save the function source code */ 34368 fd->source_len = s->buf_ptr - ptr; 34369 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len); 34370 if (!fd->source) 34371 goto fail; 34372 } 34373 34374 if (next_token(s)) { 34375 /* consume the '}' */ 34376 goto fail; 34377 } 34378 34379 /* in case there is no return, add one */ 34380 if (js_is_live_code(s)) { 34381 emit_return(s, FALSE); 34382 } 34383 done: 34384 s->cur_func = fd->parent; 34385 34386 /* Reparse identifiers after the function is terminated so that 34387 the token is parsed in the englobing function. It could be done 34388 by just using next_token() here for normal functions, but it is 34389 necessary for arrow functions with an expression body. */ 34390 reparse_ident_token(s); 34391 34392 /* create the function object */ 34393 { 34394 int idx; 34395 JSAtom func_name = fd->func_name; 34396 34397 /* the real object will be set at the end of the compilation */ 34398 idx = cpool_add(s, JS_NULL); 34399 fd->parent_cpool_idx = idx; 34400 34401 if (is_expr) { 34402 /* for constructors, no code needs to be generated here */ 34403 if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR && 34404 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) { 34405 /* OP_fclosure creates the function object from the bytecode 34406 and adds the scope information */ 34407 emit_op(s, OP_fclosure); 34408 emit_u32(s, idx); 34409 if (func_name == JS_ATOM_NULL) { 34410 emit_op(s, OP_set_name); 34411 emit_u32(s, JS_ATOM_NULL); 34412 } 34413 } 34414 } else if (func_type == JS_PARSE_FUNC_VAR) { 34415 emit_op(s, OP_fclosure); 34416 emit_u32(s, idx); 34417 if (create_func_var) { 34418 if (s->cur_func->is_global_var) { 34419 JSGlobalVar *hf; 34420 /* the global variable must be defined at the start of the 34421 function */ 34422 hf = add_global_var(ctx, s->cur_func, func_name); 34423 if (!hf) 34424 goto fail; 34425 /* it is considered as defined at the top level 34426 (needed for annex B.3.3.4 and B.3.3.5 34427 checks) */ 34428 hf->scope_level = 0; 34429 hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0); 34430 /* store directly into global var, bypass lexical scope */ 34431 emit_op(s, OP_dup); 34432 emit_op(s, OP_scope_put_var); 34433 emit_atom(s, func_name); 34434 emit_u16(s, 0); 34435 } else { 34436 /* do not call define_var to bypass lexical scope check */ 34437 func_idx = find_var(ctx, s->cur_func, func_name); 34438 if (func_idx < 0) { 34439 func_idx = add_var(ctx, s->cur_func, func_name); 34440 if (func_idx < 0) 34441 goto fail; 34442 } 34443 /* store directly into local var, bypass lexical catch scope */ 34444 emit_op(s, OP_dup); 34445 emit_op(s, OP_scope_put_var); 34446 emit_atom(s, func_name); 34447 emit_u16(s, 0); 34448 } 34449 } 34450 if (lexical_func_idx >= 0) { 34451 /* lexical variable will be initialized upon entering scope */ 34452 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx; 34453 emit_op(s, OP_drop); 34454 } else { 34455 /* store function object into its lexical name */ 34456 /* XXX: could use OP_put_loc directly */ 34457 emit_op(s, OP_scope_put_var_init); 34458 emit_atom(s, func_name); 34459 emit_u16(s, s->cur_func->scope_level); 34460 } 34461 } else { 34462 if (!s->cur_func->is_global_var) { 34463 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR); 34464 34465 if (var_idx < 0) 34466 goto fail; 34467 /* the variable will be assigned at the top of the function */ 34468 if (var_idx & ARGUMENT_VAR_OFFSET) { 34469 s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx; 34470 } else { 34471 s->cur_func->vars[var_idx].func_pool_idx = idx; 34472 } 34473 } else { 34474 JSAtom func_var_name; 34475 JSGlobalVar *hf; 34476 if (func_name == JS_ATOM_NULL) 34477 func_var_name = JS_ATOM__default_; /* export default */ 34478 else 34479 func_var_name = func_name; 34480 /* the variable will be assigned at the top of the function */ 34481 hf = add_global_var(ctx, s->cur_func, func_var_name); 34482 if (!hf) 34483 goto fail; 34484 hf->cpool_idx = idx; 34485 if (export_flag != JS_PARSE_EXPORT_NONE) { 34486 if (!add_export_entry(s, s->cur_func->module, func_var_name, 34487 export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL)) 34488 goto fail; 34489 } 34490 } 34491 } 34492 } 34493 return 0; 34494 fail: 34495 s->cur_func = fd->parent; 34496 js_free_function_def(ctx, fd); 34497 if (pfd) 34498 *pfd = NULL; 34499 return -1; 34500 } 34501 34502 static __exception int js_parse_function_decl(JSParseState *s, 34503 JSParseFunctionEnum func_type, 34504 JSFunctionKindEnum func_kind, 34505 JSAtom func_name, 34506 const uint8_t *ptr, 34507 int function_line_num) 34508 { 34509 return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr, 34510 function_line_num, JS_PARSE_EXPORT_NONE, 34511 NULL); 34512 } 34513 34514 static __exception int js_parse_program(JSParseState *s) 34515 { 34516 JSFunctionDef *fd = s->cur_func; 34517 int idx; 34518 34519 if (next_token(s)) 34520 return -1; 34521 34522 if (js_parse_directives(s)) 34523 return -1; 34524 34525 fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) || 34526 (fd->eval_type == JS_EVAL_TYPE_MODULE) || 34527 !(fd->js_mode & JS_MODE_STRICT); 34528 34529 if (!s->is_module) { 34530 /* hidden variable for the return value */ 34531 fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_); 34532 if (idx < 0) 34533 return -1; 34534 } 34535 34536 while (s->token.val != TOK_EOF) { 34537 if (js_parse_source_element(s)) 34538 return -1; 34539 } 34540 34541 if (!s->is_module) { 34542 /* return the value of the hidden variable eval_ret_idx */ 34543 if (fd->func_kind == JS_FUNC_ASYNC) { 34544 /* wrap the return value in an object so that promises can 34545 be safely returned */ 34546 emit_op(s, OP_object); 34547 emit_op(s, OP_dup); 34548 34549 emit_op(s, OP_get_loc); 34550 emit_u16(s, fd->eval_ret_idx); 34551 34552 emit_op(s, OP_put_field); 34553 emit_atom(s, JS_ATOM_value); 34554 } else { 34555 emit_op(s, OP_get_loc); 34556 emit_u16(s, fd->eval_ret_idx); 34557 } 34558 emit_return(s, TRUE); 34559 } else { 34560 emit_return(s, FALSE); 34561 } 34562 34563 return 0; 34564 } 34565 34566 static void js_parse_init(JSContext *ctx, JSParseState *s, 34567 const char *input, size_t input_len, 34568 const char *filename) 34569 { 34570 memset(s, 0, sizeof(*s)); 34571 s->ctx = ctx; 34572 s->filename = filename; 34573 s->line_num = 1; 34574 s->buf_ptr = (const uint8_t *)input; 34575 s->buf_end = s->buf_ptr + input_len; 34576 s->token.val = ' '; 34577 s->token.line_num = 1; 34578 } 34579 34580 static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, 34581 JSValueConst this_obj, 34582 JSVarRef **var_refs, JSStackFrame *sf) 34583 { 34584 JSValue ret_val; 34585 uint32_t tag; 34586 34587 tag = JS_VALUE_GET_TAG(fun_obj); 34588 if (tag == JS_TAG_FUNCTION_BYTECODE) { 34589 fun_obj = js_closure(ctx, fun_obj, var_refs, sf); 34590 ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL); 34591 } else if (tag == JS_TAG_MODULE) { 34592 JSModuleDef *m; 34593 m = JS_VALUE_GET_PTR(fun_obj); 34594 /* the module refcount should be >= 2 */ 34595 JS_FreeValue(ctx, fun_obj); 34596 if (js_create_module_function(ctx, m) < 0) 34597 goto fail; 34598 if (js_link_module(ctx, m) < 0) 34599 goto fail; 34600 ret_val = js_evaluate_module(ctx, m); 34601 if (JS_IsException(ret_val)) { 34602 fail: 34603 return JS_EXCEPTION; 34604 } 34605 } else { 34606 JS_FreeValue(ctx, fun_obj); 34607 ret_val = JS_ThrowTypeError(ctx, "bytecode function expected"); 34608 } 34609 return ret_val; 34610 } 34611 34612 JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) 34613 { 34614 return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); 34615 } 34616 34617 /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ 34618 static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 34619 const char *input, size_t input_len, 34620 const char *filename, int flags, int scope_idx) 34621 { 34622 JSParseState s1, *s = &s1; 34623 int err, js_mode, eval_type; 34624 JSValue fun_obj, ret_val; 34625 JSStackFrame *sf; 34626 JSVarRef **var_refs; 34627 JSFunctionBytecode *b; 34628 JSFunctionDef *fd; 34629 JSModuleDef *m; 34630 34631 js_parse_init(ctx, s, input, input_len, filename); 34632 skip_shebang(&s->buf_ptr, s->buf_end); 34633 34634 eval_type = flags & JS_EVAL_TYPE_MASK; 34635 m = NULL; 34636 if (eval_type == JS_EVAL_TYPE_DIRECT) { 34637 JSObject *p; 34638 sf = ctx->rt->current_stack_frame; 34639 assert(sf != NULL); 34640 assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT); 34641 p = JS_VALUE_GET_OBJ(sf->cur_func); 34642 assert(js_class_has_bytecode(p->class_id)); 34643 b = p->u.func.function_bytecode; 34644 var_refs = p->u.func.var_refs; 34645 js_mode = b->js_mode; 34646 } else { 34647 sf = NULL; 34648 b = NULL; 34649 var_refs = NULL; 34650 js_mode = 0; 34651 if (flags & JS_EVAL_FLAG_STRICT) 34652 js_mode |= JS_MODE_STRICT; 34653 if (flags & JS_EVAL_FLAG_STRIP) 34654 js_mode |= JS_MODE_STRIP; 34655 if (eval_type == JS_EVAL_TYPE_MODULE) { 34656 JSAtom module_name = JS_NewAtom(ctx, filename); 34657 if (module_name == JS_ATOM_NULL) 34658 return JS_EXCEPTION; 34659 m = js_new_module_def(ctx, module_name); 34660 if (!m) 34661 return JS_EXCEPTION; 34662 js_mode |= JS_MODE_STRICT; 34663 } 34664 } 34665 fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1); 34666 if (!fd) { 34667 goto fail1; 34668 } 34669 s->cur_func = fd; 34670 fd->eval_type = eval_type; 34671 fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT); 34672 fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0); 34673 if (eval_type == JS_EVAL_TYPE_DIRECT) { 34674 fd->new_target_allowed = b->new_target_allowed; 34675 fd->super_call_allowed = b->super_call_allowed; 34676 fd->super_allowed = b->super_allowed; 34677 fd->arguments_allowed = b->arguments_allowed; 34678 } else { 34679 fd->new_target_allowed = FALSE; 34680 fd->super_call_allowed = FALSE; 34681 fd->super_allowed = FALSE; 34682 fd->arguments_allowed = TRUE; 34683 } 34684 fd->js_mode = js_mode; 34685 fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_); 34686 if (b) { 34687 if (add_closure_variables(ctx, fd, b, scope_idx)) { 34688 goto fail; 34689 } 34690 } 34691 fd->module = m; 34692 if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) { 34693 fd->in_function_body = TRUE; 34694 fd->func_kind = JS_FUNC_ASYNC; 34695 } 34696 s->is_module = (m != NULL); 34697 s->allow_html_comments = !s->is_module; 34698 34699 push_scope(s); /* body scope */ 34700 fd->body_scope = fd->scope_level; 34701 34702 err = js_parse_program(s); 34703 if (err) { 34704 fail: 34705 free_token(s, &s->token); 34706 js_free_function_def(ctx, fd); 34707 goto fail1; 34708 } 34709 34710 if (m != NULL) 34711 m->has_tla = fd->has_await; 34712 34713 /* create the function object and all the enclosed functions */ 34714 fun_obj = js_create_function(ctx, fd); 34715 if (JS_IsException(fun_obj)) 34716 goto fail1; 34717 /* Could add a flag to avoid resolution if necessary */ 34718 if (m) { 34719 m->func_obj = fun_obj; 34720 if (js_resolve_module(ctx, m) < 0) 34721 goto fail1; 34722 fun_obj = JS_NewModuleValue(ctx, m); 34723 } 34724 if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { 34725 ret_val = fun_obj; 34726 } else { 34727 ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf); 34728 } 34729 return ret_val; 34730 fail1: 34731 /* XXX: should free all the unresolved dependencies */ 34732 if (m) 34733 js_free_module_def(ctx, m); 34734 return JS_EXCEPTION; 34735 } 34736 34737 /* the indirection is needed to make 'eval' optional */ 34738 static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, 34739 const char *input, size_t input_len, 34740 const char *filename, int flags, int scope_idx) 34741 { 34742 if (unlikely(!ctx->eval_internal)) { 34743 return JS_ThrowTypeError(ctx, "eval is not supported"); 34744 } 34745 return ctx->eval_internal(ctx, this_obj, input, input_len, filename, 34746 flags, scope_idx); 34747 } 34748 34749 static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, 34750 JSValueConst val, int flags, int scope_idx) 34751 { 34752 JSValue ret; 34753 const char *str; 34754 size_t len; 34755 34756 if (!JS_IsString(val)) 34757 return JS_DupValue(ctx, val); 34758 str = JS_ToCStringLen(ctx, &len, val); 34759 if (!str) 34760 return JS_EXCEPTION; 34761 ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx); 34762 JS_FreeCString(ctx, str); 34763 return ret; 34764 34765 } 34766 34767 JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, 34768 const char *input, size_t input_len, 34769 const char *filename, int eval_flags) 34770 { 34771 int eval_type = eval_flags & JS_EVAL_TYPE_MASK; 34772 JSValue ret; 34773 34774 assert(eval_type == JS_EVAL_TYPE_GLOBAL || 34775 eval_type == JS_EVAL_TYPE_MODULE); 34776 ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, 34777 eval_flags, -1); 34778 return ret; 34779 } 34780 34781 JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, 34782 const char *filename, int eval_flags) 34783 { 34784 return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 34785 eval_flags); 34786 } 34787 34788 int JS_ResolveModule(JSContext *ctx, JSValueConst obj) 34789 { 34790 if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { 34791 JSModuleDef *m = JS_VALUE_GET_PTR(obj); 34792 if (js_resolve_module(ctx, m) < 0) { 34793 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); 34794 return -1; 34795 } 34796 } 34797 return 0; 34798 } 34799 34800 /*******************************************************************/ 34801 /* object list */ 34802 34803 typedef struct { 34804 JSObject *obj; 34805 uint32_t hash_next; /* -1 if no next entry */ 34806 } JSObjectListEntry; 34807 34808 /* XXX: reuse it to optimize weak references */ 34809 typedef struct { 34810 JSObjectListEntry *object_tab; 34811 int object_count; 34812 int object_size; 34813 uint32_t *hash_table; 34814 uint32_t hash_size; 34815 } JSObjectList; 34816 34817 static void js_object_list_init(JSObjectList *s) 34818 { 34819 memset(s, 0, sizeof(*s)); 34820 } 34821 34822 static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size) 34823 { 34824 return ((uintptr_t)p * 3163) & (hash_size - 1); 34825 } 34826 34827 static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s, 34828 uint32_t new_hash_size) 34829 { 34830 JSObjectListEntry *e; 34831 uint32_t i, h, *new_hash_table; 34832 34833 new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size); 34834 if (!new_hash_table) 34835 return -1; 34836 js_free(ctx, s->hash_table); 34837 s->hash_table = new_hash_table; 34838 s->hash_size = new_hash_size; 34839 34840 for(i = 0; i < s->hash_size; i++) { 34841 s->hash_table[i] = -1; 34842 } 34843 for(i = 0; i < s->object_count; i++) { 34844 e = &s->object_tab[i]; 34845 h = js_object_list_get_hash(e->obj, s->hash_size); 34846 e->hash_next = s->hash_table[h]; 34847 s->hash_table[h] = i; 34848 } 34849 return 0; 34850 } 34851 34852 /* the reference count of 'obj' is not modified. Return 0 if OK, -1 if 34853 memory error */ 34854 static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj) 34855 { 34856 JSObjectListEntry *e; 34857 uint32_t h, new_hash_size; 34858 34859 if (js_resize_array(ctx, (void *)&s->object_tab, 34860 sizeof(s->object_tab[0]), 34861 &s->object_size, s->object_count + 1)) 34862 return -1; 34863 if (unlikely((s->object_count + 1) >= s->hash_size)) { 34864 new_hash_size = max_uint32(s->hash_size, 4); 34865 while (new_hash_size <= s->object_count) 34866 new_hash_size *= 2; 34867 if (js_object_list_resize_hash(ctx, s, new_hash_size)) 34868 return -1; 34869 } 34870 e = &s->object_tab[s->object_count++]; 34871 h = js_object_list_get_hash(obj, s->hash_size); 34872 e->obj = obj; 34873 e->hash_next = s->hash_table[h]; 34874 s->hash_table[h] = s->object_count - 1; 34875 return 0; 34876 } 34877 34878 /* return -1 if not present or the object index */ 34879 static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj) 34880 { 34881 JSObjectListEntry *e; 34882 uint32_t h, p; 34883 34884 /* must test empty size because there is no hash table */ 34885 if (s->object_count == 0) 34886 return -1; 34887 h = js_object_list_get_hash(obj, s->hash_size); 34888 p = s->hash_table[h]; 34889 while (p != -1) { 34890 e = &s->object_tab[p]; 34891 if (e->obj == obj) 34892 return p; 34893 p = e->hash_next; 34894 } 34895 return -1; 34896 } 34897 34898 static void js_object_list_end(JSContext *ctx, JSObjectList *s) 34899 { 34900 js_free(ctx, s->object_tab); 34901 js_free(ctx, s->hash_table); 34902 } 34903 34904 /*******************************************************************/ 34905 /* binary object writer & reader */ 34906 34907 typedef enum BCTagEnum { 34908 BC_TAG_NULL = 1, 34909 BC_TAG_UNDEFINED, 34910 BC_TAG_BOOL_FALSE, 34911 BC_TAG_BOOL_TRUE, 34912 BC_TAG_INT32, 34913 BC_TAG_FLOAT64, 34914 BC_TAG_STRING, 34915 BC_TAG_OBJECT, 34916 BC_TAG_ARRAY, 34917 BC_TAG_BIG_INT, 34918 BC_TAG_TEMPLATE_OBJECT, 34919 BC_TAG_FUNCTION_BYTECODE, 34920 BC_TAG_MODULE, 34921 BC_TAG_TYPED_ARRAY, 34922 BC_TAG_ARRAY_BUFFER, 34923 BC_TAG_SHARED_ARRAY_BUFFER, 34924 BC_TAG_DATE, 34925 BC_TAG_OBJECT_VALUE, 34926 BC_TAG_OBJECT_REFERENCE, 34927 #ifdef CONFIG_BIGNUM 34928 BC_TAG_BIG_FLOAT, 34929 BC_TAG_BIG_DECIMAL, 34930 #endif 34931 } BCTagEnum; 34932 34933 #ifdef CONFIG_BIGNUM 34934 #define BC_VERSION 0x43 34935 #else 34936 #define BC_VERSION 3 34937 #endif 34938 34939 typedef struct BCWriterState { 34940 JSContext *ctx; 34941 DynBuf dbuf; 34942 BOOL allow_bytecode : 8; 34943 BOOL allow_sab : 8; 34944 BOOL allow_reference : 8; 34945 uint32_t first_atom; 34946 uint32_t *atom_to_idx; 34947 int atom_to_idx_size; 34948 JSAtom *idx_to_atom; 34949 int idx_to_atom_count; 34950 int idx_to_atom_size; 34951 uint8_t **sab_tab; 34952 int sab_tab_len; 34953 int sab_tab_size; 34954 /* list of referenced objects (used if allow_reference = TRUE) */ 34955 JSObjectList object_list; 34956 } BCWriterState; 34957 34958 #ifdef DUMP_READ_OBJECT 34959 static const char * const bc_tag_str[] = { 34960 "invalid", 34961 "null", 34962 "undefined", 34963 "false", 34964 "true", 34965 "int32", 34966 "float64", 34967 "string", 34968 "object", 34969 "array", 34970 "bigint", 34971 "template", 34972 "function", 34973 "module", 34974 "TypedArray", 34975 "ArrayBuffer", 34976 "SharedArrayBuffer", 34977 "Date", 34978 "ObjectValue", 34979 "ObjectReference", 34980 #ifdef CONFIG_BIGNUM 34981 "bigfloat", 34982 "bigdecimal", 34983 #endif 34984 }; 34985 #endif 34986 34987 static inline BOOL is_be(void) 34988 { 34989 union { 34990 uint16_t a; 34991 uint8_t b; 34992 } u = {0x100}; 34993 return u.b; 34994 } 34995 34996 static void bc_put_u8(BCWriterState *s, uint8_t v) 34997 { 34998 dbuf_putc(&s->dbuf, v); 34999 } 35000 35001 static void bc_put_u16(BCWriterState *s, uint16_t v) 35002 { 35003 if (is_be()) 35004 v = bswap16(v); 35005 dbuf_put_u16(&s->dbuf, v); 35006 } 35007 35008 static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) 35009 { 35010 if (is_be()) 35011 v = bswap32(v); 35012 dbuf_put_u32(&s->dbuf, v); 35013 } 35014 35015 static void bc_put_u64(BCWriterState *s, uint64_t v) 35016 { 35017 if (is_be()) 35018 v = bswap64(v); 35019 dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); 35020 } 35021 35022 static void bc_put_leb128(BCWriterState *s, uint32_t v) 35023 { 35024 dbuf_put_leb128(&s->dbuf, v); 35025 } 35026 35027 static void bc_put_sleb128(BCWriterState *s, int32_t v) 35028 { 35029 dbuf_put_sleb128(&s->dbuf, v); 35030 } 35031 35032 static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n) 35033 { 35034 *pflags = *pflags | (val << *pidx); 35035 *pidx += n; 35036 } 35037 35038 static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) 35039 { 35040 uint32_t v; 35041 35042 if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { 35043 *pres = atom; 35044 return 0; 35045 } 35046 atom -= s->first_atom; 35047 if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) { 35048 *pres = s->atom_to_idx[atom]; 35049 return 0; 35050 } 35051 if (atom >= s->atom_to_idx_size) { 35052 int old_size, i; 35053 old_size = s->atom_to_idx_size; 35054 if (js_resize_array(s->ctx, (void **)&s->atom_to_idx, 35055 sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size, 35056 atom + 1)) 35057 return -1; 35058 /* XXX: could add a specific js_resize_array() function to do it */ 35059 for(i = old_size; i < s->atom_to_idx_size; i++) 35060 s->atom_to_idx[i] = 0; 35061 } 35062 if (js_resize_array(s->ctx, (void **)&s->idx_to_atom, 35063 sizeof(s->idx_to_atom[0]), 35064 &s->idx_to_atom_size, s->idx_to_atom_count + 1)) 35065 goto fail; 35066 35067 v = s->idx_to_atom_count++; 35068 s->idx_to_atom[v] = atom + s->first_atom; 35069 v += s->first_atom; 35070 s->atom_to_idx[atom] = v; 35071 *pres = v; 35072 return 0; 35073 fail: 35074 *pres = 0; 35075 return -1; 35076 } 35077 35078 static int bc_put_atom(BCWriterState *s, JSAtom atom) 35079 { 35080 uint32_t v; 35081 35082 if (__JS_AtomIsTaggedInt(atom)) { 35083 v = (__JS_AtomToUInt32(atom) << 1) | 1; 35084 } else { 35085 if (bc_atom_to_idx(s, &v, atom)) 35086 return -1; 35087 v <<= 1; 35088 } 35089 bc_put_leb128(s, v); 35090 return 0; 35091 } 35092 35093 static void bc_byte_swap(uint8_t *bc_buf, int bc_len) 35094 { 35095 int pos, len, op, fmt; 35096 35097 pos = 0; 35098 while (pos < bc_len) { 35099 op = bc_buf[pos]; 35100 len = short_opcode_info(op).size; 35101 fmt = short_opcode_info(op).fmt; 35102 switch(fmt) { 35103 case OP_FMT_u16: 35104 case OP_FMT_i16: 35105 case OP_FMT_label16: 35106 case OP_FMT_npop: 35107 case OP_FMT_loc: 35108 case OP_FMT_arg: 35109 case OP_FMT_var_ref: 35110 put_u16(bc_buf + pos + 1, 35111 bswap16(get_u16(bc_buf + pos + 1))); 35112 break; 35113 case OP_FMT_i32: 35114 case OP_FMT_u32: 35115 case OP_FMT_const: 35116 case OP_FMT_label: 35117 case OP_FMT_atom: 35118 case OP_FMT_atom_u8: 35119 put_u32(bc_buf + pos + 1, 35120 bswap32(get_u32(bc_buf + pos + 1))); 35121 break; 35122 case OP_FMT_atom_u16: 35123 case OP_FMT_label_u16: 35124 put_u32(bc_buf + pos + 1, 35125 bswap32(get_u32(bc_buf + pos + 1))); 35126 put_u16(bc_buf + pos + 1 + 4, 35127 bswap16(get_u16(bc_buf + pos + 1 + 4))); 35128 break; 35129 case OP_FMT_atom_label_u8: 35130 case OP_FMT_atom_label_u16: 35131 put_u32(bc_buf + pos + 1, 35132 bswap32(get_u32(bc_buf + pos + 1))); 35133 put_u32(bc_buf + pos + 1 + 4, 35134 bswap32(get_u32(bc_buf + pos + 1 + 4))); 35135 if (fmt == OP_FMT_atom_label_u16) { 35136 put_u16(bc_buf + pos + 1 + 4 + 4, 35137 bswap16(get_u16(bc_buf + pos + 1 + 4 + 4))); 35138 } 35139 break; 35140 case OP_FMT_npop_u16: 35141 put_u16(bc_buf + pos + 1, 35142 bswap16(get_u16(bc_buf + pos + 1))); 35143 put_u16(bc_buf + pos + 1 + 2, 35144 bswap16(get_u16(bc_buf + pos + 1 + 2))); 35145 break; 35146 default: 35147 break; 35148 } 35149 pos += len; 35150 } 35151 } 35152 35153 static int JS_WriteFunctionBytecode(BCWriterState *s, 35154 const uint8_t *bc_buf1, int bc_len) 35155 { 35156 int pos, len, op; 35157 JSAtom atom; 35158 uint8_t *bc_buf; 35159 uint32_t val; 35160 35161 bc_buf = js_malloc(s->ctx, bc_len); 35162 if (!bc_buf) 35163 return -1; 35164 memcpy(bc_buf, bc_buf1, bc_len); 35165 35166 pos = 0; 35167 while (pos < bc_len) { 35168 op = bc_buf[pos]; 35169 len = short_opcode_info(op).size; 35170 switch(short_opcode_info(op).fmt) { 35171 case OP_FMT_atom: 35172 case OP_FMT_atom_u8: 35173 case OP_FMT_atom_u16: 35174 case OP_FMT_atom_label_u8: 35175 case OP_FMT_atom_label_u16: 35176 atom = get_u32(bc_buf + pos + 1); 35177 if (bc_atom_to_idx(s, &val, atom)) 35178 goto fail; 35179 put_u32(bc_buf + pos + 1, val); 35180 break; 35181 default: 35182 break; 35183 } 35184 pos += len; 35185 } 35186 35187 if (is_be()) 35188 bc_byte_swap(bc_buf, bc_len); 35189 35190 dbuf_put(&s->dbuf, bc_buf, bc_len); 35191 35192 js_free(s->ctx, bc_buf); 35193 return 0; 35194 fail: 35195 js_free(s->ctx, bc_buf); 35196 return -1; 35197 } 35198 35199 static void JS_WriteString(BCWriterState *s, JSString *p) 35200 { 35201 int i; 35202 bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); 35203 if (p->is_wide_char) { 35204 for(i = 0; i < p->len; i++) 35205 bc_put_u16(s, p->u.str16[i]); 35206 } else { 35207 dbuf_put(&s->dbuf, p->u.str8, p->len); 35208 } 35209 } 35210 35211 static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) 35212 { 35213 uint32_t tag, tag1; 35214 int64_t e; 35215 JSBigFloat *bf = JS_VALUE_GET_PTR(obj); 35216 bf_t *a = &bf->num; 35217 size_t len, i, n1, j; 35218 limb_t v; 35219 35220 tag = JS_VALUE_GET_TAG(obj); 35221 switch(tag) { 35222 case JS_TAG_BIG_INT: 35223 tag1 = BC_TAG_BIG_INT; 35224 break; 35225 #ifdef CONFIG_BIGNUM 35226 case JS_TAG_BIG_FLOAT: 35227 tag1 = BC_TAG_BIG_FLOAT; 35228 break; 35229 case JS_TAG_BIG_DECIMAL: 35230 tag1 = BC_TAG_BIG_DECIMAL; 35231 break; 35232 #endif 35233 default: 35234 abort(); 35235 } 35236 bc_put_u8(s, tag1); 35237 35238 /* sign + exponent */ 35239 if (a->expn == BF_EXP_ZERO) 35240 e = 0; 35241 else if (a->expn == BF_EXP_INF) 35242 e = 1; 35243 else if (a->expn == BF_EXP_NAN) 35244 e = 2; 35245 else if (a->expn >= 0) 35246 e = a->expn + 3; 35247 else 35248 e = a->expn; 35249 e = (e * 2) | a->sign; 35250 if (e < INT32_MIN || e > INT32_MAX) { 35251 JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); 35252 return -1; 35253 } 35254 bc_put_sleb128(s, e); 35255 35256 /* mantissa */ 35257 if (a->len != 0) { 35258 if (tag != JS_TAG_BIG_DECIMAL) { 35259 i = 0; 35260 while (i < a->len && a->tab[i] == 0) 35261 i++; 35262 assert(i < a->len); 35263 v = a->tab[i]; 35264 n1 = sizeof(limb_t); 35265 while ((v & 0xff) == 0) { 35266 n1--; 35267 v >>= 8; 35268 } 35269 i++; 35270 len = (a->len - i) * sizeof(limb_t) + n1; 35271 if (len > INT32_MAX) { 35272 JS_ThrowInternalError(s->ctx, "bignum is too large"); 35273 return -1; 35274 } 35275 bc_put_leb128(s, len); 35276 /* always saved in byte based little endian representation */ 35277 for(j = 0; j < n1; j++) { 35278 bc_put_u8(s, v >> (j * 8)); 35279 } 35280 for(; i < a->len; i++) { 35281 limb_t v = a->tab[i]; 35282 #if LIMB_BITS == 32 35283 bc_put_u32(s, v); 35284 #else 35285 bc_put_u64(s, v); 35286 #endif 35287 } 35288 } else { 35289 int bpos, d; 35290 uint8_t v8; 35291 size_t i0; 35292 35293 /* little endian BCD */ 35294 i = 0; 35295 while (i < a->len && a->tab[i] == 0) 35296 i++; 35297 assert(i < a->len); 35298 len = a->len * LIMB_DIGITS; 35299 v = a->tab[i]; 35300 j = 0; 35301 while ((v % 10) == 0) { 35302 j++; 35303 v /= 10; 35304 } 35305 len -= j; 35306 assert(len > 0); 35307 if (len > INT32_MAX) { 35308 JS_ThrowInternalError(s->ctx, "bignum is too large"); 35309 return -1; 35310 } 35311 bc_put_leb128(s, len); 35312 35313 bpos = 0; 35314 v8 = 0; 35315 i0 = i; 35316 for(; i < a->len; i++) { 35317 if (i != i0) { 35318 v = a->tab[i]; 35319 j = 0; 35320 } 35321 for(; j < LIMB_DIGITS; j++) { 35322 d = v % 10; 35323 v /= 10; 35324 if (bpos == 0) { 35325 v8 = d; 35326 bpos = 1; 35327 } else { 35328 bc_put_u8(s, v8 | (d << 4)); 35329 bpos = 0; 35330 } 35331 } 35332 } 35333 /* flush the last digit */ 35334 if (bpos) { 35335 bc_put_u8(s, v8); 35336 } 35337 } 35338 } 35339 return 0; 35340 } 35341 35342 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); 35343 35344 static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) 35345 { 35346 JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj); 35347 uint32_t flags; 35348 int idx, i; 35349 35350 bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE); 35351 flags = idx = 0; 35352 bc_set_flags(&flags, &idx, b->has_prototype, 1); 35353 bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1); 35354 bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1); 35355 bc_set_flags(&flags, &idx, b->need_home_object, 1); 35356 bc_set_flags(&flags, &idx, b->func_kind, 2); 35357 bc_set_flags(&flags, &idx, b->new_target_allowed, 1); 35358 bc_set_flags(&flags, &idx, b->super_call_allowed, 1); 35359 bc_set_flags(&flags, &idx, b->super_allowed, 1); 35360 bc_set_flags(&flags, &idx, b->arguments_allowed, 1); 35361 bc_set_flags(&flags, &idx, b->has_debug, 1); 35362 bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); 35363 bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1); 35364 assert(idx <= 16); 35365 bc_put_u16(s, flags); 35366 bc_put_u8(s, b->js_mode); 35367 bc_put_atom(s, b->func_name); 35368 35369 bc_put_leb128(s, b->arg_count); 35370 bc_put_leb128(s, b->var_count); 35371 bc_put_leb128(s, b->defined_arg_count); 35372 bc_put_leb128(s, b->stack_size); 35373 bc_put_leb128(s, b->closure_var_count); 35374 bc_put_leb128(s, b->cpool_count); 35375 bc_put_leb128(s, b->byte_code_len); 35376 if (b->vardefs) { 35377 /* XXX: this field is redundant */ 35378 bc_put_leb128(s, b->arg_count + b->var_count); 35379 for(i = 0; i < b->arg_count + b->var_count; i++) { 35380 JSVarDef *vd = &b->vardefs[i]; 35381 bc_put_atom(s, vd->var_name); 35382 bc_put_leb128(s, vd->scope_level); 35383 bc_put_leb128(s, vd->scope_next + 1); 35384 flags = idx = 0; 35385 bc_set_flags(&flags, &idx, vd->var_kind, 4); 35386 bc_set_flags(&flags, &idx, vd->is_const, 1); 35387 bc_set_flags(&flags, &idx, vd->is_lexical, 1); 35388 bc_set_flags(&flags, &idx, vd->is_captured, 1); 35389 assert(idx <= 8); 35390 bc_put_u8(s, flags); 35391 } 35392 } else { 35393 bc_put_leb128(s, 0); 35394 } 35395 35396 for(i = 0; i < b->closure_var_count; i++) { 35397 JSClosureVar *cv = &b->closure_var[i]; 35398 bc_put_atom(s, cv->var_name); 35399 bc_put_leb128(s, cv->var_idx); 35400 flags = idx = 0; 35401 bc_set_flags(&flags, &idx, cv->is_local, 1); 35402 bc_set_flags(&flags, &idx, cv->is_arg, 1); 35403 bc_set_flags(&flags, &idx, cv->is_const, 1); 35404 bc_set_flags(&flags, &idx, cv->is_lexical, 1); 35405 bc_set_flags(&flags, &idx, cv->var_kind, 4); 35406 assert(idx <= 8); 35407 bc_put_u8(s, flags); 35408 } 35409 35410 if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len)) 35411 goto fail; 35412 35413 if (b->has_debug) { 35414 bc_put_atom(s, b->debug.filename); 35415 bc_put_leb128(s, b->debug.line_num); 35416 bc_put_leb128(s, b->debug.pc2line_len); 35417 dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len); 35418 } 35419 35420 for(i = 0; i < b->cpool_count; i++) { 35421 if (JS_WriteObjectRec(s, b->cpool[i])) 35422 goto fail; 35423 } 35424 return 0; 35425 fail: 35426 return -1; 35427 } 35428 35429 static int JS_WriteModule(BCWriterState *s, JSValueConst obj) 35430 { 35431 JSModuleDef *m = JS_VALUE_GET_PTR(obj); 35432 int i; 35433 35434 bc_put_u8(s, BC_TAG_MODULE); 35435 bc_put_atom(s, m->module_name); 35436 35437 bc_put_leb128(s, m->req_module_entries_count); 35438 for(i = 0; i < m->req_module_entries_count; i++) { 35439 JSReqModuleEntry *rme = &m->req_module_entries[i]; 35440 bc_put_atom(s, rme->module_name); 35441 } 35442 35443 bc_put_leb128(s, m->export_entries_count); 35444 for(i = 0; i < m->export_entries_count; i++) { 35445 JSExportEntry *me = &m->export_entries[i]; 35446 bc_put_u8(s, me->export_type); 35447 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 35448 bc_put_leb128(s, me->u.local.var_idx); 35449 } else { 35450 bc_put_leb128(s, me->u.req_module_idx); 35451 bc_put_atom(s, me->local_name); 35452 } 35453 bc_put_atom(s, me->export_name); 35454 } 35455 35456 bc_put_leb128(s, m->star_export_entries_count); 35457 for(i = 0; i < m->star_export_entries_count; i++) { 35458 JSStarExportEntry *se = &m->star_export_entries[i]; 35459 bc_put_leb128(s, se->req_module_idx); 35460 } 35461 35462 bc_put_leb128(s, m->import_entries_count); 35463 for(i = 0; i < m->import_entries_count; i++) { 35464 JSImportEntry *mi = &m->import_entries[i]; 35465 bc_put_leb128(s, mi->var_idx); 35466 bc_put_atom(s, mi->import_name); 35467 bc_put_leb128(s, mi->req_module_idx); 35468 } 35469 35470 bc_put_u8(s, m->has_tla); 35471 35472 if (JS_WriteObjectRec(s, m->func_obj)) 35473 goto fail; 35474 return 0; 35475 fail: 35476 return -1; 35477 } 35478 35479 static int JS_WriteArray(BCWriterState *s, JSValueConst obj) 35480 { 35481 JSObject *p = JS_VALUE_GET_OBJ(obj); 35482 uint32_t i, len; 35483 JSValue val; 35484 int ret; 35485 BOOL is_template; 35486 35487 if (s->allow_bytecode && !p->extensible) { 35488 /* not extensible array: we consider it is a 35489 template when we are saving bytecode */ 35490 bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT); 35491 is_template = TRUE; 35492 } else { 35493 bc_put_u8(s, BC_TAG_ARRAY); 35494 is_template = FALSE; 35495 } 35496 if (js_get_length32(s->ctx, &len, obj)) 35497 goto fail1; 35498 bc_put_leb128(s, len); 35499 for(i = 0; i < len; i++) { 35500 val = JS_GetPropertyUint32(s->ctx, obj, i); 35501 if (JS_IsException(val)) 35502 goto fail1; 35503 ret = JS_WriteObjectRec(s, val); 35504 JS_FreeValue(s->ctx, val); 35505 if (ret) 35506 goto fail1; 35507 } 35508 if (is_template) { 35509 val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw); 35510 if (JS_IsException(val)) 35511 goto fail1; 35512 ret = JS_WriteObjectRec(s, val); 35513 JS_FreeValue(s->ctx, val); 35514 if (ret) 35515 goto fail1; 35516 } 35517 return 0; 35518 fail1: 35519 return -1; 35520 } 35521 35522 static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj) 35523 { 35524 JSObject *p = JS_VALUE_GET_OBJ(obj); 35525 uint32_t i, prop_count; 35526 JSShape *sh; 35527 JSShapeProperty *pr; 35528 int pass; 35529 JSAtom atom; 35530 35531 bc_put_u8(s, BC_TAG_OBJECT); 35532 prop_count = 0; 35533 sh = p->shape; 35534 for(pass = 0; pass < 2; pass++) { 35535 if (pass == 1) 35536 bc_put_leb128(s, prop_count); 35537 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { 35538 atom = pr->atom; 35539 if (atom != JS_ATOM_NULL && 35540 JS_AtomIsString(s->ctx, atom) && 35541 (pr->flags & JS_PROP_ENUMERABLE)) { 35542 if (pr->flags & JS_PROP_TMASK) { 35543 JS_ThrowTypeError(s->ctx, "only value properties are supported"); 35544 goto fail; 35545 } 35546 if (pass == 0) { 35547 prop_count++; 35548 } else { 35549 bc_put_atom(s, atom); 35550 if (JS_WriteObjectRec(s, p->prop[i].u.value)) 35551 goto fail; 35552 } 35553 } 35554 } 35555 } 35556 return 0; 35557 fail: 35558 return -1; 35559 } 35560 35561 static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj) 35562 { 35563 JSObject *p = JS_VALUE_GET_OBJ(obj); 35564 JSTypedArray *ta = p->u.typed_array; 35565 35566 bc_put_u8(s, BC_TAG_TYPED_ARRAY); 35567 bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY); 35568 bc_put_leb128(s, p->u.array.count); 35569 bc_put_leb128(s, ta->offset); 35570 if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) 35571 return -1; 35572 return 0; 35573 } 35574 35575 static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj) 35576 { 35577 JSObject *p = JS_VALUE_GET_OBJ(obj); 35578 JSArrayBuffer *abuf = p->u.array_buffer; 35579 if (abuf->detached) { 35580 JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx); 35581 return -1; 35582 } 35583 bc_put_u8(s, BC_TAG_ARRAY_BUFFER); 35584 bc_put_leb128(s, abuf->byte_length); 35585 dbuf_put(&s->dbuf, abuf->data, abuf->byte_length); 35586 return 0; 35587 } 35588 35589 static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj) 35590 { 35591 JSObject *p = JS_VALUE_GET_OBJ(obj); 35592 JSArrayBuffer *abuf = p->u.array_buffer; 35593 assert(!abuf->detached); /* SharedArrayBuffer are never detached */ 35594 bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER); 35595 bc_put_leb128(s, abuf->byte_length); 35596 bc_put_u64(s, (uintptr_t)abuf->data); 35597 if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]), 35598 &s->sab_tab_size, s->sab_tab_len + 1)) 35599 return -1; 35600 /* keep the SAB pointer so that the user can clone it or free it */ 35601 s->sab_tab[s->sab_tab_len++] = abuf->data; 35602 return 0; 35603 } 35604 35605 static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) 35606 { 35607 uint32_t tag; 35608 35609 if (js_check_stack_overflow(s->ctx->rt, 0)) { 35610 JS_ThrowStackOverflow(s->ctx); 35611 return -1; 35612 } 35613 35614 tag = JS_VALUE_GET_NORM_TAG(obj); 35615 switch(tag) { 35616 case JS_TAG_NULL: 35617 bc_put_u8(s, BC_TAG_NULL); 35618 break; 35619 case JS_TAG_UNDEFINED: 35620 bc_put_u8(s, BC_TAG_UNDEFINED); 35621 break; 35622 case JS_TAG_BOOL: 35623 bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj)); 35624 break; 35625 case JS_TAG_INT: 35626 bc_put_u8(s, BC_TAG_INT32); 35627 bc_put_sleb128(s, JS_VALUE_GET_INT(obj)); 35628 break; 35629 case JS_TAG_FLOAT64: 35630 { 35631 JSFloat64Union u; 35632 bc_put_u8(s, BC_TAG_FLOAT64); 35633 u.d = JS_VALUE_GET_FLOAT64(obj); 35634 bc_put_u64(s, u.u64); 35635 } 35636 break; 35637 case JS_TAG_STRING: 35638 { 35639 JSString *p = JS_VALUE_GET_STRING(obj); 35640 bc_put_u8(s, BC_TAG_STRING); 35641 JS_WriteString(s, p); 35642 } 35643 break; 35644 case JS_TAG_FUNCTION_BYTECODE: 35645 if (!s->allow_bytecode) 35646 goto invalid_tag; 35647 if (JS_WriteFunctionTag(s, obj)) 35648 goto fail; 35649 break; 35650 case JS_TAG_MODULE: 35651 if (!s->allow_bytecode) 35652 goto invalid_tag; 35653 if (JS_WriteModule(s, obj)) 35654 goto fail; 35655 break; 35656 case JS_TAG_OBJECT: 35657 { 35658 JSObject *p = JS_VALUE_GET_OBJ(obj); 35659 int ret, idx; 35660 35661 if (s->allow_reference) { 35662 idx = js_object_list_find(s->ctx, &s->object_list, p); 35663 if (idx >= 0) { 35664 bc_put_u8(s, BC_TAG_OBJECT_REFERENCE); 35665 bc_put_leb128(s, idx); 35666 break; 35667 } else { 35668 if (js_object_list_add(s->ctx, &s->object_list, p)) 35669 goto fail; 35670 } 35671 } else { 35672 if (p->tmp_mark) { 35673 JS_ThrowTypeError(s->ctx, "circular reference"); 35674 goto fail; 35675 } 35676 p->tmp_mark = 1; 35677 } 35678 switch(p->class_id) { 35679 case JS_CLASS_ARRAY: 35680 ret = JS_WriteArray(s, obj); 35681 break; 35682 case JS_CLASS_OBJECT: 35683 ret = JS_WriteObjectTag(s, obj); 35684 break; 35685 case JS_CLASS_ARRAY_BUFFER: 35686 ret = JS_WriteArrayBuffer(s, obj); 35687 break; 35688 case JS_CLASS_SHARED_ARRAY_BUFFER: 35689 if (!s->allow_sab) 35690 goto invalid_tag; 35691 ret = JS_WriteSharedArrayBuffer(s, obj); 35692 break; 35693 case JS_CLASS_DATE: 35694 bc_put_u8(s, BC_TAG_DATE); 35695 ret = JS_WriteObjectRec(s, p->u.object_data); 35696 break; 35697 case JS_CLASS_NUMBER: 35698 case JS_CLASS_STRING: 35699 case JS_CLASS_BOOLEAN: 35700 case JS_CLASS_BIG_INT: 35701 #ifdef CONFIG_BIGNUM 35702 case JS_CLASS_BIG_FLOAT: 35703 case JS_CLASS_BIG_DECIMAL: 35704 #endif 35705 bc_put_u8(s, BC_TAG_OBJECT_VALUE); 35706 ret = JS_WriteObjectRec(s, p->u.object_data); 35707 break; 35708 default: 35709 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 35710 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 35711 ret = JS_WriteTypedArray(s, obj); 35712 } else { 35713 JS_ThrowTypeError(s->ctx, "unsupported object class"); 35714 ret = -1; 35715 } 35716 break; 35717 } 35718 p->tmp_mark = 0; 35719 if (ret) 35720 goto fail; 35721 } 35722 break; 35723 case JS_TAG_BIG_INT: 35724 #ifdef CONFIG_BIGNUM 35725 case JS_TAG_BIG_FLOAT: 35726 case JS_TAG_BIG_DECIMAL: 35727 #endif 35728 if (JS_WriteBigNum(s, obj)) 35729 goto fail; 35730 break; 35731 default: 35732 invalid_tag: 35733 JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); 35734 goto fail; 35735 } 35736 return 0; 35737 35738 fail: 35739 return -1; 35740 } 35741 35742 /* create the atom table */ 35743 static int JS_WriteObjectAtoms(BCWriterState *s) 35744 { 35745 JSRuntime *rt = s->ctx->rt; 35746 DynBuf dbuf1; 35747 int i, atoms_size; 35748 35749 dbuf1 = s->dbuf; 35750 js_dbuf_init(s->ctx, &s->dbuf); 35751 bc_put_u8(s, BC_VERSION); 35752 35753 bc_put_leb128(s, s->idx_to_atom_count); 35754 for(i = 0; i < s->idx_to_atom_count; i++) { 35755 JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]]; 35756 JS_WriteString(s, p); 35757 } 35758 /* XXX: should check for OOM in above phase */ 35759 35760 /* move the atoms at the start */ 35761 /* XXX: could just append dbuf1 data, but it uses more memory if 35762 dbuf1 is larger than dbuf */ 35763 atoms_size = s->dbuf.size; 35764 if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size)) 35765 goto fail; 35766 memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size); 35767 memcpy(dbuf1.buf, s->dbuf.buf, atoms_size); 35768 dbuf1.size += atoms_size; 35769 dbuf_free(&s->dbuf); 35770 s->dbuf = dbuf1; 35771 return 0; 35772 fail: 35773 dbuf_free(&dbuf1); 35774 return -1; 35775 } 35776 35777 uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, 35778 int flags, uint8_t ***psab_tab, size_t *psab_tab_len) 35779 { 35780 BCWriterState ss, *s = &ss; 35781 35782 memset(s, 0, sizeof(*s)); 35783 s->ctx = ctx; 35784 s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); 35785 s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); 35786 s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); 35787 /* XXX: could use a different version when bytecode is included */ 35788 if (s->allow_bytecode) 35789 s->first_atom = JS_ATOM_END; 35790 else 35791 s->first_atom = 1; 35792 js_dbuf_init(ctx, &s->dbuf); 35793 js_object_list_init(&s->object_list); 35794 35795 if (JS_WriteObjectRec(s, obj)) 35796 goto fail; 35797 if (JS_WriteObjectAtoms(s)) 35798 goto fail; 35799 js_object_list_end(ctx, &s->object_list); 35800 js_free(ctx, s->atom_to_idx); 35801 js_free(ctx, s->idx_to_atom); 35802 *psize = s->dbuf.size; 35803 if (psab_tab) 35804 *psab_tab = s->sab_tab; 35805 if (psab_tab_len) 35806 *psab_tab_len = s->sab_tab_len; 35807 return s->dbuf.buf; 35808 fail: 35809 js_object_list_end(ctx, &s->object_list); 35810 js_free(ctx, s->atom_to_idx); 35811 js_free(ctx, s->idx_to_atom); 35812 dbuf_free(&s->dbuf); 35813 *psize = 0; 35814 if (psab_tab) 35815 *psab_tab = NULL; 35816 if (psab_tab_len) 35817 *psab_tab_len = 0; 35818 return NULL; 35819 } 35820 35821 uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj, 35822 int flags) 35823 { 35824 return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL); 35825 } 35826 35827 typedef struct BCReaderState { 35828 JSContext *ctx; 35829 const uint8_t *buf_start, *ptr, *buf_end; 35830 uint32_t first_atom; 35831 uint32_t idx_to_atom_count; 35832 JSAtom *idx_to_atom; 35833 int error_state; 35834 BOOL allow_sab : 8; 35835 BOOL allow_bytecode : 8; 35836 BOOL is_rom_data : 8; 35837 BOOL allow_reference : 8; 35838 /* object references */ 35839 JSObject **objects; 35840 int objects_count; 35841 int objects_size; 35842 35843 #ifdef DUMP_READ_OBJECT 35844 const uint8_t *ptr_last; 35845 int level; 35846 #endif 35847 } BCReaderState; 35848 35849 #ifdef DUMP_READ_OBJECT 35850 static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) { 35851 va_list ap; 35852 int i, n, n0; 35853 35854 if (!s->ptr_last) 35855 s->ptr_last = s->buf_start; 35856 35857 n = n0 = 0; 35858 if (s->ptr > s->ptr_last || s->ptr == s->buf_start) { 35859 n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start)); 35860 n += n0; 35861 } 35862 for (i = 0; s->ptr_last < s->ptr; i++) { 35863 if ((i & 7) == 0 && i > 0) { 35864 printf("\n%*s", n0, ""); 35865 n = n0; 35866 } 35867 n += printf(" %02x", *s->ptr_last++); 35868 } 35869 if (*fmt == '}') 35870 s->level--; 35871 if (n < 32 + s->level * 2) { 35872 printf("%*s", 32 + s->level * 2 - n, ""); 35873 } 35874 va_start(ap, fmt); 35875 vfprintf(stdout, fmt, ap); 35876 va_end(ap); 35877 if (strchr(fmt, '{')) 35878 s->level++; 35879 } 35880 #else 35881 #define bc_read_trace(...) 35882 #endif 35883 35884 static int bc_read_error_end(BCReaderState *s) 35885 { 35886 if (!s->error_state) { 35887 JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer"); 35888 } 35889 return s->error_state = -1; 35890 } 35891 35892 static int bc_get_u8(BCReaderState *s, uint8_t *pval) 35893 { 35894 if (unlikely(s->buf_end - s->ptr < 1)) { 35895 *pval = 0; /* avoid warning */ 35896 return bc_read_error_end(s); 35897 } 35898 *pval = *s->ptr++; 35899 return 0; 35900 } 35901 35902 static int bc_get_u16(BCReaderState *s, uint16_t *pval) 35903 { 35904 uint16_t v; 35905 if (unlikely(s->buf_end - s->ptr < 2)) { 35906 *pval = 0; /* avoid warning */ 35907 return bc_read_error_end(s); 35908 } 35909 v = get_u16(s->ptr); 35910 if (is_be()) 35911 v = bswap16(v); 35912 *pval = v; 35913 s->ptr += 2; 35914 return 0; 35915 } 35916 35917 static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) 35918 { 35919 uint32_t v; 35920 if (unlikely(s->buf_end - s->ptr < 4)) { 35921 *pval = 0; /* avoid warning */ 35922 return bc_read_error_end(s); 35923 } 35924 v = get_u32(s->ptr); 35925 if (is_be()) 35926 v = bswap32(v); 35927 *pval = v; 35928 s->ptr += 4; 35929 return 0; 35930 } 35931 35932 static int bc_get_u64(BCReaderState *s, uint64_t *pval) 35933 { 35934 uint64_t v; 35935 if (unlikely(s->buf_end - s->ptr < 8)) { 35936 *pval = 0; /* avoid warning */ 35937 return bc_read_error_end(s); 35938 } 35939 v = get_u64(s->ptr); 35940 if (is_be()) 35941 v = bswap64(v); 35942 *pval = v; 35943 s->ptr += 8; 35944 return 0; 35945 } 35946 35947 static int bc_get_leb128(BCReaderState *s, uint32_t *pval) 35948 { 35949 int ret; 35950 ret = get_leb128(pval, s->ptr, s->buf_end); 35951 if (unlikely(ret < 0)) 35952 return bc_read_error_end(s); 35953 s->ptr += ret; 35954 return 0; 35955 } 35956 35957 static int bc_get_sleb128(BCReaderState *s, int32_t *pval) 35958 { 35959 int ret; 35960 ret = get_sleb128(pval, s->ptr, s->buf_end); 35961 if (unlikely(ret < 0)) 35962 return bc_read_error_end(s); 35963 s->ptr += ret; 35964 return 0; 35965 } 35966 35967 /* XXX: used to read an `int` with a positive value */ 35968 static int bc_get_leb128_int(BCReaderState *s, int *pval) 35969 { 35970 return bc_get_leb128(s, (uint32_t *)pval); 35971 } 35972 35973 static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval) 35974 { 35975 uint32_t val; 35976 if (bc_get_leb128(s, &val)) { 35977 *pval = 0; 35978 return -1; 35979 } 35980 *pval = val; 35981 return 0; 35982 } 35983 35984 static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len) 35985 { 35986 if (buf_len != 0) { 35987 if (unlikely(!buf || s->buf_end - s->ptr < buf_len)) 35988 return bc_read_error_end(s); 35989 memcpy(buf, s->ptr, buf_len); 35990 s->ptr += buf_len; 35991 } 35992 return 0; 35993 } 35994 35995 static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) 35996 { 35997 JSAtom atom; 35998 35999 if (__JS_AtomIsTaggedInt(idx)) { 36000 atom = idx; 36001 } else if (idx < s->first_atom) { 36002 atom = JS_DupAtom(s->ctx, idx); 36003 } else { 36004 idx -= s->first_atom; 36005 if (idx >= s->idx_to_atom_count) { 36006 JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)", 36007 (unsigned int)(s->ptr - s->buf_start)); 36008 *patom = JS_ATOM_NULL; 36009 return s->error_state = -1; 36010 } 36011 atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]); 36012 } 36013 *patom = atom; 36014 return 0; 36015 } 36016 36017 static int bc_get_atom(BCReaderState *s, JSAtom *patom) 36018 { 36019 uint32_t v; 36020 if (bc_get_leb128(s, &v)) 36021 return -1; 36022 if (v & 1) { 36023 *patom = __JS_AtomFromUInt32(v >> 1); 36024 return 0; 36025 } else { 36026 return bc_idx_to_atom(s, patom, v >> 1); 36027 } 36028 } 36029 36030 static JSString *JS_ReadString(BCReaderState *s) 36031 { 36032 uint32_t len; 36033 size_t size; 36034 BOOL is_wide_char; 36035 JSString *p; 36036 36037 if (bc_get_leb128(s, &len)) 36038 return NULL; 36039 is_wide_char = len & 1; 36040 len >>= 1; 36041 p = js_alloc_string(s->ctx, len, is_wide_char); 36042 if (!p) { 36043 s->error_state = -1; 36044 return NULL; 36045 } 36046 size = (size_t)len << is_wide_char; 36047 if ((s->buf_end - s->ptr) < size) { 36048 bc_read_error_end(s); 36049 js_free_string(s->ctx->rt, p); 36050 return NULL; 36051 } 36052 memcpy(p->u.str8, s->ptr, size); 36053 s->ptr += size; 36054 if (is_wide_char) { 36055 if (is_be()) { 36056 uint32_t i; 36057 for (i = 0; i < len; i++) 36058 p->u.str16[i] = bswap16(p->u.str16[i]); 36059 } 36060 } else { 36061 p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ 36062 } 36063 #ifdef DUMP_READ_OBJECT 36064 JS_DumpString(s->ctx->rt, p); printf("\n"); 36065 #endif 36066 return p; 36067 } 36068 36069 static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n) 36070 { 36071 uint32_t val; 36072 /* XXX: this does not work for n == 32 */ 36073 val = (flags >> *pidx) & ((1U << n) - 1); 36074 *pidx += n; 36075 return val; 36076 } 36077 36078 static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, 36079 int byte_code_offset, uint32_t bc_len) 36080 { 36081 uint8_t *bc_buf; 36082 int pos, len, op; 36083 JSAtom atom; 36084 uint32_t idx; 36085 36086 if (s->is_rom_data) { 36087 /* directly use the input buffer */ 36088 if (unlikely(s->buf_end - s->ptr < bc_len)) 36089 return bc_read_error_end(s); 36090 bc_buf = (uint8_t *)s->ptr; 36091 s->ptr += bc_len; 36092 } else { 36093 bc_buf = (void *)((uint8_t*)b + byte_code_offset); 36094 if (bc_get_buf(s, bc_buf, bc_len)) 36095 return -1; 36096 } 36097 b->byte_code_buf = bc_buf; 36098 36099 if (is_be()) 36100 bc_byte_swap(bc_buf, bc_len); 36101 36102 pos = 0; 36103 while (pos < bc_len) { 36104 op = bc_buf[pos]; 36105 len = short_opcode_info(op).size; 36106 switch(short_opcode_info(op).fmt) { 36107 case OP_FMT_atom: 36108 case OP_FMT_atom_u8: 36109 case OP_FMT_atom_u16: 36110 case OP_FMT_atom_label_u8: 36111 case OP_FMT_atom_label_u16: 36112 idx = get_u32(bc_buf + pos + 1); 36113 if (s->is_rom_data) { 36114 /* just increment the reference count of the atom */ 36115 JS_DupAtom(s->ctx, (JSAtom)idx); 36116 } else { 36117 if (bc_idx_to_atom(s, &atom, idx)) { 36118 /* Note: the atoms will be freed up to this position */ 36119 b->byte_code_len = pos; 36120 return -1; 36121 } 36122 put_u32(bc_buf + pos + 1, atom); 36123 #ifdef DUMP_READ_OBJECT 36124 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n"); 36125 #endif 36126 } 36127 break; 36128 default: 36129 break; 36130 } 36131 pos += len; 36132 } 36133 return 0; 36134 } 36135 36136 static JSValue JS_ReadBigNum(BCReaderState *s, int tag) 36137 { 36138 JSValue obj = JS_UNDEFINED; 36139 uint8_t v8; 36140 int32_t e; 36141 uint32_t len; 36142 limb_t l, i, n; 36143 JSBigFloat *p; 36144 limb_t v; 36145 bf_t *a; 36146 36147 p = js_new_bf(s->ctx); 36148 if (!p) 36149 goto fail; 36150 switch(tag) { 36151 case BC_TAG_BIG_INT: 36152 obj = JS_MKPTR(JS_TAG_BIG_INT, p); 36153 break; 36154 #ifdef CONFIG_BIGNUM 36155 case BC_TAG_BIG_FLOAT: 36156 obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); 36157 break; 36158 case BC_TAG_BIG_DECIMAL: 36159 obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); 36160 break; 36161 #endif 36162 default: 36163 abort(); 36164 } 36165 36166 /* sign + exponent */ 36167 if (bc_get_sleb128(s, &e)) 36168 goto fail; 36169 36170 a = &p->num; 36171 a->sign = e & 1; 36172 e >>= 1; 36173 if (e == 0) 36174 a->expn = BF_EXP_ZERO; 36175 else if (e == 1) 36176 a->expn = BF_EXP_INF; 36177 else if (e == 2) 36178 a->expn = BF_EXP_NAN; 36179 else if (e >= 3) 36180 a->expn = e - 3; 36181 else 36182 a->expn = e; 36183 36184 /* mantissa */ 36185 if (a->expn != BF_EXP_ZERO && 36186 a->expn != BF_EXP_INF && 36187 a->expn != BF_EXP_NAN) { 36188 if (bc_get_leb128(s, &len)) 36189 goto fail; 36190 bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len); 36191 if (len == 0) { 36192 JS_ThrowInternalError(s->ctx, "invalid bignum length"); 36193 goto fail; 36194 } 36195 #ifdef CONFIG_BIGNUM 36196 if (tag == BC_TAG_BIG_DECIMAL) { 36197 l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; 36198 } else 36199 #endif 36200 { 36201 l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); 36202 } 36203 if (bf_resize(a, l)) { 36204 JS_ThrowOutOfMemory(s->ctx); 36205 goto fail; 36206 } 36207 #ifdef CONFIG_BIGNUM 36208 if (tag == BC_TAG_BIG_DECIMAL) { 36209 limb_t j; 36210 int bpos, d; 36211 36212 bpos = 0; 36213 for(i = 0; i < l; i++) { 36214 if (i == 0 && (n = len % LIMB_DIGITS) != 0) { 36215 j = LIMB_DIGITS - n; 36216 } else { 36217 j = 0; 36218 } 36219 v = 0; 36220 for(; j < LIMB_DIGITS; j++) { 36221 if (bpos == 0) { 36222 if (bc_get_u8(s, &v8)) 36223 goto fail; 36224 d = v8 & 0xf; 36225 bpos = 1; 36226 } else { 36227 d = v8 >> 4; 36228 bpos = 0; 36229 } 36230 if (d >= 10) { 36231 JS_ThrowInternalError(s->ctx, "invalid digit"); 36232 goto fail; 36233 } 36234 v += mp_pow_dec[j] * d; 36235 } 36236 a->tab[i] = v; 36237 } 36238 } else 36239 #endif /* CONFIG_BIGNUM */ 36240 { 36241 n = len & (sizeof(limb_t) - 1); 36242 if (n != 0) { 36243 v = 0; 36244 for(i = 0; i < n; i++) { 36245 if (bc_get_u8(s, &v8)) 36246 goto fail; 36247 v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); 36248 } 36249 a->tab[0] = v; 36250 i = 1; 36251 } else { 36252 i = 0; 36253 } 36254 for(; i < l; i++) { 36255 #if LIMB_BITS == 32 36256 if (bc_get_u32(s, &v)) 36257 goto fail; 36258 #else 36259 if (bc_get_u64(s, &v)) 36260 goto fail; 36261 #endif 36262 a->tab[i] = v; 36263 } 36264 } 36265 } 36266 bc_read_trace(s, "}\n"); 36267 return obj; 36268 fail: 36269 JS_FreeValue(s->ctx, obj); 36270 return JS_EXCEPTION; 36271 } 36272 36273 static JSValue JS_ReadObjectRec(BCReaderState *s); 36274 36275 static int BC_add_object_ref1(BCReaderState *s, JSObject *p) 36276 { 36277 if (s->allow_reference) { 36278 if (js_resize_array(s->ctx, (void *)&s->objects, 36279 sizeof(s->objects[0]), 36280 &s->objects_size, s->objects_count + 1)) 36281 return -1; 36282 s->objects[s->objects_count++] = p; 36283 } 36284 return 0; 36285 } 36286 36287 static int BC_add_object_ref(BCReaderState *s, JSValueConst obj) 36288 { 36289 return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj)); 36290 } 36291 36292 static JSValue JS_ReadFunctionTag(BCReaderState *s) 36293 { 36294 JSContext *ctx = s->ctx; 36295 JSFunctionBytecode bc, *b; 36296 JSValue obj = JS_UNDEFINED; 36297 uint16_t v16; 36298 uint8_t v8; 36299 int idx, i, local_count; 36300 int function_size, cpool_offset, byte_code_offset; 36301 int closure_var_offset, vardefs_offset; 36302 36303 memset(&bc, 0, sizeof(bc)); 36304 bc.header.ref_count = 1; 36305 //bc.gc_header.mark = 0; 36306 36307 if (bc_get_u16(s, &v16)) 36308 goto fail; 36309 idx = 0; 36310 bc.has_prototype = bc_get_flags(v16, &idx, 1); 36311 bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1); 36312 bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1); 36313 bc.need_home_object = bc_get_flags(v16, &idx, 1); 36314 bc.func_kind = bc_get_flags(v16, &idx, 2); 36315 bc.new_target_allowed = bc_get_flags(v16, &idx, 1); 36316 bc.super_call_allowed = bc_get_flags(v16, &idx, 1); 36317 bc.super_allowed = bc_get_flags(v16, &idx, 1); 36318 bc.arguments_allowed = bc_get_flags(v16, &idx, 1); 36319 bc.has_debug = bc_get_flags(v16, &idx, 1); 36320 bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); 36321 bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1); 36322 bc.read_only_bytecode = s->is_rom_data; 36323 if (bc_get_u8(s, &v8)) 36324 goto fail; 36325 bc.js_mode = v8; 36326 if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure 36327 goto fail; 36328 if (bc_get_leb128_u16(s, &bc.arg_count)) 36329 goto fail; 36330 if (bc_get_leb128_u16(s, &bc.var_count)) 36331 goto fail; 36332 if (bc_get_leb128_u16(s, &bc.defined_arg_count)) 36333 goto fail; 36334 if (bc_get_leb128_u16(s, &bc.stack_size)) 36335 goto fail; 36336 if (bc_get_leb128_int(s, &bc.closure_var_count)) 36337 goto fail; 36338 if (bc_get_leb128_int(s, &bc.cpool_count)) 36339 goto fail; 36340 if (bc_get_leb128_int(s, &bc.byte_code_len)) 36341 goto fail; 36342 if (bc_get_leb128_int(s, &local_count)) 36343 goto fail; 36344 36345 if (bc.has_debug) { 36346 function_size = sizeof(*b); 36347 } else { 36348 function_size = offsetof(JSFunctionBytecode, debug); 36349 } 36350 cpool_offset = function_size; 36351 function_size += bc.cpool_count * sizeof(*bc.cpool); 36352 vardefs_offset = function_size; 36353 function_size += local_count * sizeof(*bc.vardefs); 36354 closure_var_offset = function_size; 36355 function_size += bc.closure_var_count * sizeof(*bc.closure_var); 36356 byte_code_offset = function_size; 36357 if (!bc.read_only_bytecode) { 36358 function_size += bc.byte_code_len; 36359 } 36360 36361 b = js_mallocz(ctx, function_size); 36362 if (!b) 36363 return JS_EXCEPTION; 36364 36365 memcpy(b, &bc, offsetof(JSFunctionBytecode, debug)); 36366 b->header.ref_count = 1; 36367 if (local_count != 0) { 36368 b->vardefs = (void *)((uint8_t*)b + vardefs_offset); 36369 } 36370 if (b->closure_var_count != 0) { 36371 b->closure_var = (void *)((uint8_t*)b + closure_var_offset); 36372 } 36373 if (b->cpool_count != 0) { 36374 b->cpool = (void *)((uint8_t*)b + cpool_offset); 36375 } 36376 36377 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); 36378 36379 obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b); 36380 36381 #ifdef DUMP_READ_OBJECT 36382 bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n"); 36383 #endif 36384 bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n", 36385 b->arg_count, b->var_count, b->defined_arg_count, 36386 b->closure_var_count, b->cpool_count); 36387 bc_read_trace(s, "stack=%d bclen=%d locals=%d\n", 36388 b->stack_size, b->byte_code_len, local_count); 36389 36390 if (local_count != 0) { 36391 bc_read_trace(s, "vars {\n"); 36392 for(i = 0; i < local_count; i++) { 36393 JSVarDef *vd = &b->vardefs[i]; 36394 if (bc_get_atom(s, &vd->var_name)) 36395 goto fail; 36396 if (bc_get_leb128_int(s, &vd->scope_level)) 36397 goto fail; 36398 if (bc_get_leb128_int(s, &vd->scope_next)) 36399 goto fail; 36400 vd->scope_next--; 36401 if (bc_get_u8(s, &v8)) 36402 goto fail; 36403 idx = 0; 36404 vd->var_kind = bc_get_flags(v8, &idx, 4); 36405 vd->is_const = bc_get_flags(v8, &idx, 1); 36406 vd->is_lexical = bc_get_flags(v8, &idx, 1); 36407 vd->is_captured = bc_get_flags(v8, &idx, 1); 36408 #ifdef DUMP_READ_OBJECT 36409 bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n"); 36410 #endif 36411 } 36412 bc_read_trace(s, "}\n"); 36413 } 36414 if (b->closure_var_count != 0) { 36415 bc_read_trace(s, "closure vars {\n"); 36416 for(i = 0; i < b->closure_var_count; i++) { 36417 JSClosureVar *cv = &b->closure_var[i]; 36418 int var_idx; 36419 if (bc_get_atom(s, &cv->var_name)) 36420 goto fail; 36421 if (bc_get_leb128_int(s, &var_idx)) 36422 goto fail; 36423 cv->var_idx = var_idx; 36424 if (bc_get_u8(s, &v8)) 36425 goto fail; 36426 idx = 0; 36427 cv->is_local = bc_get_flags(v8, &idx, 1); 36428 cv->is_arg = bc_get_flags(v8, &idx, 1); 36429 cv->is_const = bc_get_flags(v8, &idx, 1); 36430 cv->is_lexical = bc_get_flags(v8, &idx, 1); 36431 cv->var_kind = bc_get_flags(v8, &idx, 4); 36432 #ifdef DUMP_READ_OBJECT 36433 bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n"); 36434 #endif 36435 } 36436 bc_read_trace(s, "}\n"); 36437 } 36438 { 36439 bc_read_trace(s, "bytecode {\n"); 36440 if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len)) 36441 goto fail; 36442 bc_read_trace(s, "}\n"); 36443 } 36444 if (b->has_debug) { 36445 /* read optional debug information */ 36446 bc_read_trace(s, "debug {\n"); 36447 if (bc_get_atom(s, &b->debug.filename)) 36448 goto fail; 36449 if (bc_get_leb128_int(s, &b->debug.line_num)) 36450 goto fail; 36451 if (bc_get_leb128_int(s, &b->debug.pc2line_len)) 36452 goto fail; 36453 if (b->debug.pc2line_len) { 36454 b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len); 36455 if (!b->debug.pc2line_buf) 36456 goto fail; 36457 if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len)) 36458 goto fail; 36459 } 36460 #ifdef DUMP_READ_OBJECT 36461 bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n"); 36462 #endif 36463 bc_read_trace(s, "}\n"); 36464 } 36465 if (b->cpool_count != 0) { 36466 bc_read_trace(s, "cpool {\n"); 36467 for(i = 0; i < b->cpool_count; i++) { 36468 JSValue val; 36469 val = JS_ReadObjectRec(s); 36470 if (JS_IsException(val)) 36471 goto fail; 36472 b->cpool[i] = val; 36473 } 36474 bc_read_trace(s, "}\n"); 36475 } 36476 b->realm = JS_DupContext(ctx); 36477 return obj; 36478 fail: 36479 JS_FreeValue(ctx, obj); 36480 return JS_EXCEPTION; 36481 } 36482 36483 static JSValue JS_ReadModule(BCReaderState *s) 36484 { 36485 JSContext *ctx = s->ctx; 36486 JSValue obj; 36487 JSModuleDef *m = NULL; 36488 JSAtom module_name; 36489 int i; 36490 uint8_t v8; 36491 36492 if (bc_get_atom(s, &module_name)) 36493 goto fail; 36494 #ifdef DUMP_READ_OBJECT 36495 bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n"); 36496 #endif 36497 m = js_new_module_def(ctx, module_name); 36498 if (!m) 36499 goto fail; 36500 obj = JS_NewModuleValue(ctx, m); 36501 if (bc_get_leb128_int(s, &m->req_module_entries_count)) 36502 goto fail; 36503 if (m->req_module_entries_count != 0) { 36504 m->req_module_entries_size = m->req_module_entries_count; 36505 m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size); 36506 if (!m->req_module_entries) 36507 goto fail; 36508 for(i = 0; i < m->req_module_entries_count; i++) { 36509 JSReqModuleEntry *rme = &m->req_module_entries[i]; 36510 if (bc_get_atom(s, &rme->module_name)) 36511 goto fail; 36512 } 36513 } 36514 36515 if (bc_get_leb128_int(s, &m->export_entries_count)) 36516 goto fail; 36517 if (m->export_entries_count != 0) { 36518 m->export_entries_size = m->export_entries_count; 36519 m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size); 36520 if (!m->export_entries) 36521 goto fail; 36522 for(i = 0; i < m->export_entries_count; i++) { 36523 JSExportEntry *me = &m->export_entries[i]; 36524 if (bc_get_u8(s, &v8)) 36525 goto fail; 36526 me->export_type = v8; 36527 if (me->export_type == JS_EXPORT_TYPE_LOCAL) { 36528 if (bc_get_leb128_int(s, &me->u.local.var_idx)) 36529 goto fail; 36530 } else { 36531 if (bc_get_leb128_int(s, &me->u.req_module_idx)) 36532 goto fail; 36533 if (bc_get_atom(s, &me->local_name)) 36534 goto fail; 36535 } 36536 if (bc_get_atom(s, &me->export_name)) 36537 goto fail; 36538 } 36539 } 36540 36541 if (bc_get_leb128_int(s, &m->star_export_entries_count)) 36542 goto fail; 36543 if (m->star_export_entries_count != 0) { 36544 m->star_export_entries_size = m->star_export_entries_count; 36545 m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size); 36546 if (!m->star_export_entries) 36547 goto fail; 36548 for(i = 0; i < m->star_export_entries_count; i++) { 36549 JSStarExportEntry *se = &m->star_export_entries[i]; 36550 if (bc_get_leb128_int(s, &se->req_module_idx)) 36551 goto fail; 36552 } 36553 } 36554 36555 if (bc_get_leb128_int(s, &m->import_entries_count)) 36556 goto fail; 36557 if (m->import_entries_count != 0) { 36558 m->import_entries_size = m->import_entries_count; 36559 m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size); 36560 if (!m->import_entries) 36561 goto fail; 36562 for(i = 0; i < m->import_entries_count; i++) { 36563 JSImportEntry *mi = &m->import_entries[i]; 36564 if (bc_get_leb128_int(s, &mi->var_idx)) 36565 goto fail; 36566 if (bc_get_atom(s, &mi->import_name)) 36567 goto fail; 36568 if (bc_get_leb128_int(s, &mi->req_module_idx)) 36569 goto fail; 36570 } 36571 } 36572 36573 if (bc_get_u8(s, &v8)) 36574 goto fail; 36575 m->has_tla = (v8 != 0); 36576 36577 m->func_obj = JS_ReadObjectRec(s); 36578 if (JS_IsException(m->func_obj)) 36579 goto fail; 36580 return obj; 36581 fail: 36582 if (m) { 36583 js_free_module_def(ctx, m); 36584 } 36585 return JS_EXCEPTION; 36586 } 36587 36588 static JSValue JS_ReadObjectTag(BCReaderState *s) 36589 { 36590 JSContext *ctx = s->ctx; 36591 JSValue obj; 36592 uint32_t prop_count, i; 36593 JSAtom atom; 36594 JSValue val; 36595 int ret; 36596 36597 obj = JS_NewObject(ctx); 36598 if (BC_add_object_ref(s, obj)) 36599 goto fail; 36600 if (bc_get_leb128(s, &prop_count)) 36601 goto fail; 36602 for(i = 0; i < prop_count; i++) { 36603 if (bc_get_atom(s, &atom)) 36604 goto fail; 36605 #ifdef DUMP_READ_OBJECT 36606 bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n"); 36607 #endif 36608 val = JS_ReadObjectRec(s); 36609 if (JS_IsException(val)) { 36610 JS_FreeAtom(ctx, atom); 36611 goto fail; 36612 } 36613 ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E); 36614 JS_FreeAtom(ctx, atom); 36615 if (ret < 0) 36616 goto fail; 36617 } 36618 return obj; 36619 fail: 36620 JS_FreeValue(ctx, obj); 36621 return JS_EXCEPTION; 36622 } 36623 36624 static JSValue JS_ReadArray(BCReaderState *s, int tag) 36625 { 36626 JSContext *ctx = s->ctx; 36627 JSValue obj; 36628 uint32_t len, i; 36629 JSValue val; 36630 int ret, prop_flags; 36631 BOOL is_template; 36632 36633 obj = JS_NewArray(ctx); 36634 if (BC_add_object_ref(s, obj)) 36635 goto fail; 36636 is_template = (tag == BC_TAG_TEMPLATE_OBJECT); 36637 if (bc_get_leb128(s, &len)) 36638 goto fail; 36639 for(i = 0; i < len; i++) { 36640 val = JS_ReadObjectRec(s); 36641 if (JS_IsException(val)) 36642 goto fail; 36643 if (is_template) 36644 prop_flags = JS_PROP_ENUMERABLE; 36645 else 36646 prop_flags = JS_PROP_C_W_E; 36647 ret = JS_DefinePropertyValueUint32(ctx, obj, i, val, 36648 prop_flags); 36649 if (ret < 0) 36650 goto fail; 36651 } 36652 if (is_template) { 36653 val = JS_ReadObjectRec(s); 36654 if (JS_IsException(val)) 36655 goto fail; 36656 if (!JS_IsUndefined(val)) { 36657 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0); 36658 if (ret < 0) 36659 goto fail; 36660 } 36661 JS_PreventExtensions(ctx, obj); 36662 } 36663 return obj; 36664 fail: 36665 JS_FreeValue(ctx, obj); 36666 return JS_EXCEPTION; 36667 } 36668 36669 static JSValue JS_ReadTypedArray(BCReaderState *s) 36670 { 36671 JSContext *ctx = s->ctx; 36672 JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED; 36673 uint8_t array_tag; 36674 JSValueConst args[3]; 36675 uint32_t offset, len, idx; 36676 36677 if (bc_get_u8(s, &array_tag)) 36678 return JS_EXCEPTION; 36679 if (array_tag >= JS_TYPED_ARRAY_COUNT) 36680 return JS_ThrowTypeError(ctx, "invalid typed array"); 36681 if (bc_get_leb128(s, &len)) 36682 return JS_EXCEPTION; 36683 if (bc_get_leb128(s, &offset)) 36684 return JS_EXCEPTION; 36685 /* XXX: this hack could be avoided if the typed array could be 36686 created before the array buffer */ 36687 idx = s->objects_count; 36688 if (BC_add_object_ref1(s, NULL)) 36689 goto fail; 36690 array_buffer = JS_ReadObjectRec(s); 36691 if (JS_IsException(array_buffer)) 36692 return JS_EXCEPTION; 36693 if (!js_get_array_buffer(ctx, array_buffer)) { 36694 JS_FreeValue(ctx, array_buffer); 36695 return JS_EXCEPTION; 36696 } 36697 args[0] = array_buffer; 36698 args[1] = JS_NewInt64(ctx, offset); 36699 args[2] = JS_NewInt64(ctx, len); 36700 obj = js_typed_array_constructor(ctx, JS_UNDEFINED, 36701 3, args, 36702 JS_CLASS_UINT8C_ARRAY + array_tag); 36703 if (JS_IsException(obj)) 36704 goto fail; 36705 if (s->allow_reference) { 36706 s->objects[idx] = JS_VALUE_GET_OBJ(obj); 36707 } 36708 JS_FreeValue(ctx, array_buffer); 36709 return obj; 36710 fail: 36711 JS_FreeValue(ctx, array_buffer); 36712 JS_FreeValue(ctx, obj); 36713 return JS_EXCEPTION; 36714 } 36715 36716 static JSValue JS_ReadArrayBuffer(BCReaderState *s) 36717 { 36718 JSContext *ctx = s->ctx; 36719 uint32_t byte_length; 36720 JSValue obj; 36721 36722 if (bc_get_leb128(s, &byte_length)) 36723 return JS_EXCEPTION; 36724 if (unlikely(s->buf_end - s->ptr < byte_length)) { 36725 bc_read_error_end(s); 36726 return JS_EXCEPTION; 36727 } 36728 obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length); 36729 if (JS_IsException(obj)) 36730 goto fail; 36731 if (BC_add_object_ref(s, obj)) 36732 goto fail; 36733 s->ptr += byte_length; 36734 return obj; 36735 fail: 36736 JS_FreeValue(ctx, obj); 36737 return JS_EXCEPTION; 36738 } 36739 36740 static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s) 36741 { 36742 JSContext *ctx = s->ctx; 36743 uint32_t byte_length; 36744 uint8_t *data_ptr; 36745 JSValue obj; 36746 uint64_t u64; 36747 36748 if (bc_get_leb128(s, &byte_length)) 36749 return JS_EXCEPTION; 36750 if (bc_get_u64(s, &u64)) 36751 return JS_EXCEPTION; 36752 data_ptr = (uint8_t *)(uintptr_t)u64; 36753 /* the SharedArrayBuffer is cloned */ 36754 obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length, 36755 JS_CLASS_SHARED_ARRAY_BUFFER, 36756 data_ptr, 36757 NULL, NULL, FALSE); 36758 if (JS_IsException(obj)) 36759 goto fail; 36760 if (BC_add_object_ref(s, obj)) 36761 goto fail; 36762 return obj; 36763 fail: 36764 JS_FreeValue(ctx, obj); 36765 return JS_EXCEPTION; 36766 } 36767 36768 static JSValue JS_ReadDate(BCReaderState *s) 36769 { 36770 JSContext *ctx = s->ctx; 36771 JSValue val, obj = JS_UNDEFINED; 36772 36773 val = JS_ReadObjectRec(s); 36774 if (JS_IsException(val)) 36775 goto fail; 36776 if (!JS_IsNumber(val)) { 36777 JS_ThrowTypeError(ctx, "Number tag expected for date"); 36778 goto fail; 36779 } 36780 obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE], 36781 JS_CLASS_DATE); 36782 if (JS_IsException(obj)) 36783 goto fail; 36784 if (BC_add_object_ref(s, obj)) 36785 goto fail; 36786 JS_SetObjectData(ctx, obj, val); 36787 return obj; 36788 fail: 36789 JS_FreeValue(ctx, val); 36790 JS_FreeValue(ctx, obj); 36791 return JS_EXCEPTION; 36792 } 36793 36794 static JSValue JS_ReadObjectValue(BCReaderState *s) 36795 { 36796 JSContext *ctx = s->ctx; 36797 JSValue val, obj = JS_UNDEFINED; 36798 36799 val = JS_ReadObjectRec(s); 36800 if (JS_IsException(val)) 36801 goto fail; 36802 obj = JS_ToObject(ctx, val); 36803 if (JS_IsException(obj)) 36804 goto fail; 36805 if (BC_add_object_ref(s, obj)) 36806 goto fail; 36807 JS_FreeValue(ctx, val); 36808 return obj; 36809 fail: 36810 JS_FreeValue(ctx, val); 36811 JS_FreeValue(ctx, obj); 36812 return JS_EXCEPTION; 36813 } 36814 36815 static JSValue JS_ReadObjectRec(BCReaderState *s) 36816 { 36817 JSContext *ctx = s->ctx; 36818 uint8_t tag; 36819 JSValue obj = JS_UNDEFINED; 36820 36821 if (js_check_stack_overflow(ctx->rt, 0)) 36822 return JS_ThrowStackOverflow(ctx); 36823 36824 if (bc_get_u8(s, &tag)) 36825 return JS_EXCEPTION; 36826 36827 bc_read_trace(s, "%s {\n", bc_tag_str[tag]); 36828 36829 switch(tag) { 36830 case BC_TAG_NULL: 36831 obj = JS_NULL; 36832 break; 36833 case BC_TAG_UNDEFINED: 36834 obj = JS_UNDEFINED; 36835 break; 36836 case BC_TAG_BOOL_FALSE: 36837 case BC_TAG_BOOL_TRUE: 36838 obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE); 36839 break; 36840 case BC_TAG_INT32: 36841 { 36842 int32_t val; 36843 if (bc_get_sleb128(s, &val)) 36844 return JS_EXCEPTION; 36845 bc_read_trace(s, "%d\n", val); 36846 obj = JS_NewInt32(ctx, val); 36847 } 36848 break; 36849 case BC_TAG_FLOAT64: 36850 { 36851 JSFloat64Union u; 36852 if (bc_get_u64(s, &u.u64)) 36853 return JS_EXCEPTION; 36854 bc_read_trace(s, "%g\n", u.d); 36855 obj = __JS_NewFloat64(ctx, u.d); 36856 } 36857 break; 36858 case BC_TAG_STRING: 36859 { 36860 JSString *p; 36861 p = JS_ReadString(s); 36862 if (!p) 36863 return JS_EXCEPTION; 36864 obj = JS_MKPTR(JS_TAG_STRING, p); 36865 } 36866 break; 36867 case BC_TAG_FUNCTION_BYTECODE: 36868 if (!s->allow_bytecode) 36869 goto invalid_tag; 36870 obj = JS_ReadFunctionTag(s); 36871 break; 36872 case BC_TAG_MODULE: 36873 if (!s->allow_bytecode) 36874 goto invalid_tag; 36875 obj = JS_ReadModule(s); 36876 break; 36877 case BC_TAG_OBJECT: 36878 obj = JS_ReadObjectTag(s); 36879 break; 36880 case BC_TAG_ARRAY: 36881 case BC_TAG_TEMPLATE_OBJECT: 36882 obj = JS_ReadArray(s, tag); 36883 break; 36884 case BC_TAG_TYPED_ARRAY: 36885 obj = JS_ReadTypedArray(s); 36886 break; 36887 case BC_TAG_ARRAY_BUFFER: 36888 obj = JS_ReadArrayBuffer(s); 36889 break; 36890 case BC_TAG_SHARED_ARRAY_BUFFER: 36891 if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup) 36892 goto invalid_tag; 36893 obj = JS_ReadSharedArrayBuffer(s); 36894 break; 36895 case BC_TAG_DATE: 36896 obj = JS_ReadDate(s); 36897 break; 36898 case BC_TAG_OBJECT_VALUE: 36899 obj = JS_ReadObjectValue(s); 36900 break; 36901 case BC_TAG_BIG_INT: 36902 #ifdef CONFIG_BIGNUM 36903 case BC_TAG_BIG_FLOAT: 36904 case BC_TAG_BIG_DECIMAL: 36905 #endif 36906 obj = JS_ReadBigNum(s, tag); 36907 break; 36908 case BC_TAG_OBJECT_REFERENCE: 36909 { 36910 uint32_t val; 36911 if (!s->allow_reference) 36912 return JS_ThrowSyntaxError(ctx, "object references are not allowed"); 36913 if (bc_get_leb128(s, &val)) 36914 return JS_EXCEPTION; 36915 bc_read_trace(s, "%u\n", val); 36916 if (val >= s->objects_count) { 36917 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", 36918 val, s->objects_count); 36919 } 36920 obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val])); 36921 } 36922 break; 36923 default: 36924 invalid_tag: 36925 return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)", 36926 tag, (unsigned int)(s->ptr - s->buf_start)); 36927 } 36928 bc_read_trace(s, "}\n"); 36929 return obj; 36930 } 36931 36932 static int JS_ReadObjectAtoms(BCReaderState *s) 36933 { 36934 uint8_t v8; 36935 JSString *p; 36936 int i; 36937 JSAtom atom; 36938 36939 if (bc_get_u8(s, &v8)) 36940 return -1; 36941 if (v8 != BC_VERSION) { 36942 JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", 36943 v8, BC_VERSION); 36944 return -1; 36945 } 36946 if (bc_get_leb128(s, &s->idx_to_atom_count)) 36947 return -1; 36948 36949 bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count); 36950 36951 if (s->idx_to_atom_count != 0) { 36952 s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count * 36953 sizeof(s->idx_to_atom[0])); 36954 if (!s->idx_to_atom) 36955 return s->error_state = -1; 36956 } 36957 for(i = 0; i < s->idx_to_atom_count; i++) { 36958 p = JS_ReadString(s); 36959 if (!p) 36960 return -1; 36961 atom = JS_NewAtomStr(s->ctx, p); 36962 if (atom == JS_ATOM_NULL) 36963 return s->error_state = -1; 36964 s->idx_to_atom[i] = atom; 36965 if (s->is_rom_data && (atom != (i + s->first_atom))) 36966 s->is_rom_data = FALSE; /* atoms must be relocated */ 36967 } 36968 bc_read_trace(s, "}\n"); 36969 return 0; 36970 } 36971 36972 static void bc_reader_free(BCReaderState *s) 36973 { 36974 int i; 36975 if (s->idx_to_atom) { 36976 for(i = 0; i < s->idx_to_atom_count; i++) { 36977 JS_FreeAtom(s->ctx, s->idx_to_atom[i]); 36978 } 36979 js_free(s->ctx, s->idx_to_atom); 36980 } 36981 js_free(s->ctx, s->objects); 36982 } 36983 36984 JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len, 36985 int flags) 36986 { 36987 BCReaderState ss, *s = &ss; 36988 JSValue obj; 36989 36990 ctx->binary_object_count += 1; 36991 ctx->binary_object_size += buf_len; 36992 36993 memset(s, 0, sizeof(*s)); 36994 s->ctx = ctx; 36995 s->buf_start = buf; 36996 s->buf_end = buf + buf_len; 36997 s->ptr = buf; 36998 s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0); 36999 s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0); 37000 s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0); 37001 s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0); 37002 if (s->allow_bytecode) 37003 s->first_atom = JS_ATOM_END; 37004 else 37005 s->first_atom = 1; 37006 if (JS_ReadObjectAtoms(s)) { 37007 obj = JS_EXCEPTION; 37008 } else { 37009 obj = JS_ReadObjectRec(s); 37010 } 37011 bc_reader_free(s); 37012 return obj; 37013 } 37014 37015 /*******************************************************************/ 37016 /* runtime functions & objects */ 37017 37018 static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val, 37019 int argc, JSValueConst *argv); 37020 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val, 37021 int argc, JSValueConst *argv); 37022 static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val, 37023 int argc, JSValueConst *argv); 37024 37025 static int check_function(JSContext *ctx, JSValueConst obj) 37026 { 37027 if (likely(JS_IsFunction(ctx, obj))) 37028 return 0; 37029 JS_ThrowTypeError(ctx, "not a function"); 37030 return -1; 37031 } 37032 37033 static int check_exception_free(JSContext *ctx, JSValue obj) 37034 { 37035 JS_FreeValue(ctx, obj); 37036 return JS_IsException(obj); 37037 } 37038 37039 static JSAtom find_atom(JSContext *ctx, const char *name) 37040 { 37041 JSAtom atom; 37042 int len; 37043 37044 if (*name == '[') { 37045 name++; 37046 len = strlen(name) - 1; 37047 /* We assume 8 bit non null strings, which is the case for these 37048 symbols */ 37049 for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) { 37050 JSAtomStruct *p = ctx->rt->atom_array[atom]; 37051 JSString *str = p; 37052 if (str->len == len && !memcmp(str->u.str8, name, len)) 37053 return JS_DupAtom(ctx, atom); 37054 } 37055 abort(); 37056 } else { 37057 atom = JS_NewAtom(ctx, name); 37058 } 37059 return atom; 37060 } 37061 37062 static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, 37063 JSAtom atom, void *opaque) 37064 { 37065 const JSCFunctionListEntry *e = opaque; 37066 JSValue val; 37067 37068 switch(e->def_type) { 37069 case JS_DEF_CFUNC: 37070 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, 37071 e->name, e->u.func.length, e->u.func.cproto, e->magic); 37072 break; 37073 case JS_DEF_PROP_STRING: 37074 val = JS_NewAtomString(ctx, e->u.str); 37075 break; 37076 case JS_DEF_OBJECT: 37077 val = JS_NewObject(ctx); 37078 JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); 37079 break; 37080 default: 37081 abort(); 37082 } 37083 return val; 37084 } 37085 37086 static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, 37087 JSAtom atom, 37088 const JSCFunctionListEntry *e) 37089 { 37090 JSValue val; 37091 int prop_flags = e->prop_flags; 37092 37093 switch(e->def_type) { 37094 case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */ 37095 { 37096 JSAtom atom1 = find_atom(ctx, e->u.alias.name); 37097 switch (e->u.alias.base) { 37098 case -1: 37099 val = JS_GetProperty(ctx, obj, atom1); 37100 break; 37101 case 0: 37102 val = JS_GetProperty(ctx, ctx->global_obj, atom1); 37103 break; 37104 case 1: 37105 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1); 37106 break; 37107 default: 37108 abort(); 37109 } 37110 JS_FreeAtom(ctx, atom1); 37111 if (atom == JS_ATOM_Symbol_toPrimitive) { 37112 /* Symbol.toPrimitive functions are not writable */ 37113 prop_flags = JS_PROP_CONFIGURABLE; 37114 } else if (atom == JS_ATOM_Symbol_hasInstance) { 37115 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ 37116 prop_flags = 0; 37117 } 37118 } 37119 break; 37120 case JS_DEF_CFUNC: 37121 if (atom == JS_ATOM_Symbol_toPrimitive) { 37122 /* Symbol.toPrimitive functions are not writable */ 37123 prop_flags = JS_PROP_CONFIGURABLE; 37124 } else if (atom == JS_ATOM_Symbol_hasInstance) { 37125 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */ 37126 prop_flags = 0; 37127 } 37128 JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, 37129 (void *)e, prop_flags); 37130 return 0; 37131 case JS_DEF_CGETSET: /* XXX: use autoinit again ? */ 37132 case JS_DEF_CGETSET_MAGIC: 37133 { 37134 JSValue getter, setter; 37135 char buf[64]; 37136 37137 getter = JS_UNDEFINED; 37138 if (e->u.getset.get.generic) { 37139 snprintf(buf, sizeof(buf), "get %s", e->name); 37140 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic, 37141 buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter, 37142 e->magic); 37143 } 37144 setter = JS_UNDEFINED; 37145 if (e->u.getset.set.generic) { 37146 snprintf(buf, sizeof(buf), "set %s", e->name); 37147 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic, 37148 buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter, 37149 e->magic); 37150 } 37151 JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags); 37152 return 0; 37153 } 37154 break; 37155 case JS_DEF_PROP_INT32: 37156 val = JS_NewInt32(ctx, e->u.i32); 37157 break; 37158 case JS_DEF_PROP_INT64: 37159 val = JS_NewInt64(ctx, e->u.i64); 37160 break; 37161 case JS_DEF_PROP_DOUBLE: 37162 val = __JS_NewFloat64(ctx, e->u.f64); 37163 break; 37164 case JS_DEF_PROP_UNDEFINED: 37165 val = JS_UNDEFINED; 37166 break; 37167 case JS_DEF_PROP_STRING: 37168 case JS_DEF_OBJECT: 37169 JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP, 37170 (void *)e, prop_flags); 37171 return 0; 37172 default: 37173 abort(); 37174 } 37175 JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags); 37176 return 0; 37177 } 37178 37179 void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj, 37180 const JSCFunctionListEntry *tab, int len) 37181 { 37182 int i; 37183 37184 for (i = 0; i < len; i++) { 37185 const JSCFunctionListEntry *e = &tab[i]; 37186 JSAtom atom = find_atom(ctx, e->name); 37187 JS_InstantiateFunctionListItem(ctx, obj, atom, e); 37188 JS_FreeAtom(ctx, atom); 37189 } 37190 } 37191 37192 int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m, 37193 const JSCFunctionListEntry *tab, int len) 37194 { 37195 int i; 37196 for(i = 0; i < len; i++) { 37197 if (JS_AddModuleExport(ctx, m, tab[i].name)) 37198 return -1; 37199 } 37200 return 0; 37201 } 37202 37203 int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, 37204 const JSCFunctionListEntry *tab, int len) 37205 { 37206 int i; 37207 JSValue val; 37208 37209 for(i = 0; i < len; i++) { 37210 const JSCFunctionListEntry *e = &tab[i]; 37211 switch(e->def_type) { 37212 case JS_DEF_CFUNC: 37213 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic, 37214 e->name, e->u.func.length, e->u.func.cproto, e->magic); 37215 break; 37216 case JS_DEF_PROP_STRING: 37217 val = JS_NewString(ctx, e->u.str); 37218 break; 37219 case JS_DEF_PROP_INT32: 37220 val = JS_NewInt32(ctx, e->u.i32); 37221 break; 37222 case JS_DEF_PROP_INT64: 37223 val = JS_NewInt64(ctx, e->u.i64); 37224 break; 37225 case JS_DEF_PROP_DOUBLE: 37226 val = __JS_NewFloat64(ctx, e->u.f64); 37227 break; 37228 case JS_DEF_OBJECT: 37229 val = JS_NewObject(ctx); 37230 JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len); 37231 break; 37232 default: 37233 abort(); 37234 } 37235 if (JS_SetModuleExport(ctx, m, e->name, val)) 37236 return -1; 37237 } 37238 return 0; 37239 } 37240 37241 /* Note: 'func_obj' is not necessarily a constructor */ 37242 static void JS_SetConstructor2(JSContext *ctx, 37243 JSValueConst func_obj, 37244 JSValueConst proto, 37245 int proto_flags, int ctor_flags) 37246 { 37247 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, 37248 JS_DupValue(ctx, proto), proto_flags); 37249 JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, 37250 JS_DupValue(ctx, func_obj), 37251 ctor_flags); 37252 set_cycle_flag(ctx, func_obj); 37253 set_cycle_flag(ctx, proto); 37254 } 37255 37256 void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, 37257 JSValueConst proto) 37258 { 37259 JS_SetConstructor2(ctx, func_obj, proto, 37260 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 37261 } 37262 37263 static void JS_NewGlobalCConstructor2(JSContext *ctx, 37264 JSValue func_obj, 37265 const char *name, 37266 JSValueConst proto) 37267 { 37268 JS_DefinePropertyValueStr(ctx, ctx->global_obj, name, 37269 JS_DupValue(ctx, func_obj), 37270 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 37271 JS_SetConstructor(ctx, func_obj, proto); 37272 JS_FreeValue(ctx, func_obj); 37273 } 37274 37275 static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name, 37276 JSCFunction *func, int length, 37277 JSValueConst proto) 37278 { 37279 JSValue func_obj; 37280 func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0); 37281 JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); 37282 return func_obj; 37283 } 37284 37285 static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name, 37286 JSCFunction *func, int length, 37287 JSValueConst proto) 37288 { 37289 JSValue func_obj; 37290 func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0); 37291 JS_NewGlobalCConstructor2(ctx, func_obj, name, proto); 37292 return func_obj; 37293 } 37294 37295 static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val, 37296 int argc, JSValueConst *argv) 37297 { 37298 return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1); 37299 } 37300 37301 static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, 37302 int argc, JSValueConst *argv) 37303 { 37304 double d; 37305 37306 /* XXX: does this work for bigfloat? */ 37307 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 37308 return JS_EXCEPTION; 37309 return JS_NewBool(ctx, isnan(d)); 37310 } 37311 37312 static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, 37313 int argc, JSValueConst *argv) 37314 { 37315 double d; 37316 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 37317 return JS_EXCEPTION; 37318 return JS_NewBool(ctx, isfinite(d)); 37319 } 37320 37321 /* Object class */ 37322 37323 static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) 37324 { 37325 int tag = JS_VALUE_GET_NORM_TAG(val); 37326 JSValue obj; 37327 37328 switch(tag) { 37329 default: 37330 case JS_TAG_NULL: 37331 case JS_TAG_UNDEFINED: 37332 return JS_ThrowTypeError(ctx, "cannot convert to object"); 37333 case JS_TAG_OBJECT: 37334 case JS_TAG_EXCEPTION: 37335 return JS_DupValue(ctx, val); 37336 case JS_TAG_BIG_INT: 37337 obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); 37338 goto set_value; 37339 #ifdef CONFIG_BIGNUM 37340 case JS_TAG_BIG_FLOAT: 37341 obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); 37342 goto set_value; 37343 case JS_TAG_BIG_DECIMAL: 37344 obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL); 37345 goto set_value; 37346 #endif 37347 case JS_TAG_INT: 37348 case JS_TAG_FLOAT64: 37349 obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER); 37350 goto set_value; 37351 case JS_TAG_STRING: 37352 /* XXX: should call the string constructor */ 37353 { 37354 JSString *p1 = JS_VALUE_GET_STRING(val); 37355 obj = JS_NewObjectClass(ctx, JS_CLASS_STRING); 37356 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); 37357 } 37358 goto set_value; 37359 case JS_TAG_BOOL: 37360 obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN); 37361 goto set_value; 37362 case JS_TAG_SYMBOL: 37363 obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL); 37364 set_value: 37365 if (!JS_IsException(obj)) 37366 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val)); 37367 return obj; 37368 } 37369 } 37370 37371 static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val) 37372 { 37373 JSValue obj = JS_ToObject(ctx, val); 37374 JS_FreeValue(ctx, val); 37375 return obj; 37376 } 37377 37378 static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, 37379 JSValueConst desc) 37380 { 37381 JSValue val, getter, setter; 37382 int flags; 37383 37384 if (!JS_IsObject(desc)) { 37385 JS_ThrowTypeErrorNotAnObject(ctx); 37386 return -1; 37387 } 37388 flags = 0; 37389 val = JS_UNDEFINED; 37390 getter = JS_UNDEFINED; 37391 setter = JS_UNDEFINED; 37392 if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) { 37393 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable); 37394 if (JS_IsException(prop)) 37395 goto fail; 37396 flags |= JS_PROP_HAS_CONFIGURABLE; 37397 if (JS_ToBoolFree(ctx, prop)) 37398 flags |= JS_PROP_CONFIGURABLE; 37399 } 37400 if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) { 37401 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable); 37402 if (JS_IsException(prop)) 37403 goto fail; 37404 flags |= JS_PROP_HAS_WRITABLE; 37405 if (JS_ToBoolFree(ctx, prop)) 37406 flags |= JS_PROP_WRITABLE; 37407 } 37408 if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) { 37409 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable); 37410 if (JS_IsException(prop)) 37411 goto fail; 37412 flags |= JS_PROP_HAS_ENUMERABLE; 37413 if (JS_ToBoolFree(ctx, prop)) 37414 flags |= JS_PROP_ENUMERABLE; 37415 } 37416 if (JS_HasProperty(ctx, desc, JS_ATOM_value)) { 37417 flags |= JS_PROP_HAS_VALUE; 37418 val = JS_GetProperty(ctx, desc, JS_ATOM_value); 37419 if (JS_IsException(val)) 37420 goto fail; 37421 } 37422 if (JS_HasProperty(ctx, desc, JS_ATOM_get)) { 37423 flags |= JS_PROP_HAS_GET; 37424 getter = JS_GetProperty(ctx, desc, JS_ATOM_get); 37425 if (JS_IsException(getter) || 37426 !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) { 37427 JS_ThrowTypeError(ctx, "invalid getter"); 37428 goto fail; 37429 } 37430 } 37431 if (JS_HasProperty(ctx, desc, JS_ATOM_set)) { 37432 flags |= JS_PROP_HAS_SET; 37433 setter = JS_GetProperty(ctx, desc, JS_ATOM_set); 37434 if (JS_IsException(setter) || 37435 !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) { 37436 JS_ThrowTypeError(ctx, "invalid setter"); 37437 goto fail; 37438 } 37439 } 37440 if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) && 37441 (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) { 37442 JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable"); 37443 goto fail; 37444 } 37445 d->flags = flags; 37446 d->value = val; 37447 d->getter = getter; 37448 d->setter = setter; 37449 return 0; 37450 fail: 37451 JS_FreeValue(ctx, val); 37452 JS_FreeValue(ctx, getter); 37453 JS_FreeValue(ctx, setter); 37454 return -1; 37455 } 37456 37457 static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, 37458 JSAtom prop, JSValueConst desc, 37459 int flags) 37460 { 37461 JSPropertyDescriptor d; 37462 int ret; 37463 37464 if (js_obj_to_desc(ctx, &d, desc) < 0) 37465 return -1; 37466 37467 ret = JS_DefineProperty(ctx, obj, prop, 37468 d.value, d.getter, d.setter, d.flags | flags); 37469 js_free_desc(ctx, &d); 37470 return ret; 37471 } 37472 37473 static __exception int JS_ObjectDefineProperties(JSContext *ctx, 37474 JSValueConst obj, 37475 JSValueConst properties) 37476 { 37477 JSValue props, desc; 37478 JSObject *p; 37479 JSPropertyEnum *atoms; 37480 uint32_t len, i; 37481 int ret = -1; 37482 37483 if (!JS_IsObject(obj)) { 37484 JS_ThrowTypeErrorNotAnObject(ctx); 37485 return -1; 37486 } 37487 desc = JS_UNDEFINED; 37488 props = JS_ToObject(ctx, properties); 37489 if (JS_IsException(props)) 37490 return -1; 37491 p = JS_VALUE_GET_OBJ(props); 37492 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0) 37493 goto exception; 37494 for(i = 0; i < len; i++) { 37495 JS_FreeValue(ctx, desc); 37496 desc = JS_GetProperty(ctx, props, atoms[i].atom); 37497 if (JS_IsException(desc)) 37498 goto exception; 37499 if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0) 37500 goto exception; 37501 } 37502 ret = 0; 37503 37504 exception: 37505 js_free_prop_enum(ctx, atoms, len); 37506 JS_FreeValue(ctx, props); 37507 JS_FreeValue(ctx, desc); 37508 return ret; 37509 } 37510 37511 static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target, 37512 int argc, JSValueConst *argv) 37513 { 37514 JSValue ret; 37515 if (!JS_IsUndefined(new_target) && 37516 JS_VALUE_GET_OBJ(new_target) != 37517 JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) { 37518 ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); 37519 } else { 37520 int tag = JS_VALUE_GET_NORM_TAG(argv[0]); 37521 switch(tag) { 37522 case JS_TAG_NULL: 37523 case JS_TAG_UNDEFINED: 37524 ret = JS_NewObject(ctx); 37525 break; 37526 default: 37527 ret = JS_ToObject(ctx, argv[0]); 37528 break; 37529 } 37530 } 37531 return ret; 37532 } 37533 37534 static JSValue js_object_create(JSContext *ctx, JSValueConst this_val, 37535 int argc, JSValueConst *argv) 37536 { 37537 JSValueConst proto, props; 37538 JSValue obj; 37539 37540 proto = argv[0]; 37541 if (!JS_IsObject(proto) && !JS_IsNull(proto)) 37542 return JS_ThrowTypeError(ctx, "not a prototype"); 37543 obj = JS_NewObjectProto(ctx, proto); 37544 if (JS_IsException(obj)) 37545 return JS_EXCEPTION; 37546 props = argv[1]; 37547 if (!JS_IsUndefined(props)) { 37548 if (JS_ObjectDefineProperties(ctx, obj, props)) { 37549 JS_FreeValue(ctx, obj); 37550 return JS_EXCEPTION; 37551 } 37552 } 37553 return obj; 37554 } 37555 37556 static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val, 37557 int argc, JSValueConst *argv, int magic) 37558 { 37559 JSValueConst val; 37560 37561 val = argv[0]; 37562 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) { 37563 /* ES6 feature non compatible with ES5.1: primitive types are 37564 accepted */ 37565 if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL || 37566 JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED) 37567 return JS_ThrowTypeErrorNotAnObject(ctx); 37568 } 37569 return JS_GetPrototype(ctx, val); 37570 } 37571 37572 static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val, 37573 int argc, JSValueConst *argv) 37574 { 37575 JSValueConst obj; 37576 obj = argv[0]; 37577 if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0) 37578 return JS_EXCEPTION; 37579 return JS_DupValue(ctx, obj); 37580 } 37581 37582 /* magic = 1 if called as Reflect.defineProperty */ 37583 static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val, 37584 int argc, JSValueConst *argv, int magic) 37585 { 37586 JSValueConst obj, prop, desc; 37587 int ret, flags; 37588 JSAtom atom; 37589 37590 obj = argv[0]; 37591 prop = argv[1]; 37592 desc = argv[2]; 37593 37594 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 37595 return JS_ThrowTypeErrorNotAnObject(ctx); 37596 atom = JS_ValueToAtom(ctx, prop); 37597 if (unlikely(atom == JS_ATOM_NULL)) 37598 return JS_EXCEPTION; 37599 flags = 0; 37600 if (!magic) 37601 flags |= JS_PROP_THROW; 37602 ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags); 37603 JS_FreeAtom(ctx, atom); 37604 if (ret < 0) { 37605 return JS_EXCEPTION; 37606 } else if (magic) { 37607 return JS_NewBool(ctx, ret); 37608 } else { 37609 return JS_DupValue(ctx, obj); 37610 } 37611 } 37612 37613 static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val, 37614 int argc, JSValueConst *argv) 37615 { 37616 // defineProperties(obj, properties) 37617 JSValueConst obj = argv[0]; 37618 37619 if (JS_ObjectDefineProperties(ctx, obj, argv[1])) 37620 return JS_EXCEPTION; 37621 else 37622 return JS_DupValue(ctx, obj); 37623 } 37624 37625 /* magic = 1 if called as __defineSetter__ */ 37626 static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val, 37627 int argc, JSValueConst *argv, int magic) 37628 { 37629 JSValue obj; 37630 JSValueConst prop, value, get, set; 37631 int ret, flags; 37632 JSAtom atom; 37633 37634 prop = argv[0]; 37635 value = argv[1]; 37636 37637 obj = JS_ToObject(ctx, this_val); 37638 if (JS_IsException(obj)) 37639 return JS_EXCEPTION; 37640 37641 if (check_function(ctx, value)) { 37642 JS_FreeValue(ctx, obj); 37643 return JS_EXCEPTION; 37644 } 37645 atom = JS_ValueToAtom(ctx, prop); 37646 if (unlikely(atom == JS_ATOM_NULL)) { 37647 JS_FreeValue(ctx, obj); 37648 return JS_EXCEPTION; 37649 } 37650 flags = JS_PROP_THROW | 37651 JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE | 37652 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE; 37653 if (magic) { 37654 get = JS_UNDEFINED; 37655 set = value; 37656 flags |= JS_PROP_HAS_SET; 37657 } else { 37658 get = value; 37659 set = JS_UNDEFINED; 37660 flags |= JS_PROP_HAS_GET; 37661 } 37662 ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags); 37663 JS_FreeValue(ctx, obj); 37664 JS_FreeAtom(ctx, atom); 37665 if (ret < 0) { 37666 return JS_EXCEPTION; 37667 } else { 37668 return JS_UNDEFINED; 37669 } 37670 } 37671 37672 static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val, 37673 int argc, JSValueConst *argv, int magic) 37674 { 37675 JSValueConst prop; 37676 JSAtom atom; 37677 JSValue ret, obj; 37678 JSPropertyDescriptor desc; 37679 int res, flags; 37680 37681 if (magic) { 37682 /* Reflect.getOwnPropertyDescriptor case */ 37683 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) 37684 return JS_ThrowTypeErrorNotAnObject(ctx); 37685 obj = JS_DupValue(ctx, argv[0]); 37686 } else { 37687 obj = JS_ToObject(ctx, argv[0]); 37688 if (JS_IsException(obj)) 37689 return obj; 37690 } 37691 prop = argv[1]; 37692 atom = JS_ValueToAtom(ctx, prop); 37693 if (unlikely(atom == JS_ATOM_NULL)) 37694 goto exception; 37695 ret = JS_UNDEFINED; 37696 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 37697 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom); 37698 if (res < 0) 37699 goto exception; 37700 if (res) { 37701 ret = JS_NewObject(ctx); 37702 if (JS_IsException(ret)) 37703 goto exception1; 37704 flags = JS_PROP_C_W_E | JS_PROP_THROW; 37705 if (desc.flags & JS_PROP_GETSET) { 37706 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0 37707 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0) 37708 goto exception1; 37709 } else { 37710 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 37711 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, 37712 JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0) 37713 goto exception1; 37714 } 37715 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, 37716 JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0 37717 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, 37718 JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0) 37719 goto exception1; 37720 js_free_desc(ctx, &desc); 37721 } 37722 } 37723 JS_FreeAtom(ctx, atom); 37724 JS_FreeValue(ctx, obj); 37725 return ret; 37726 37727 exception1: 37728 js_free_desc(ctx, &desc); 37729 JS_FreeValue(ctx, ret); 37730 exception: 37731 JS_FreeAtom(ctx, atom); 37732 JS_FreeValue(ctx, obj); 37733 return JS_EXCEPTION; 37734 } 37735 37736 static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val, 37737 int argc, JSValueConst *argv) 37738 { 37739 //getOwnPropertyDescriptors(obj) 37740 JSValue obj, r; 37741 JSObject *p; 37742 JSPropertyEnum *props; 37743 uint32_t len, i; 37744 37745 r = JS_UNDEFINED; 37746 obj = JS_ToObject(ctx, argv[0]); 37747 if (JS_IsException(obj)) 37748 return JS_EXCEPTION; 37749 37750 p = JS_VALUE_GET_OBJ(obj); 37751 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, 37752 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) 37753 goto exception; 37754 r = JS_NewObject(ctx); 37755 if (JS_IsException(r)) 37756 goto exception; 37757 for(i = 0; i < len; i++) { 37758 JSValue atomValue, desc; 37759 JSValueConst args[2]; 37760 37761 atomValue = JS_AtomToValue(ctx, props[i].atom); 37762 if (JS_IsException(atomValue)) 37763 goto exception; 37764 args[0] = obj; 37765 args[1] = atomValue; 37766 desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0); 37767 JS_FreeValue(ctx, atomValue); 37768 if (JS_IsException(desc)) 37769 goto exception; 37770 if (!JS_IsUndefined(desc)) { 37771 if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc, 37772 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 37773 goto exception; 37774 } 37775 } 37776 js_free_prop_enum(ctx, props, len); 37777 JS_FreeValue(ctx, obj); 37778 return r; 37779 37780 exception: 37781 js_free_prop_enum(ctx, props, len); 37782 JS_FreeValue(ctx, obj); 37783 JS_FreeValue(ctx, r); 37784 return JS_EXCEPTION; 37785 } 37786 37787 static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1, 37788 int flags, int kind) 37789 { 37790 JSValue obj, r, val, key, value; 37791 JSObject *p; 37792 JSPropertyEnum *atoms; 37793 uint32_t len, i, j; 37794 37795 r = JS_UNDEFINED; 37796 val = JS_UNDEFINED; 37797 obj = JS_ToObject(ctx, obj1); 37798 if (JS_IsException(obj)) 37799 return JS_EXCEPTION; 37800 p = JS_VALUE_GET_OBJ(obj); 37801 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY)) 37802 goto exception; 37803 r = JS_NewArray(ctx); 37804 if (JS_IsException(r)) 37805 goto exception; 37806 for(j = i = 0; i < len; i++) { 37807 JSAtom atom = atoms[i].atom; 37808 if (flags & JS_GPN_ENUM_ONLY) { 37809 JSPropertyDescriptor desc; 37810 int res; 37811 37812 /* Check if property is still enumerable */ 37813 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 37814 if (res < 0) 37815 goto exception; 37816 if (!res) 37817 continue; 37818 js_free_desc(ctx, &desc); 37819 if (!(desc.flags & JS_PROP_ENUMERABLE)) 37820 continue; 37821 } 37822 switch(kind) { 37823 default: 37824 case JS_ITERATOR_KIND_KEY: 37825 val = JS_AtomToValue(ctx, atom); 37826 if (JS_IsException(val)) 37827 goto exception; 37828 break; 37829 case JS_ITERATOR_KIND_VALUE: 37830 val = JS_GetProperty(ctx, obj, atom); 37831 if (JS_IsException(val)) 37832 goto exception; 37833 break; 37834 case JS_ITERATOR_KIND_KEY_AND_VALUE: 37835 val = JS_NewArray(ctx); 37836 if (JS_IsException(val)) 37837 goto exception; 37838 key = JS_AtomToValue(ctx, atom); 37839 if (JS_IsException(key)) 37840 goto exception1; 37841 if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0) 37842 goto exception1; 37843 value = JS_GetProperty(ctx, obj, atom); 37844 if (JS_IsException(value)) 37845 goto exception1; 37846 if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0) 37847 goto exception1; 37848 break; 37849 } 37850 if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0) 37851 goto exception; 37852 } 37853 goto done; 37854 37855 exception1: 37856 JS_FreeValue(ctx, val); 37857 exception: 37858 JS_FreeValue(ctx, r); 37859 r = JS_EXCEPTION; 37860 done: 37861 js_free_prop_enum(ctx, atoms, len); 37862 JS_FreeValue(ctx, obj); 37863 return r; 37864 } 37865 37866 static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val, 37867 int argc, JSValueConst *argv) 37868 { 37869 return JS_GetOwnPropertyNames2(ctx, argv[0], 37870 JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY); 37871 } 37872 37873 static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val, 37874 int argc, JSValueConst *argv) 37875 { 37876 return JS_GetOwnPropertyNames2(ctx, argv[0], 37877 JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY); 37878 } 37879 37880 static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val, 37881 int argc, JSValueConst *argv, int kind) 37882 { 37883 return JS_GetOwnPropertyNames2(ctx, argv[0], 37884 JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind); 37885 } 37886 37887 static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val, 37888 int argc, JSValueConst *argv, int reflect) 37889 { 37890 JSValueConst obj; 37891 int ret; 37892 37893 obj = argv[0]; 37894 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 37895 if (reflect) 37896 return JS_ThrowTypeErrorNotAnObject(ctx); 37897 else 37898 return JS_FALSE; 37899 } 37900 ret = JS_IsExtensible(ctx, obj); 37901 if (ret < 0) 37902 return JS_EXCEPTION; 37903 else 37904 return JS_NewBool(ctx, ret); 37905 } 37906 37907 static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val, 37908 int argc, JSValueConst *argv, int reflect) 37909 { 37910 JSValueConst obj; 37911 int ret; 37912 37913 obj = argv[0]; 37914 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { 37915 if (reflect) 37916 return JS_ThrowTypeErrorNotAnObject(ctx); 37917 else 37918 return JS_DupValue(ctx, obj); 37919 } 37920 ret = JS_PreventExtensions(ctx, obj); 37921 if (ret < 0) 37922 return JS_EXCEPTION; 37923 if (reflect) { 37924 return JS_NewBool(ctx, ret); 37925 } else { 37926 if (!ret) 37927 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); 37928 return JS_DupValue(ctx, obj); 37929 } 37930 } 37931 37932 static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, 37933 int argc, JSValueConst *argv) 37934 { 37935 JSValue obj; 37936 JSAtom atom; 37937 JSObject *p; 37938 BOOL ret; 37939 37940 atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */ 37941 if (unlikely(atom == JS_ATOM_NULL)) 37942 return JS_EXCEPTION; 37943 obj = JS_ToObject(ctx, this_val); 37944 if (JS_IsException(obj)) { 37945 JS_FreeAtom(ctx, atom); 37946 return obj; 37947 } 37948 p = JS_VALUE_GET_OBJ(obj); 37949 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); 37950 JS_FreeAtom(ctx, atom); 37951 JS_FreeValue(ctx, obj); 37952 if (ret < 0) 37953 return JS_EXCEPTION; 37954 else 37955 return JS_NewBool(ctx, ret); 37956 } 37957 37958 static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, 37959 int argc, JSValueConst *argv) 37960 { 37961 JSValue obj; 37962 JSAtom atom; 37963 JSObject *p; 37964 BOOL ret; 37965 37966 obj = JS_ToObject(ctx, argv[0]); 37967 if (JS_IsException(obj)) 37968 return obj; 37969 atom = JS_ValueToAtom(ctx, argv[1]); 37970 if (unlikely(atom == JS_ATOM_NULL)) { 37971 JS_FreeValue(ctx, obj); 37972 return JS_EXCEPTION; 37973 } 37974 p = JS_VALUE_GET_OBJ(obj); 37975 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); 37976 JS_FreeAtom(ctx, atom); 37977 JS_FreeValue(ctx, obj); 37978 if (ret < 0) 37979 return JS_EXCEPTION; 37980 else 37981 return JS_NewBool(ctx, ret); 37982 } 37983 37984 static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, 37985 int argc, JSValueConst *argv) 37986 { 37987 return JS_ToObject(ctx, this_val); 37988 } 37989 37990 static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val, 37991 int argc, JSValueConst *argv) 37992 { 37993 JSValue obj, tag; 37994 int is_array; 37995 JSAtom atom; 37996 JSObject *p; 37997 37998 if (JS_IsNull(this_val)) { 37999 tag = JS_NewString(ctx, "Null"); 38000 } else if (JS_IsUndefined(this_val)) { 38001 tag = JS_NewString(ctx, "Undefined"); 38002 } else { 38003 obj = JS_ToObject(ctx, this_val); 38004 if (JS_IsException(obj)) 38005 return obj; 38006 is_array = JS_IsArray(ctx, obj); 38007 if (is_array < 0) { 38008 JS_FreeValue(ctx, obj); 38009 return JS_EXCEPTION; 38010 } 38011 if (is_array) { 38012 atom = JS_ATOM_Array; 38013 } else if (JS_IsFunction(ctx, obj)) { 38014 atom = JS_ATOM_Function; 38015 } else { 38016 p = JS_VALUE_GET_OBJ(obj); 38017 switch(p->class_id) { 38018 case JS_CLASS_STRING: 38019 case JS_CLASS_ARGUMENTS: 38020 case JS_CLASS_MAPPED_ARGUMENTS: 38021 case JS_CLASS_ERROR: 38022 case JS_CLASS_BOOLEAN: 38023 case JS_CLASS_NUMBER: 38024 case JS_CLASS_DATE: 38025 case JS_CLASS_REGEXP: 38026 atom = ctx->rt->class_array[p->class_id].class_name; 38027 break; 38028 default: 38029 atom = JS_ATOM_Object; 38030 break; 38031 } 38032 } 38033 tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag); 38034 JS_FreeValue(ctx, obj); 38035 if (JS_IsException(tag)) 38036 return JS_EXCEPTION; 38037 if (!JS_IsString(tag)) { 38038 JS_FreeValue(ctx, tag); 38039 tag = JS_AtomToString(ctx, atom); 38040 } 38041 } 38042 return JS_ConcatString3(ctx, "[object ", tag, "]"); 38043 } 38044 38045 static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val, 38046 int argc, JSValueConst *argv) 38047 { 38048 return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL); 38049 } 38050 38051 static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val, 38052 int argc, JSValueConst *argv) 38053 { 38054 // Object.assign(obj, source1) 38055 JSValue obj, s; 38056 int i; 38057 38058 s = JS_UNDEFINED; 38059 obj = JS_ToObject(ctx, argv[0]); 38060 if (JS_IsException(obj)) 38061 goto exception; 38062 for (i = 1; i < argc; i++) { 38063 if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) { 38064 s = JS_ToObject(ctx, argv[i]); 38065 if (JS_IsException(s)) 38066 goto exception; 38067 if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE)) 38068 goto exception; 38069 JS_FreeValue(ctx, s); 38070 } 38071 } 38072 return obj; 38073 exception: 38074 JS_FreeValue(ctx, obj); 38075 JS_FreeValue(ctx, s); 38076 return JS_EXCEPTION; 38077 } 38078 38079 static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val, 38080 int argc, JSValueConst *argv, int freeze_flag) 38081 { 38082 JSValueConst obj = argv[0]; 38083 JSObject *p; 38084 JSPropertyEnum *props; 38085 uint32_t len, i; 38086 int flags, desc_flags, res; 38087 38088 if (!JS_IsObject(obj)) 38089 return JS_DupValue(ctx, obj); 38090 38091 res = JS_PreventExtensions(ctx, obj); 38092 if (res < 0) 38093 return JS_EXCEPTION; 38094 if (!res) { 38095 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false"); 38096 } 38097 38098 p = JS_VALUE_GET_OBJ(obj); 38099 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; 38100 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) 38101 return JS_EXCEPTION; 38102 38103 for(i = 0; i < len; i++) { 38104 JSPropertyDescriptor desc; 38105 JSAtom prop = props[i].atom; 38106 38107 desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE; 38108 if (freeze_flag) { 38109 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 38110 if (res < 0) 38111 goto exception; 38112 if (res) { 38113 if (desc.flags & JS_PROP_WRITABLE) 38114 desc_flags |= JS_PROP_HAS_WRITABLE; 38115 js_free_desc(ctx, &desc); 38116 } 38117 } 38118 if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED, 38119 JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0) 38120 goto exception; 38121 } 38122 js_free_prop_enum(ctx, props, len); 38123 return JS_DupValue(ctx, obj); 38124 38125 exception: 38126 js_free_prop_enum(ctx, props, len); 38127 return JS_EXCEPTION; 38128 } 38129 38130 static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val, 38131 int argc, JSValueConst *argv, int is_frozen) 38132 { 38133 JSValueConst obj = argv[0]; 38134 JSObject *p; 38135 JSPropertyEnum *props; 38136 uint32_t len, i; 38137 int flags, res; 38138 38139 if (!JS_IsObject(obj)) 38140 return JS_TRUE; 38141 38142 p = JS_VALUE_GET_OBJ(obj); 38143 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK; 38144 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags)) 38145 return JS_EXCEPTION; 38146 38147 for(i = 0; i < len; i++) { 38148 JSPropertyDescriptor desc; 38149 JSAtom prop = props[i].atom; 38150 38151 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 38152 if (res < 0) 38153 goto exception; 38154 if (res) { 38155 js_free_desc(ctx, &desc); 38156 if ((desc.flags & JS_PROP_CONFIGURABLE) 38157 || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) { 38158 res = FALSE; 38159 goto done; 38160 } 38161 } 38162 } 38163 res = JS_IsExtensible(ctx, obj); 38164 if (res < 0) 38165 return JS_EXCEPTION; 38166 res ^= 1; 38167 done: 38168 js_free_prop_enum(ctx, props, len); 38169 return JS_NewBool(ctx, res); 38170 38171 exception: 38172 js_free_prop_enum(ctx, props, len); 38173 return JS_EXCEPTION; 38174 } 38175 38176 static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val, 38177 int argc, JSValueConst *argv) 38178 { 38179 JSValue obj, iter, next_method = JS_UNDEFINED; 38180 JSValueConst iterable; 38181 BOOL done; 38182 38183 /* RequireObjectCoercible() not necessary because it is tested in 38184 JS_GetIterator() by JS_GetProperty() */ 38185 iterable = argv[0]; 38186 38187 obj = JS_NewObject(ctx); 38188 if (JS_IsException(obj)) 38189 return obj; 38190 38191 iter = JS_GetIterator(ctx, iterable, FALSE); 38192 if (JS_IsException(iter)) 38193 goto fail; 38194 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 38195 if (JS_IsException(next_method)) 38196 goto fail; 38197 38198 for(;;) { 38199 JSValue key, value, item; 38200 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 38201 if (JS_IsException(item)) 38202 goto fail; 38203 if (done) { 38204 JS_FreeValue(ctx, item); 38205 break; 38206 } 38207 38208 key = JS_UNDEFINED; 38209 value = JS_UNDEFINED; 38210 if (!JS_IsObject(item)) { 38211 JS_ThrowTypeErrorNotAnObject(ctx); 38212 goto fail1; 38213 } 38214 key = JS_GetPropertyUint32(ctx, item, 0); 38215 if (JS_IsException(key)) 38216 goto fail1; 38217 value = JS_GetPropertyUint32(ctx, item, 1); 38218 if (JS_IsException(value)) { 38219 JS_FreeValue(ctx, key); 38220 goto fail1; 38221 } 38222 if (JS_DefinePropertyValueValue(ctx, obj, key, value, 38223 JS_PROP_C_W_E | JS_PROP_THROW) < 0) { 38224 fail1: 38225 JS_FreeValue(ctx, item); 38226 goto fail; 38227 } 38228 JS_FreeValue(ctx, item); 38229 } 38230 JS_FreeValue(ctx, next_method); 38231 JS_FreeValue(ctx, iter); 38232 return obj; 38233 fail: 38234 if (JS_IsObject(iter)) { 38235 /* close the iterator object, preserving pending exception */ 38236 JS_IteratorClose(ctx, iter, TRUE); 38237 } 38238 JS_FreeValue(ctx, next_method); 38239 JS_FreeValue(ctx, iter); 38240 JS_FreeValue(ctx, obj); 38241 return JS_EXCEPTION; 38242 } 38243 38244 #if 0 38245 /* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */ 38246 static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val, 38247 int argc, JSValueConst *argv) 38248 { 38249 int ret; 38250 ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]), 38251 JS_DupValue(ctx, argv[2]), 38252 JS_PROP_C_W_E | JS_PROP_THROW); 38253 if (ret < 0) 38254 return JS_EXCEPTION; 38255 else 38256 return JS_NewBool(ctx, ret); 38257 } 38258 38259 static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val, 38260 int argc, JSValueConst *argv) 38261 { 38262 return JS_ToObject(ctx, argv[0]); 38263 } 38264 38265 static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val, 38266 int argc, JSValueConst *argv) 38267 { 38268 int hint = HINT_NONE; 38269 38270 if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT) 38271 hint = JS_VALUE_GET_INT(argv[1]); 38272 38273 return JS_ToPrimitive(ctx, argv[0], hint); 38274 } 38275 #endif 38276 38277 /* return an empty string if not an object */ 38278 static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val, 38279 int argc, JSValueConst *argv) 38280 { 38281 JSAtom atom; 38282 JSObject *p; 38283 uint32_t tag; 38284 int class_id; 38285 38286 tag = JS_VALUE_GET_NORM_TAG(argv[0]); 38287 if (tag == JS_TAG_OBJECT) { 38288 p = JS_VALUE_GET_OBJ(argv[0]); 38289 class_id = p->class_id; 38290 if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0])) 38291 class_id = JS_CLASS_BYTECODE_FUNCTION; 38292 atom = ctx->rt->class_array[class_id].class_name; 38293 } else { 38294 atom = JS_ATOM_empty_string; 38295 } 38296 return JS_AtomToString(ctx, atom); 38297 } 38298 38299 static JSValue js_object_is(JSContext *ctx, JSValueConst this_val, 38300 int argc, JSValueConst *argv) 38301 { 38302 return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1])); 38303 } 38304 38305 #if 0 38306 static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val, 38307 int argc, JSValueConst *argv) 38308 { 38309 return JS_GetObjectData(ctx, argv[0]); 38310 } 38311 38312 static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val, 38313 int argc, JSValueConst *argv) 38314 { 38315 if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1]))) 38316 return JS_EXCEPTION; 38317 return JS_DupValue(ctx, argv[1]); 38318 } 38319 38320 static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val, 38321 int argc, JSValueConst *argv) 38322 { 38323 return JS_ToPropertyKey(ctx, argv[0]); 38324 } 38325 38326 static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val, 38327 int argc, JSValueConst *argv) 38328 { 38329 return JS_NewBool(ctx, JS_IsObject(argv[0])); 38330 } 38331 38332 static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val, 38333 int argc, JSValueConst *argv) 38334 { 38335 return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1])); 38336 } 38337 38338 static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val, 38339 int argc, JSValueConst *argv) 38340 { 38341 return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0])); 38342 } 38343 #endif 38344 38345 static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj, 38346 JSValueConst defaultConstructor) 38347 { 38348 JSValue ctor, species; 38349 38350 if (!JS_IsObject(obj)) 38351 return JS_ThrowTypeErrorNotAnObject(ctx); 38352 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); 38353 if (JS_IsException(ctor)) 38354 return ctor; 38355 if (JS_IsUndefined(ctor)) 38356 return JS_DupValue(ctx, defaultConstructor); 38357 if (!JS_IsObject(ctor)) { 38358 JS_FreeValue(ctx, ctor); 38359 return JS_ThrowTypeErrorNotAnObject(ctx); 38360 } 38361 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); 38362 JS_FreeValue(ctx, ctor); 38363 if (JS_IsException(species)) 38364 return species; 38365 if (JS_IsUndefined(species) || JS_IsNull(species)) 38366 return JS_DupValue(ctx, defaultConstructor); 38367 if (!JS_IsConstructor(ctx, species)) { 38368 JS_FreeValue(ctx, species); 38369 return JS_ThrowTypeError(ctx, "not a constructor"); 38370 } 38371 return species; 38372 } 38373 38374 #if 0 38375 static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val, 38376 int argc, JSValueConst *argv) 38377 { 38378 return JS_SpeciesConstructor(ctx, argv[0], argv[1]); 38379 } 38380 #endif 38381 38382 static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val) 38383 { 38384 JSValue val, ret; 38385 38386 val = JS_ToObject(ctx, this_val); 38387 if (JS_IsException(val)) 38388 return val; 38389 ret = JS_GetPrototype(ctx, val); 38390 JS_FreeValue(ctx, val); 38391 return ret; 38392 } 38393 38394 static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val, 38395 JSValueConst proto) 38396 { 38397 if (JS_IsUndefined(this_val) || JS_IsNull(this_val)) 38398 return JS_ThrowTypeErrorNotAnObject(ctx); 38399 if (!JS_IsObject(proto) && !JS_IsNull(proto)) 38400 return JS_UNDEFINED; 38401 if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0) 38402 return JS_EXCEPTION; 38403 else 38404 return JS_UNDEFINED; 38405 } 38406 38407 static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val, 38408 int argc, JSValueConst *argv) 38409 { 38410 JSValue obj, v1; 38411 JSValueConst v; 38412 int res; 38413 38414 v = argv[0]; 38415 if (!JS_IsObject(v)) 38416 return JS_FALSE; 38417 obj = JS_ToObject(ctx, this_val); 38418 if (JS_IsException(obj)) 38419 return JS_EXCEPTION; 38420 v1 = JS_DupValue(ctx, v); 38421 for(;;) { 38422 v1 = JS_GetPrototypeFree(ctx, v1); 38423 if (JS_IsException(v1)) 38424 goto exception; 38425 if (JS_IsNull(v1)) { 38426 res = FALSE; 38427 break; 38428 } 38429 if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) { 38430 res = TRUE; 38431 break; 38432 } 38433 /* avoid infinite loop (possible with proxies) */ 38434 if (js_poll_interrupts(ctx)) 38435 goto exception; 38436 } 38437 JS_FreeValue(ctx, v1); 38438 JS_FreeValue(ctx, obj); 38439 return JS_NewBool(ctx, res); 38440 38441 exception: 38442 JS_FreeValue(ctx, v1); 38443 JS_FreeValue(ctx, obj); 38444 return JS_EXCEPTION; 38445 } 38446 38447 static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val, 38448 int argc, JSValueConst *argv) 38449 { 38450 JSValue obj, res = JS_EXCEPTION; 38451 JSAtom prop = JS_ATOM_NULL; 38452 JSPropertyDescriptor desc; 38453 int has_prop; 38454 38455 obj = JS_ToObject(ctx, this_val); 38456 if (JS_IsException(obj)) 38457 goto exception; 38458 prop = JS_ValueToAtom(ctx, argv[0]); 38459 if (unlikely(prop == JS_ATOM_NULL)) 38460 goto exception; 38461 38462 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); 38463 if (has_prop < 0) 38464 goto exception; 38465 if (has_prop) { 38466 res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE); 38467 js_free_desc(ctx, &desc); 38468 } else { 38469 res = JS_FALSE; 38470 } 38471 38472 exception: 38473 JS_FreeAtom(ctx, prop); 38474 JS_FreeValue(ctx, obj); 38475 return res; 38476 } 38477 38478 static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val, 38479 int argc, JSValueConst *argv, int setter) 38480 { 38481 JSValue obj, res = JS_EXCEPTION; 38482 JSAtom prop = JS_ATOM_NULL; 38483 JSPropertyDescriptor desc; 38484 int has_prop; 38485 38486 obj = JS_ToObject(ctx, this_val); 38487 if (JS_IsException(obj)) 38488 goto exception; 38489 prop = JS_ValueToAtom(ctx, argv[0]); 38490 if (unlikely(prop == JS_ATOM_NULL)) 38491 goto exception; 38492 38493 for (;;) { 38494 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop); 38495 if (has_prop < 0) 38496 goto exception; 38497 if (has_prop) { 38498 if (desc.flags & JS_PROP_GETSET) 38499 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter); 38500 else 38501 res = JS_UNDEFINED; 38502 js_free_desc(ctx, &desc); 38503 break; 38504 } 38505 obj = JS_GetPrototypeFree(ctx, obj); 38506 if (JS_IsException(obj)) 38507 goto exception; 38508 if (JS_IsNull(obj)) { 38509 res = JS_UNDEFINED; 38510 break; 38511 } 38512 /* avoid infinite loop (possible with proxies) */ 38513 if (js_poll_interrupts(ctx)) 38514 goto exception; 38515 } 38516 38517 exception: 38518 JS_FreeAtom(ctx, prop); 38519 JS_FreeValue(ctx, obj); 38520 return res; 38521 } 38522 38523 static const JSCFunctionListEntry js_object_funcs[] = { 38524 JS_CFUNC_DEF("create", 2, js_object_create ), 38525 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), 38526 JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ), 38527 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ), 38528 JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), 38529 JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), 38530 JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), 38531 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ), 38532 JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), 38533 JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), 38534 JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), 38535 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ), 38536 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ), 38537 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ), 38538 JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ), 38539 JS_CFUNC_DEF("is", 2, js_object_is ), 38540 JS_CFUNC_DEF("assign", 2, js_object_assign ), 38541 JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ), 38542 JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ), 38543 JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ), 38544 JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ), 38545 JS_CFUNC_DEF("__getClass", 1, js_object___getClass ), 38546 //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ), 38547 //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ), 38548 //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ), 38549 //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ), 38550 //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ), 38551 //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ), 38552 //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ), 38553 //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ), 38554 //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ), 38555 //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ), 38556 JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ), 38557 JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ), 38558 }; 38559 38560 static const JSCFunctionListEntry js_object_proto_funcs[] = { 38561 JS_CFUNC_DEF("toString", 0, js_object_toString ), 38562 JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ), 38563 JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ), 38564 JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ), 38565 JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ), 38566 JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ), 38567 JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ), 38568 JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ), 38569 JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ), 38570 JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ), 38571 JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ), 38572 }; 38573 38574 /* Function class */ 38575 38576 static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val, 38577 int argc, JSValueConst *argv) 38578 { 38579 return JS_UNDEFINED; 38580 } 38581 38582 /* XXX: add a specific eval mode so that Function("}), ({") is rejected */ 38583 static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, 38584 int argc, JSValueConst *argv, int magic) 38585 { 38586 JSFunctionKindEnum func_kind = magic; 38587 int i, n, ret; 38588 JSValue s, proto, obj = JS_UNDEFINED; 38589 StringBuffer b_s, *b = &b_s; 38590 38591 string_buffer_init(ctx, b, 0); 38592 string_buffer_putc8(b, '('); 38593 38594 if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) { 38595 string_buffer_puts8(b, "async "); 38596 } 38597 string_buffer_puts8(b, "function"); 38598 38599 if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) { 38600 string_buffer_putc8(b, '*'); 38601 } 38602 string_buffer_puts8(b, " anonymous("); 38603 38604 n = argc - 1; 38605 for(i = 0; i < n; i++) { 38606 if (i != 0) { 38607 string_buffer_putc8(b, ','); 38608 } 38609 if (string_buffer_concat_value(b, argv[i])) 38610 goto fail; 38611 } 38612 string_buffer_puts8(b, "\n) {\n"); 38613 if (n >= 0) { 38614 if (string_buffer_concat_value(b, argv[n])) 38615 goto fail; 38616 } 38617 string_buffer_puts8(b, "\n})"); 38618 s = string_buffer_end(b); 38619 if (JS_IsException(s)) 38620 goto fail1; 38621 38622 obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1); 38623 JS_FreeValue(ctx, s); 38624 if (JS_IsException(obj)) 38625 goto fail1; 38626 if (!JS_IsUndefined(new_target)) { 38627 /* set the prototype */ 38628 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); 38629 if (JS_IsException(proto)) 38630 goto fail1; 38631 if (!JS_IsObject(proto)) { 38632 JSContext *realm; 38633 JS_FreeValue(ctx, proto); 38634 realm = JS_GetFunctionRealm(ctx, new_target); 38635 if (!realm) 38636 goto fail1; 38637 proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]); 38638 } 38639 ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE); 38640 JS_FreeValue(ctx, proto); 38641 if (ret < 0) 38642 goto fail1; 38643 } 38644 return obj; 38645 38646 fail: 38647 string_buffer_free(b); 38648 fail1: 38649 JS_FreeValue(ctx, obj); 38650 return JS_EXCEPTION; 38651 } 38652 38653 static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, 38654 JSValueConst obj) 38655 { 38656 JSValue len_val; 38657 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); 38658 if (JS_IsException(len_val)) { 38659 *pres = 0; 38660 return -1; 38661 } 38662 return JS_ToUint32Free(ctx, pres, len_val); 38663 } 38664 38665 static __exception int js_get_length64(JSContext *ctx, int64_t *pres, 38666 JSValueConst obj) 38667 { 38668 JSValue len_val; 38669 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length); 38670 if (JS_IsException(len_val)) { 38671 *pres = 0; 38672 return -1; 38673 } 38674 return JS_ToLengthFree(ctx, pres, len_val); 38675 } 38676 38677 static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len) 38678 { 38679 uint32_t i; 38680 for(i = 0; i < len; i++) { 38681 JS_FreeValue(ctx, tab[i]); 38682 } 38683 js_free(ctx, tab); 38684 } 38685 38686 /* XXX: should use ValueArray */ 38687 static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, 38688 JSValueConst array_arg) 38689 { 38690 uint32_t len, i; 38691 JSValue *tab, ret; 38692 JSObject *p; 38693 38694 if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) { 38695 JS_ThrowTypeError(ctx, "not a object"); 38696 return NULL; 38697 } 38698 if (js_get_length32(ctx, &len, array_arg)) 38699 return NULL; 38700 if (len > JS_MAX_LOCAL_VARS) { 38701 // XXX: check for stack overflow? 38702 JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", 38703 JS_MAX_LOCAL_VARS); 38704 return NULL; 38705 } 38706 /* avoid allocating 0 bytes */ 38707 tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len)); 38708 if (!tab) 38709 return NULL; 38710 p = JS_VALUE_GET_OBJ(array_arg); 38711 if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) && 38712 p->fast_array && 38713 len == p->u.array.count) { 38714 for(i = 0; i < len; i++) { 38715 tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]); 38716 } 38717 } else { 38718 for(i = 0; i < len; i++) { 38719 ret = JS_GetPropertyUint32(ctx, array_arg, i); 38720 if (JS_IsException(ret)) { 38721 free_arg_list(ctx, tab, i); 38722 return NULL; 38723 } 38724 tab[i] = ret; 38725 } 38726 } 38727 *plen = len; 38728 return tab; 38729 } 38730 38731 /* magic value: 0 = normal apply, 1 = apply for constructor, 2 = 38732 Reflect.apply */ 38733 static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, 38734 int argc, JSValueConst *argv, int magic) 38735 { 38736 JSValueConst this_arg, array_arg; 38737 uint32_t len; 38738 JSValue *tab, ret; 38739 38740 if (check_function(ctx, this_val)) 38741 return JS_EXCEPTION; 38742 this_arg = argv[0]; 38743 array_arg = argv[1]; 38744 if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED || 38745 JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) { 38746 return JS_Call(ctx, this_val, this_arg, 0, NULL); 38747 } 38748 tab = build_arg_list(ctx, &len, array_arg); 38749 if (!tab) 38750 return JS_EXCEPTION; 38751 if (magic & 1) { 38752 ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); 38753 } else { 38754 ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); 38755 } 38756 free_arg_list(ctx, tab, len); 38757 return ret; 38758 } 38759 38760 static JSValue js_function_call(JSContext *ctx, JSValueConst this_val, 38761 int argc, JSValueConst *argv) 38762 { 38763 if (argc <= 0) { 38764 return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL); 38765 } else { 38766 return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1); 38767 } 38768 } 38769 38770 static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val, 38771 int argc, JSValueConst *argv) 38772 { 38773 JSBoundFunction *bf; 38774 JSValue func_obj, name1, len_val; 38775 JSObject *p; 38776 int arg_count, i, ret; 38777 38778 if (check_function(ctx, this_val)) 38779 return JS_EXCEPTION; 38780 38781 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 38782 JS_CLASS_BOUND_FUNCTION); 38783 if (JS_IsException(func_obj)) 38784 return JS_EXCEPTION; 38785 p = JS_VALUE_GET_OBJ(func_obj); 38786 p->is_constructor = JS_IsConstructor(ctx, this_val); 38787 arg_count = max_int(0, argc - 1); 38788 bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue)); 38789 if (!bf) 38790 goto exception; 38791 bf->func_obj = JS_DupValue(ctx, this_val); 38792 bf->this_val = JS_DupValue(ctx, argv[0]); 38793 bf->argc = arg_count; 38794 for(i = 0; i < arg_count; i++) { 38795 bf->argv[i] = JS_DupValue(ctx, argv[i + 1]); 38796 } 38797 p->u.bound_function = bf; 38798 38799 /* XXX: the spec could be simpler by only using GetOwnProperty */ 38800 ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length); 38801 if (ret < 0) 38802 goto exception; 38803 if (!ret) { 38804 len_val = JS_NewInt32(ctx, 0); 38805 } else { 38806 len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length); 38807 if (JS_IsException(len_val)) 38808 goto exception; 38809 if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) { 38810 /* most common case */ 38811 int len1 = JS_VALUE_GET_INT(len_val); 38812 if (len1 <= arg_count) 38813 len1 = 0; 38814 else 38815 len1 -= arg_count; 38816 len_val = JS_NewInt32(ctx, len1); 38817 } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) { 38818 double d = JS_VALUE_GET_FLOAT64(len_val); 38819 if (isnan(d)) { 38820 d = 0.0; 38821 } else { 38822 d = trunc(d); 38823 if (d <= (double)arg_count) 38824 d = 0.0; 38825 else 38826 d -= (double)arg_count; /* also converts -0 to +0 */ 38827 } 38828 len_val = JS_NewFloat64(ctx, d); 38829 } else { 38830 JS_FreeValue(ctx, len_val); 38831 len_val = JS_NewInt32(ctx, 0); 38832 } 38833 } 38834 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, 38835 len_val, JS_PROP_CONFIGURABLE); 38836 38837 name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name); 38838 if (JS_IsException(name1)) 38839 goto exception; 38840 if (!JS_IsString(name1)) { 38841 JS_FreeValue(ctx, name1); 38842 name1 = JS_AtomToString(ctx, JS_ATOM_empty_string); 38843 } 38844 name1 = JS_ConcatString3(ctx, "bound ", name1, ""); 38845 if (JS_IsException(name1)) 38846 goto exception; 38847 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1, 38848 JS_PROP_CONFIGURABLE); 38849 return func_obj; 38850 exception: 38851 JS_FreeValue(ctx, func_obj); 38852 return JS_EXCEPTION; 38853 } 38854 38855 static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val, 38856 int argc, JSValueConst *argv) 38857 { 38858 JSObject *p; 38859 JSFunctionKindEnum func_kind = JS_FUNC_NORMAL; 38860 38861 if (check_function(ctx, this_val)) 38862 return JS_EXCEPTION; 38863 38864 p = JS_VALUE_GET_OBJ(this_val); 38865 if (js_class_has_bytecode(p->class_id)) { 38866 JSFunctionBytecode *b = p->u.func.function_bytecode; 38867 if (b->has_debug && b->debug.source) { 38868 return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len); 38869 } 38870 func_kind = b->func_kind; 38871 } 38872 { 38873 JSValue name; 38874 const char *pref, *suff; 38875 38876 switch(func_kind) { 38877 default: 38878 case JS_FUNC_NORMAL: 38879 pref = "function "; 38880 break; 38881 case JS_FUNC_GENERATOR: 38882 pref = "function *"; 38883 break; 38884 case JS_FUNC_ASYNC: 38885 pref = "async function "; 38886 break; 38887 case JS_FUNC_ASYNC_GENERATOR: 38888 pref = "async function *"; 38889 break; 38890 } 38891 suff = "() {\n [native code]\n}"; 38892 name = JS_GetProperty(ctx, this_val, JS_ATOM_name); 38893 if (JS_IsUndefined(name)) 38894 name = JS_AtomToString(ctx, JS_ATOM_empty_string); 38895 return JS_ConcatString3(ctx, pref, name, suff); 38896 } 38897 } 38898 38899 static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val, 38900 int argc, JSValueConst *argv) 38901 { 38902 int ret; 38903 ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val); 38904 if (ret < 0) 38905 return JS_EXCEPTION; 38906 else 38907 return JS_NewBool(ctx, ret); 38908 } 38909 38910 static const JSCFunctionListEntry js_function_proto_funcs[] = { 38911 JS_CFUNC_DEF("call", 1, js_function_call ), 38912 JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ), 38913 JS_CFUNC_DEF("bind", 1, js_function_bind ), 38914 JS_CFUNC_DEF("toString", 0, js_function_toString ), 38915 JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ), 38916 JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ), 38917 JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ), 38918 }; 38919 38920 /* Error class */ 38921 38922 static JSValue iterator_to_array(JSContext *ctx, JSValueConst items) 38923 { 38924 JSValue iter, next_method = JS_UNDEFINED; 38925 JSValue v, r = JS_UNDEFINED; 38926 int64_t k; 38927 BOOL done; 38928 38929 iter = JS_GetIterator(ctx, items, FALSE); 38930 if (JS_IsException(iter)) 38931 goto exception; 38932 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 38933 if (JS_IsException(next_method)) 38934 goto exception; 38935 r = JS_NewArray(ctx); 38936 if (JS_IsException(r)) 38937 goto exception; 38938 for (k = 0;; k++) { 38939 v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 38940 if (JS_IsException(v)) 38941 goto exception_close; 38942 if (done) 38943 break; 38944 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 38945 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 38946 goto exception_close; 38947 } 38948 done: 38949 JS_FreeValue(ctx, next_method); 38950 JS_FreeValue(ctx, iter); 38951 return r; 38952 exception_close: 38953 JS_IteratorClose(ctx, iter, TRUE); 38954 exception: 38955 JS_FreeValue(ctx, r); 38956 r = JS_EXCEPTION; 38957 goto done; 38958 } 38959 38960 static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, 38961 int argc, JSValueConst *argv, int magic) 38962 { 38963 JSValue obj, msg, proto; 38964 JSValueConst message, options; 38965 int arg_index; 38966 38967 if (JS_IsUndefined(new_target)) 38968 new_target = JS_GetActiveFunction(ctx); 38969 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype); 38970 if (JS_IsException(proto)) 38971 return proto; 38972 if (!JS_IsObject(proto)) { 38973 JSContext *realm; 38974 JSValueConst proto1; 38975 38976 JS_FreeValue(ctx, proto); 38977 realm = JS_GetFunctionRealm(ctx, new_target); 38978 if (!realm) 38979 return JS_EXCEPTION; 38980 if (magic < 0) { 38981 proto1 = realm->class_proto[JS_CLASS_ERROR]; 38982 } else { 38983 proto1 = realm->native_error_proto[magic]; 38984 } 38985 proto = JS_DupValue(ctx, proto1); 38986 } 38987 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR); 38988 JS_FreeValue(ctx, proto); 38989 if (JS_IsException(obj)) 38990 return obj; 38991 arg_index = (magic == JS_AGGREGATE_ERROR); 38992 38993 message = argv[arg_index++]; 38994 if (!JS_IsUndefined(message)) { 38995 msg = JS_ToString(ctx, message); 38996 if (unlikely(JS_IsException(msg))) 38997 goto exception; 38998 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, 38999 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 39000 } 39001 39002 if (arg_index < argc) { 39003 options = argv[arg_index]; 39004 if (JS_IsObject(options)) { 39005 int present = JS_HasProperty(ctx, options, JS_ATOM_cause); 39006 if (present < 0) 39007 goto exception; 39008 if (present) { 39009 JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause); 39010 if (JS_IsException(cause)) 39011 goto exception; 39012 JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause, 39013 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 39014 } 39015 } 39016 } 39017 39018 if (magic == JS_AGGREGATE_ERROR) { 39019 JSValue error_list = iterator_to_array(ctx, argv[0]); 39020 if (JS_IsException(error_list)) 39021 goto exception; 39022 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list, 39023 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 39024 } 39025 39026 /* skip the Error() function in the backtrace */ 39027 build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL); 39028 return obj; 39029 exception: 39030 JS_FreeValue(ctx, obj); 39031 return JS_EXCEPTION; 39032 } 39033 39034 static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, 39035 int argc, JSValueConst *argv) 39036 { 39037 JSValue name, msg; 39038 39039 if (!JS_IsObject(this_val)) 39040 return JS_ThrowTypeErrorNotAnObject(ctx); 39041 name = JS_GetProperty(ctx, this_val, JS_ATOM_name); 39042 if (JS_IsUndefined(name)) 39043 name = JS_AtomToString(ctx, JS_ATOM_Error); 39044 else 39045 name = JS_ToStringFree(ctx, name); 39046 if (JS_IsException(name)) 39047 return JS_EXCEPTION; 39048 39049 msg = JS_GetProperty(ctx, this_val, JS_ATOM_message); 39050 if (JS_IsUndefined(msg)) 39051 msg = JS_AtomToString(ctx, JS_ATOM_empty_string); 39052 else 39053 msg = JS_ToStringFree(ctx, msg); 39054 if (JS_IsException(msg)) { 39055 JS_FreeValue(ctx, name); 39056 return JS_EXCEPTION; 39057 } 39058 if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg)) 39059 name = JS_ConcatString3(ctx, "", name, ": "); 39060 return JS_ConcatString(ctx, name, msg); 39061 } 39062 39063 static const JSCFunctionListEntry js_error_proto_funcs[] = { 39064 JS_CFUNC_DEF("toString", 0, js_error_toString ), 39065 JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 39066 JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 39067 }; 39068 39069 /* AggregateError */ 39070 39071 /* used by C code. */ 39072 static JSValue js_aggregate_error_constructor(JSContext *ctx, 39073 JSValueConst errors) 39074 { 39075 JSValue obj; 39076 39077 obj = JS_NewObjectProtoClass(ctx, 39078 ctx->native_error_proto[JS_AGGREGATE_ERROR], 39079 JS_CLASS_ERROR); 39080 if (JS_IsException(obj)) 39081 return obj; 39082 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors), 39083 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 39084 return obj; 39085 } 39086 39087 /* Array */ 39088 39089 static int JS_CopySubArray(JSContext *ctx, 39090 JSValueConst obj, int64_t to_pos, 39091 int64_t from_pos, int64_t count, int dir) 39092 { 39093 JSObject *p; 39094 int64_t i, from, to, len; 39095 JSValue val; 39096 int fromPresent; 39097 39098 p = NULL; 39099 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 39100 p = JS_VALUE_GET_OBJ(obj); 39101 if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) { 39102 p = NULL; 39103 } 39104 } 39105 39106 for (i = 0; i < count; ) { 39107 if (dir < 0) { 39108 from = from_pos + count - i - 1; 39109 to = to_pos + count - i - 1; 39110 } else { 39111 from = from_pos + i; 39112 to = to_pos + i; 39113 } 39114 if (p && p->fast_array && 39115 from >= 0 && from < (len = p->u.array.count) && 39116 to >= 0 && to < len) { 39117 int64_t l, j; 39118 /* Fast path for fast arrays. Since we don't look at the 39119 prototype chain, we can optimize only the cases where 39120 all the elements are present in the array. */ 39121 l = count - i; 39122 if (dir < 0) { 39123 l = min_int64(l, from + 1); 39124 l = min_int64(l, to + 1); 39125 for(j = 0; j < l; j++) { 39126 set_value(ctx, &p->u.array.u.values[to - j], 39127 JS_DupValue(ctx, p->u.array.u.values[from - j])); 39128 } 39129 } else { 39130 l = min_int64(l, len - from); 39131 l = min_int64(l, len - to); 39132 for(j = 0; j < l; j++) { 39133 set_value(ctx, &p->u.array.u.values[to + j], 39134 JS_DupValue(ctx, p->u.array.u.values[from + j])); 39135 } 39136 } 39137 i += l; 39138 } else { 39139 fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val); 39140 if (fromPresent < 0) 39141 goto exception; 39142 39143 if (fromPresent) { 39144 if (JS_SetPropertyInt64(ctx, obj, to, val) < 0) 39145 goto exception; 39146 } else { 39147 if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0) 39148 goto exception; 39149 } 39150 i++; 39151 } 39152 } 39153 return 0; 39154 39155 exception: 39156 return -1; 39157 } 39158 39159 static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target, 39160 int argc, JSValueConst *argv) 39161 { 39162 JSValue obj; 39163 int i; 39164 39165 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY); 39166 if (JS_IsException(obj)) 39167 return obj; 39168 if (argc == 1 && JS_IsNumber(argv[0])) { 39169 uint32_t len; 39170 if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE)) 39171 goto fail; 39172 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0) 39173 goto fail; 39174 } else { 39175 for(i = 0; i < argc; i++) { 39176 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) 39177 goto fail; 39178 } 39179 } 39180 return obj; 39181 fail: 39182 JS_FreeValue(ctx, obj); 39183 return JS_EXCEPTION; 39184 } 39185 39186 static JSValue js_array_from(JSContext *ctx, JSValueConst this_val, 39187 int argc, JSValueConst *argv) 39188 { 39189 // from(items, mapfn = void 0, this_arg = void 0) 39190 JSValueConst items = argv[0], mapfn, this_arg; 39191 JSValueConst args[2]; 39192 JSValue stack[2]; 39193 JSValue iter, r, v, v2, arrayLike; 39194 int64_t k, len; 39195 int done, mapping; 39196 39197 mapping = FALSE; 39198 mapfn = JS_UNDEFINED; 39199 this_arg = JS_UNDEFINED; 39200 r = JS_UNDEFINED; 39201 arrayLike = JS_UNDEFINED; 39202 stack[0] = JS_UNDEFINED; 39203 stack[1] = JS_UNDEFINED; 39204 39205 if (argc > 1) { 39206 mapfn = argv[1]; 39207 if (!JS_IsUndefined(mapfn)) { 39208 if (check_function(ctx, mapfn)) 39209 goto exception; 39210 mapping = 1; 39211 if (argc > 2) 39212 this_arg = argv[2]; 39213 } 39214 } 39215 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); 39216 if (JS_IsException(iter)) 39217 goto exception; 39218 if (!JS_IsUndefined(iter)) { 39219 JS_FreeValue(ctx, iter); 39220 if (JS_IsConstructor(ctx, this_val)) 39221 r = JS_CallConstructor(ctx, this_val, 0, NULL); 39222 else 39223 r = JS_NewArray(ctx); 39224 if (JS_IsException(r)) 39225 goto exception; 39226 stack[0] = JS_DupValue(ctx, items); 39227 if (js_for_of_start(ctx, &stack[1], FALSE)) 39228 goto exception; 39229 for (k = 0;; k++) { 39230 v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); 39231 if (JS_IsException(v)) 39232 goto exception_close; 39233 if (done) 39234 break; 39235 if (mapping) { 39236 args[0] = v; 39237 args[1] = JS_NewInt32(ctx, k); 39238 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 39239 JS_FreeValue(ctx, v); 39240 v = v2; 39241 if (JS_IsException(v)) 39242 goto exception_close; 39243 } 39244 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 39245 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39246 goto exception_close; 39247 } 39248 } else { 39249 arrayLike = JS_ToObject(ctx, items); 39250 if (JS_IsException(arrayLike)) 39251 goto exception; 39252 if (js_get_length64(ctx, &len, arrayLike) < 0) 39253 goto exception; 39254 v = JS_NewInt64(ctx, len); 39255 args[0] = v; 39256 if (JS_IsConstructor(ctx, this_val)) { 39257 r = JS_CallConstructor(ctx, this_val, 1, args); 39258 } else { 39259 r = js_array_constructor(ctx, JS_UNDEFINED, 1, args); 39260 } 39261 JS_FreeValue(ctx, v); 39262 if (JS_IsException(r)) 39263 goto exception; 39264 for(k = 0; k < len; k++) { 39265 v = JS_GetPropertyInt64(ctx, arrayLike, k); 39266 if (JS_IsException(v)) 39267 goto exception; 39268 if (mapping) { 39269 args[0] = v; 39270 args[1] = JS_NewInt32(ctx, k); 39271 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 39272 JS_FreeValue(ctx, v); 39273 v = v2; 39274 if (JS_IsException(v)) 39275 goto exception; 39276 } 39277 if (JS_DefinePropertyValueInt64(ctx, r, k, v, 39278 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39279 goto exception; 39280 } 39281 } 39282 if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0) 39283 goto exception; 39284 goto done; 39285 39286 exception_close: 39287 if (!JS_IsUndefined(stack[0])) 39288 JS_IteratorClose(ctx, stack[0], TRUE); 39289 exception: 39290 JS_FreeValue(ctx, r); 39291 r = JS_EXCEPTION; 39292 done: 39293 JS_FreeValue(ctx, arrayLike); 39294 JS_FreeValue(ctx, stack[0]); 39295 JS_FreeValue(ctx, stack[1]); 39296 return r; 39297 } 39298 39299 static JSValue js_array_of(JSContext *ctx, JSValueConst this_val, 39300 int argc, JSValueConst *argv) 39301 { 39302 JSValue obj, args[1]; 39303 int i; 39304 39305 if (JS_IsConstructor(ctx, this_val)) { 39306 args[0] = JS_NewInt32(ctx, argc); 39307 obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args); 39308 } else { 39309 obj = JS_NewArray(ctx); 39310 } 39311 if (JS_IsException(obj)) 39312 return JS_EXCEPTION; 39313 for(i = 0; i < argc; i++) { 39314 if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]), 39315 JS_PROP_THROW) < 0) { 39316 goto fail; 39317 } 39318 } 39319 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) { 39320 fail: 39321 JS_FreeValue(ctx, obj); 39322 return JS_EXCEPTION; 39323 } 39324 return obj; 39325 } 39326 39327 static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val, 39328 int argc, JSValueConst *argv) 39329 { 39330 int ret; 39331 ret = JS_IsArray(ctx, argv[0]); 39332 if (ret < 0) 39333 return JS_EXCEPTION; 39334 else 39335 return JS_NewBool(ctx, ret); 39336 } 39337 39338 static JSValue js_get_this(JSContext *ctx, 39339 JSValueConst this_val) 39340 { 39341 return JS_DupValue(ctx, this_val); 39342 } 39343 39344 static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj, 39345 JSValueConst len_val) 39346 { 39347 JSValue ctor, ret, species; 39348 int res; 39349 JSContext *realm; 39350 39351 res = JS_IsArray(ctx, obj); 39352 if (res < 0) 39353 return JS_EXCEPTION; 39354 if (!res) 39355 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); 39356 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor); 39357 if (JS_IsException(ctor)) 39358 return ctor; 39359 if (JS_IsConstructor(ctx, ctor)) { 39360 /* legacy web compatibility */ 39361 realm = JS_GetFunctionRealm(ctx, ctor); 39362 if (!realm) { 39363 JS_FreeValue(ctx, ctor); 39364 return JS_EXCEPTION; 39365 } 39366 if (realm != ctx && 39367 js_same_value(ctx, ctor, realm->array_ctor)) { 39368 JS_FreeValue(ctx, ctor); 39369 ctor = JS_UNDEFINED; 39370 } 39371 } 39372 if (JS_IsObject(ctor)) { 39373 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species); 39374 JS_FreeValue(ctx, ctor); 39375 if (JS_IsException(species)) 39376 return species; 39377 ctor = species; 39378 if (JS_IsNull(ctor)) 39379 ctor = JS_UNDEFINED; 39380 } 39381 if (JS_IsUndefined(ctor)) { 39382 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val); 39383 } else { 39384 ret = JS_CallConstructor(ctx, ctor, 1, &len_val); 39385 JS_FreeValue(ctx, ctor); 39386 return ret; 39387 } 39388 } 39389 39390 static const JSCFunctionListEntry js_array_funcs[] = { 39391 JS_CFUNC_DEF("isArray", 1, js_array_isArray ), 39392 JS_CFUNC_DEF("from", 1, js_array_from ), 39393 JS_CFUNC_DEF("of", 0, js_array_of ), 39394 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 39395 }; 39396 39397 static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) 39398 { 39399 JSValue val; 39400 39401 if (!JS_IsObject(obj)) 39402 return FALSE; 39403 val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable); 39404 if (JS_IsException(val)) 39405 return -1; 39406 if (!JS_IsUndefined(val)) 39407 return JS_ToBoolFree(ctx, val); 39408 return JS_IsArray(ctx, obj); 39409 } 39410 39411 static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, 39412 int argc, JSValueConst *argv) 39413 { 39414 JSValue obj, ret; 39415 int64_t len, idx; 39416 JSValue *arrp; 39417 uint32_t count; 39418 39419 obj = JS_ToObject(ctx, this_val); 39420 if (js_get_length64(ctx, &len, obj)) 39421 goto exception; 39422 39423 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 39424 goto exception; 39425 39426 if (idx < 0) 39427 idx = len + idx; 39428 if (idx < 0 || idx >= len) { 39429 ret = JS_UNDEFINED; 39430 } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) { 39431 ret = JS_DupValue(ctx, arrp[idx]); 39432 } else { 39433 int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret); 39434 if (present < 0) 39435 goto exception; 39436 if (!present) 39437 ret = JS_UNDEFINED; 39438 } 39439 JS_FreeValue(ctx, obj); 39440 return ret; 39441 exception: 39442 JS_FreeValue(ctx, obj); 39443 return JS_EXCEPTION; 39444 } 39445 39446 static JSValue js_array_with(JSContext *ctx, JSValueConst this_val, 39447 int argc, JSValueConst *argv) 39448 { 39449 JSValue arr, obj, ret, *arrp, *pval; 39450 JSObject *p; 39451 int64_t i, len, idx; 39452 uint32_t count32; 39453 39454 ret = JS_EXCEPTION; 39455 arr = JS_UNDEFINED; 39456 obj = JS_ToObject(ctx, this_val); 39457 if (js_get_length64(ctx, &len, obj)) 39458 goto exception; 39459 39460 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 39461 goto exception; 39462 39463 if (idx < 0) 39464 idx = len + idx; 39465 39466 if (idx < 0 || idx >= len) { 39467 JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx); 39468 goto exception; 39469 } 39470 39471 arr = js_allocate_fast_array(ctx, len); 39472 if (JS_IsException(arr)) 39473 goto exception; 39474 39475 p = JS_VALUE_GET_OBJ(arr); 39476 i = 0; 39477 pval = p->u.array.u.values; 39478 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 39479 for (; i < idx; i++, pval++) 39480 *pval = JS_DupValue(ctx, arrp[i]); 39481 *pval = JS_DupValue(ctx, argv[1]); 39482 for (i++, pval++; i < len; i++, pval++) 39483 *pval = JS_DupValue(ctx, arrp[i]); 39484 } else { 39485 for (; i < idx; i++, pval++) 39486 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 39487 goto fill_and_fail; 39488 *pval = JS_DupValue(ctx, argv[1]); 39489 for (i++, pval++; i < len; i++, pval++) { 39490 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { 39491 fill_and_fail: 39492 for (; i < len; i++, pval++) 39493 *pval = JS_UNDEFINED; 39494 goto exception; 39495 } 39496 } 39497 } 39498 39499 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) 39500 goto exception; 39501 39502 ret = arr; 39503 arr = JS_UNDEFINED; 39504 39505 exception: 39506 JS_FreeValue(ctx, arr); 39507 JS_FreeValue(ctx, obj); 39508 return ret; 39509 } 39510 39511 static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, 39512 int argc, JSValueConst *argv) 39513 { 39514 JSValue obj, arr, val; 39515 JSValueConst e; 39516 int64_t len, k, n; 39517 int i, res; 39518 39519 arr = JS_UNDEFINED; 39520 obj = JS_ToObject(ctx, this_val); 39521 if (JS_IsException(obj)) 39522 goto exception; 39523 39524 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 39525 if (JS_IsException(arr)) 39526 goto exception; 39527 n = 0; 39528 for (i = -1; i < argc; i++) { 39529 if (i < 0) 39530 e = obj; 39531 else 39532 e = argv[i]; 39533 39534 res = JS_isConcatSpreadable(ctx, e); 39535 if (res < 0) 39536 goto exception; 39537 if (res) { 39538 if (js_get_length64(ctx, &len, e)) 39539 goto exception; 39540 if (n + len > MAX_SAFE_INTEGER) { 39541 JS_ThrowTypeError(ctx, "Array loo long"); 39542 goto exception; 39543 } 39544 for (k = 0; k < len; k++, n++) { 39545 res = JS_TryGetPropertyInt64(ctx, e, k, &val); 39546 if (res < 0) 39547 goto exception; 39548 if (res) { 39549 if (JS_DefinePropertyValueInt64(ctx, arr, n, val, 39550 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39551 goto exception; 39552 } 39553 } 39554 } else { 39555 if (n >= MAX_SAFE_INTEGER) { 39556 JS_ThrowTypeError(ctx, "Array loo long"); 39557 goto exception; 39558 } 39559 if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e), 39560 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39561 goto exception; 39562 n++; 39563 } 39564 } 39565 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) 39566 goto exception; 39567 39568 JS_FreeValue(ctx, obj); 39569 return arr; 39570 39571 exception: 39572 JS_FreeValue(ctx, arr); 39573 JS_FreeValue(ctx, obj); 39574 return JS_EXCEPTION; 39575 } 39576 39577 #define special_every 0 39578 #define special_some 1 39579 #define special_forEach 2 39580 #define special_map 3 39581 #define special_filter 4 39582 #define special_TA 8 39583 39584 static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj); 39585 39586 static JSValue js_typed_array___speciesCreate(JSContext *ctx, 39587 JSValueConst this_val, 39588 int argc, JSValueConst *argv); 39589 39590 static JSValue js_array_every(JSContext *ctx, JSValueConst this_val, 39591 int argc, JSValueConst *argv, int special) 39592 { 39593 JSValue obj, val, index_val, res, ret; 39594 JSValueConst args[3]; 39595 JSValueConst func, this_arg; 39596 int64_t len, k, n; 39597 int present; 39598 39599 ret = JS_UNDEFINED; 39600 val = JS_UNDEFINED; 39601 if (special & special_TA) { 39602 obj = JS_DupValue(ctx, this_val); 39603 len = js_typed_array_get_length_internal(ctx, obj); 39604 if (len < 0) 39605 goto exception; 39606 } else { 39607 obj = JS_ToObject(ctx, this_val); 39608 if (js_get_length64(ctx, &len, obj)) 39609 goto exception; 39610 } 39611 func = argv[0]; 39612 this_arg = JS_UNDEFINED; 39613 if (argc > 1) 39614 this_arg = argv[1]; 39615 39616 if (check_function(ctx, func)) 39617 goto exception; 39618 39619 switch (special) { 39620 case special_every: 39621 case special_every | special_TA: 39622 ret = JS_TRUE; 39623 break; 39624 case special_some: 39625 case special_some | special_TA: 39626 ret = JS_FALSE; 39627 break; 39628 case special_map: 39629 /* XXX: JS_ArraySpeciesCreate should take int64_t */ 39630 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len)); 39631 if (JS_IsException(ret)) 39632 goto exception; 39633 break; 39634 case special_filter: 39635 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 39636 if (JS_IsException(ret)) 39637 goto exception; 39638 break; 39639 case special_map | special_TA: 39640 args[0] = obj; 39641 args[1] = JS_NewInt32(ctx, len); 39642 ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 39643 if (JS_IsException(ret)) 39644 goto exception; 39645 break; 39646 case special_filter | special_TA: 39647 ret = JS_NewArray(ctx); 39648 if (JS_IsException(ret)) 39649 goto exception; 39650 break; 39651 } 39652 n = 0; 39653 39654 for(k = 0; k < len; k++) { 39655 if (special & special_TA) { 39656 val = JS_GetPropertyInt64(ctx, obj, k); 39657 if (JS_IsException(val)) 39658 goto exception; 39659 present = TRUE; 39660 } else { 39661 present = JS_TryGetPropertyInt64(ctx, obj, k, &val); 39662 if (present < 0) 39663 goto exception; 39664 } 39665 if (present) { 39666 index_val = JS_NewInt64(ctx, k); 39667 if (JS_IsException(index_val)) 39668 goto exception; 39669 args[0] = val; 39670 args[1] = index_val; 39671 args[2] = obj; 39672 res = JS_Call(ctx, func, this_arg, 3, args); 39673 JS_FreeValue(ctx, index_val); 39674 if (JS_IsException(res)) 39675 goto exception; 39676 switch (special) { 39677 case special_every: 39678 case special_every | special_TA: 39679 if (!JS_ToBoolFree(ctx, res)) { 39680 ret = JS_FALSE; 39681 goto done; 39682 } 39683 break; 39684 case special_some: 39685 case special_some | special_TA: 39686 if (JS_ToBoolFree(ctx, res)) { 39687 ret = JS_TRUE; 39688 goto done; 39689 } 39690 break; 39691 case special_map: 39692 if (JS_DefinePropertyValueInt64(ctx, ret, k, res, 39693 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39694 goto exception; 39695 break; 39696 case special_map | special_TA: 39697 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0) 39698 goto exception; 39699 break; 39700 case special_filter: 39701 case special_filter | special_TA: 39702 if (JS_ToBoolFree(ctx, res)) { 39703 if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val), 39704 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 39705 goto exception; 39706 } 39707 break; 39708 default: 39709 JS_FreeValue(ctx, res); 39710 break; 39711 } 39712 JS_FreeValue(ctx, val); 39713 val = JS_UNDEFINED; 39714 } 39715 } 39716 done: 39717 if (special == (special_filter | special_TA)) { 39718 JSValue arr; 39719 args[0] = obj; 39720 args[1] = JS_NewInt32(ctx, n); 39721 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 39722 if (JS_IsException(arr)) 39723 goto exception; 39724 args[0] = ret; 39725 res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args); 39726 if (check_exception_free(ctx, res)) 39727 goto exception; 39728 JS_FreeValue(ctx, ret); 39729 ret = arr; 39730 } 39731 JS_FreeValue(ctx, val); 39732 JS_FreeValue(ctx, obj); 39733 return ret; 39734 39735 exception: 39736 JS_FreeValue(ctx, ret); 39737 JS_FreeValue(ctx, val); 39738 JS_FreeValue(ctx, obj); 39739 return JS_EXCEPTION; 39740 } 39741 39742 #define special_reduce 0 39743 #define special_reduceRight 1 39744 39745 static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val, 39746 int argc, JSValueConst *argv, int special) 39747 { 39748 JSValue obj, val, index_val, acc, acc1; 39749 JSValueConst args[4]; 39750 JSValueConst func; 39751 int64_t len, k, k1; 39752 int present; 39753 39754 acc = JS_UNDEFINED; 39755 val = JS_UNDEFINED; 39756 if (special & special_TA) { 39757 obj = JS_DupValue(ctx, this_val); 39758 len = js_typed_array_get_length_internal(ctx, obj); 39759 if (len < 0) 39760 goto exception; 39761 } else { 39762 obj = JS_ToObject(ctx, this_val); 39763 if (js_get_length64(ctx, &len, obj)) 39764 goto exception; 39765 } 39766 func = argv[0]; 39767 39768 if (check_function(ctx, func)) 39769 goto exception; 39770 39771 k = 0; 39772 if (argc > 1) { 39773 acc = JS_DupValue(ctx, argv[1]); 39774 } else { 39775 for(;;) { 39776 if (k >= len) { 39777 JS_ThrowTypeError(ctx, "empty array"); 39778 goto exception; 39779 } 39780 k1 = (special & special_reduceRight) ? len - k - 1 : k; 39781 k++; 39782 if (special & special_TA) { 39783 acc = JS_GetPropertyInt64(ctx, obj, k1); 39784 if (JS_IsException(acc)) 39785 goto exception; 39786 break; 39787 } else { 39788 present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc); 39789 if (present < 0) 39790 goto exception; 39791 if (present) 39792 break; 39793 } 39794 } 39795 } 39796 for (; k < len; k++) { 39797 k1 = (special & special_reduceRight) ? len - k - 1 : k; 39798 if (special & special_TA) { 39799 val = JS_GetPropertyInt64(ctx, obj, k1); 39800 if (JS_IsException(val)) 39801 goto exception; 39802 present = TRUE; 39803 } else { 39804 present = JS_TryGetPropertyInt64(ctx, obj, k1, &val); 39805 if (present < 0) 39806 goto exception; 39807 } 39808 if (present) { 39809 index_val = JS_NewInt64(ctx, k1); 39810 if (JS_IsException(index_val)) 39811 goto exception; 39812 args[0] = acc; 39813 args[1] = val; 39814 args[2] = index_val; 39815 args[3] = obj; 39816 acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args); 39817 JS_FreeValue(ctx, index_val); 39818 JS_FreeValue(ctx, val); 39819 val = JS_UNDEFINED; 39820 if (JS_IsException(acc1)) 39821 goto exception; 39822 JS_FreeValue(ctx, acc); 39823 acc = acc1; 39824 } 39825 } 39826 JS_FreeValue(ctx, obj); 39827 return acc; 39828 39829 exception: 39830 JS_FreeValue(ctx, acc); 39831 JS_FreeValue(ctx, val); 39832 JS_FreeValue(ctx, obj); 39833 return JS_EXCEPTION; 39834 } 39835 39836 static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val, 39837 int argc, JSValueConst *argv) 39838 { 39839 JSValue obj; 39840 int64_t len, start, end; 39841 39842 obj = JS_ToObject(ctx, this_val); 39843 if (js_get_length64(ctx, &len, obj)) 39844 goto exception; 39845 39846 start = 0; 39847 if (argc > 1 && !JS_IsUndefined(argv[1])) { 39848 if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len)) 39849 goto exception; 39850 } 39851 39852 end = len; 39853 if (argc > 2 && !JS_IsUndefined(argv[2])) { 39854 if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len)) 39855 goto exception; 39856 } 39857 39858 /* XXX: should special case fast arrays */ 39859 while (start < end) { 39860 if (JS_SetPropertyInt64(ctx, obj, start, 39861 JS_DupValue(ctx, argv[0])) < 0) 39862 goto exception; 39863 start++; 39864 } 39865 return obj; 39866 39867 exception: 39868 JS_FreeValue(ctx, obj); 39869 return JS_EXCEPTION; 39870 } 39871 39872 static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, 39873 int argc, JSValueConst *argv) 39874 { 39875 JSValue obj, val; 39876 int64_t len, n; 39877 JSValue *arrp; 39878 uint32_t count; 39879 int res; 39880 39881 obj = JS_ToObject(ctx, this_val); 39882 if (js_get_length64(ctx, &len, obj)) 39883 goto exception; 39884 39885 res = FALSE; 39886 if (len > 0) { 39887 n = 0; 39888 if (argc > 1) { 39889 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) 39890 goto exception; 39891 } 39892 if (js_get_fast_array(ctx, obj, &arrp, &count)) { 39893 for (; n < count; n++) { 39894 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), 39895 JS_DupValue(ctx, arrp[n]), 39896 JS_EQ_SAME_VALUE_ZERO)) { 39897 res = TRUE; 39898 goto done; 39899 } 39900 } 39901 } 39902 for (; n < len; n++) { 39903 val = JS_GetPropertyInt64(ctx, obj, n); 39904 if (JS_IsException(val)) 39905 goto exception; 39906 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, 39907 JS_EQ_SAME_VALUE_ZERO)) { 39908 res = TRUE; 39909 break; 39910 } 39911 } 39912 } 39913 done: 39914 JS_FreeValue(ctx, obj); 39915 return JS_NewBool(ctx, res); 39916 39917 exception: 39918 JS_FreeValue(ctx, obj); 39919 return JS_EXCEPTION; 39920 } 39921 39922 static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val, 39923 int argc, JSValueConst *argv) 39924 { 39925 JSValue obj, val; 39926 int64_t len, n, res; 39927 JSValue *arrp; 39928 uint32_t count; 39929 39930 obj = JS_ToObject(ctx, this_val); 39931 if (js_get_length64(ctx, &len, obj)) 39932 goto exception; 39933 39934 res = -1; 39935 if (len > 0) { 39936 n = 0; 39937 if (argc > 1) { 39938 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len)) 39939 goto exception; 39940 } 39941 if (js_get_fast_array(ctx, obj, &arrp, &count)) { 39942 for (; n < count; n++) { 39943 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), 39944 JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) { 39945 res = n; 39946 goto done; 39947 } 39948 } 39949 } 39950 for (; n < len; n++) { 39951 int present = JS_TryGetPropertyInt64(ctx, obj, n, &val); 39952 if (present < 0) 39953 goto exception; 39954 if (present) { 39955 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { 39956 res = n; 39957 break; 39958 } 39959 } 39960 } 39961 } 39962 done: 39963 JS_FreeValue(ctx, obj); 39964 return JS_NewInt64(ctx, res); 39965 39966 exception: 39967 JS_FreeValue(ctx, obj); 39968 return JS_EXCEPTION; 39969 } 39970 39971 static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, 39972 int argc, JSValueConst *argv) 39973 { 39974 JSValue obj, val; 39975 int64_t len, n, res; 39976 int present; 39977 39978 obj = JS_ToObject(ctx, this_val); 39979 if (js_get_length64(ctx, &len, obj)) 39980 goto exception; 39981 39982 res = -1; 39983 if (len > 0) { 39984 n = len - 1; 39985 if (argc > 1) { 39986 if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len)) 39987 goto exception; 39988 } 39989 /* XXX: should special case fast arrays */ 39990 for (; n >= 0; n--) { 39991 present = JS_TryGetPropertyInt64(ctx, obj, n, &val); 39992 if (present < 0) 39993 goto exception; 39994 if (present) { 39995 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) { 39996 res = n; 39997 break; 39998 } 39999 } 40000 } 40001 } 40002 JS_FreeValue(ctx, obj); 40003 return JS_NewInt64(ctx, res); 40004 40005 exception: 40006 JS_FreeValue(ctx, obj); 40007 return JS_EXCEPTION; 40008 } 40009 40010 enum { 40011 ArrayFind, 40012 ArrayFindIndex, 40013 ArrayFindLast, 40014 ArrayFindLastIndex, 40015 }; 40016 40017 static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, 40018 int argc, JSValueConst *argv, int mode) 40019 { 40020 JSValueConst func, this_arg; 40021 JSValueConst args[3]; 40022 JSValue obj, val, index_val, res; 40023 int64_t len, k, end; 40024 int dir; 40025 40026 index_val = JS_UNDEFINED; 40027 val = JS_UNDEFINED; 40028 obj = JS_ToObject(ctx, this_val); 40029 if (js_get_length64(ctx, &len, obj)) 40030 goto exception; 40031 40032 func = argv[0]; 40033 if (check_function(ctx, func)) 40034 goto exception; 40035 40036 this_arg = JS_UNDEFINED; 40037 if (argc > 1) 40038 this_arg = argv[1]; 40039 40040 k = 0; 40041 dir = 1; 40042 end = len; 40043 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { 40044 k = len - 1; 40045 dir = -1; 40046 end = -1; 40047 } 40048 40049 // TODO(bnoordhuis) add fast path for fast arrays 40050 for(; k != end; k += dir) { 40051 index_val = JS_NewInt64(ctx, k); 40052 if (JS_IsException(index_val)) 40053 goto exception; 40054 val = JS_GetPropertyValue(ctx, obj, index_val); 40055 if (JS_IsException(val)) 40056 goto exception; 40057 args[0] = val; 40058 args[1] = index_val; 40059 args[2] = this_val; 40060 res = JS_Call(ctx, func, this_arg, 3, args); 40061 if (JS_IsException(res)) 40062 goto exception; 40063 if (JS_ToBoolFree(ctx, res)) { 40064 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { 40065 JS_FreeValue(ctx, val); 40066 JS_FreeValue(ctx, obj); 40067 return index_val; 40068 } else { 40069 JS_FreeValue(ctx, index_val); 40070 JS_FreeValue(ctx, obj); 40071 return val; 40072 } 40073 } 40074 JS_FreeValue(ctx, val); 40075 JS_FreeValue(ctx, index_val); 40076 } 40077 JS_FreeValue(ctx, obj); 40078 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) 40079 return JS_NewInt32(ctx, -1); 40080 else 40081 return JS_UNDEFINED; 40082 40083 exception: 40084 JS_FreeValue(ctx, index_val); 40085 JS_FreeValue(ctx, val); 40086 JS_FreeValue(ctx, obj); 40087 return JS_EXCEPTION; 40088 } 40089 40090 static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, 40091 int argc, JSValueConst *argv) 40092 { 40093 JSValue obj, method, ret; 40094 40095 obj = JS_ToObject(ctx, this_val); 40096 if (JS_IsException(obj)) 40097 return JS_EXCEPTION; 40098 method = JS_GetProperty(ctx, obj, JS_ATOM_join); 40099 if (JS_IsException(method)) { 40100 ret = JS_EXCEPTION; 40101 } else 40102 if (!JS_IsFunction(ctx, method)) { 40103 /* Use intrinsic Object.prototype.toString */ 40104 JS_FreeValue(ctx, method); 40105 ret = js_object_toString(ctx, obj, 0, NULL); 40106 } else { 40107 ret = JS_CallFree(ctx, method, obj, 0, NULL); 40108 } 40109 JS_FreeValue(ctx, obj); 40110 return ret; 40111 } 40112 40113 static JSValue js_array_join(JSContext *ctx, JSValueConst this_val, 40114 int argc, JSValueConst *argv, int toLocaleString) 40115 { 40116 JSValue obj, sep = JS_UNDEFINED, el; 40117 StringBuffer b_s, *b = &b_s; 40118 JSString *p = NULL; 40119 int64_t i, n; 40120 int c; 40121 40122 obj = JS_ToObject(ctx, this_val); 40123 if (js_get_length64(ctx, &n, obj)) 40124 goto exception; 40125 40126 c = ','; /* default separator */ 40127 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { 40128 sep = JS_ToString(ctx, argv[0]); 40129 if (JS_IsException(sep)) 40130 goto exception; 40131 p = JS_VALUE_GET_STRING(sep); 40132 if (p->len == 1 && !p->is_wide_char) 40133 c = p->u.str8[0]; 40134 else 40135 c = -1; 40136 } 40137 string_buffer_init(ctx, b, 0); 40138 40139 for(i = 0; i < n; i++) { 40140 if (i > 0) { 40141 if (c >= 0) { 40142 string_buffer_putc8(b, c); 40143 } else { 40144 string_buffer_concat(b, p, 0, p->len); 40145 } 40146 } 40147 el = JS_GetPropertyUint32(ctx, obj, i); 40148 if (JS_IsException(el)) 40149 goto fail; 40150 if (!JS_IsNull(el) && !JS_IsUndefined(el)) { 40151 if (toLocaleString) { 40152 el = JS_ToLocaleStringFree(ctx, el); 40153 } 40154 if (string_buffer_concat_value_free(b, el)) 40155 goto fail; 40156 } 40157 } 40158 JS_FreeValue(ctx, sep); 40159 JS_FreeValue(ctx, obj); 40160 return string_buffer_end(b); 40161 40162 fail: 40163 string_buffer_free(b); 40164 JS_FreeValue(ctx, sep); 40165 exception: 40166 JS_FreeValue(ctx, obj); 40167 return JS_EXCEPTION; 40168 } 40169 40170 static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val, 40171 int argc, JSValueConst *argv, int shift) 40172 { 40173 JSValue obj, res = JS_UNDEFINED; 40174 int64_t len, newLen; 40175 JSValue *arrp; 40176 uint32_t count32; 40177 40178 obj = JS_ToObject(ctx, this_val); 40179 if (js_get_length64(ctx, &len, obj)) 40180 goto exception; 40181 newLen = 0; 40182 if (len > 0) { 40183 newLen = len - 1; 40184 /* Special case fast arrays */ 40185 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 40186 JSObject *p = JS_VALUE_GET_OBJ(obj); 40187 if (shift) { 40188 res = arrp[0]; 40189 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp)); 40190 p->u.array.count--; 40191 } else { 40192 res = arrp[count32 - 1]; 40193 p->u.array.count--; 40194 } 40195 } else { 40196 if (shift) { 40197 res = JS_GetPropertyInt64(ctx, obj, 0); 40198 if (JS_IsException(res)) 40199 goto exception; 40200 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1)) 40201 goto exception; 40202 } else { 40203 res = JS_GetPropertyInt64(ctx, obj, newLen); 40204 if (JS_IsException(res)) 40205 goto exception; 40206 } 40207 if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0) 40208 goto exception; 40209 } 40210 } 40211 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 40212 goto exception; 40213 40214 JS_FreeValue(ctx, obj); 40215 return res; 40216 40217 exception: 40218 JS_FreeValue(ctx, res); 40219 JS_FreeValue(ctx, obj); 40220 return JS_EXCEPTION; 40221 } 40222 40223 int qjs_array_append_new(JSContext *ctx, JSValue this_val, JSValue item) 40224 { 40225 JSValue obj; 40226 int64_t len, from, newLen; 40227 40228 obj = JS_ToObject(ctx, this_val); 40229 if (js_get_length64(ctx, &len, obj)) 40230 goto exception; 40231 newLen = len + 1; 40232 if (newLen > MAX_SAFE_INTEGER) { 40233 JS_ThrowTypeError(ctx, "Array loo long"); 40234 goto exception; 40235 } 40236 from = len; 40237 if (JS_SetPropertyInt64(ctx, obj, from, item) < 0) 40238 goto exception; 40239 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 40240 goto exception; 40241 40242 JS_FreeValue(ctx, obj); 40243 return 0; 40244 40245 exception: 40246 JS_FreeValue(ctx, obj); 40247 return -1; 40248 } 40249 40250 static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, 40251 int argc, JSValueConst *argv, int unshift) 40252 { 40253 JSValue obj; 40254 int i; 40255 int64_t len, from, newLen; 40256 40257 obj = JS_ToObject(ctx, this_val); 40258 if (js_get_length64(ctx, &len, obj)) 40259 goto exception; 40260 newLen = len + argc; 40261 if (newLen > MAX_SAFE_INTEGER) { 40262 JS_ThrowTypeError(ctx, "Array loo long"); 40263 goto exception; 40264 } 40265 from = len; 40266 if (unshift && argc > 0) { 40267 if (JS_CopySubArray(ctx, obj, argc, 0, len, -1)) 40268 goto exception; 40269 from = 0; 40270 } 40271 for(i = 0; i < argc; i++) { 40272 if (JS_SetPropertyInt64(ctx, obj, from + i, 40273 JS_DupValue(ctx, argv[i])) < 0) 40274 goto exception; 40275 } 40276 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0) 40277 goto exception; 40278 40279 JS_FreeValue(ctx, obj); 40280 return JS_NewInt64(ctx, newLen); 40281 40282 exception: 40283 JS_FreeValue(ctx, obj); 40284 return JS_EXCEPTION; 40285 } 40286 40287 static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, 40288 int argc, JSValueConst *argv) 40289 { 40290 JSValue obj, lval, hval; 40291 JSValue *arrp; 40292 int64_t len, l, h; 40293 int l_present, h_present; 40294 uint32_t count32; 40295 40296 lval = JS_UNDEFINED; 40297 obj = JS_ToObject(ctx, this_val); 40298 if (js_get_length64(ctx, &len, obj)) 40299 goto exception; 40300 40301 /* Special case fast arrays */ 40302 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 40303 uint32_t ll, hh; 40304 40305 if (count32 > 1) { 40306 for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) { 40307 lval = arrp[ll]; 40308 arrp[ll] = arrp[hh]; 40309 arrp[hh] = lval; 40310 } 40311 } 40312 return obj; 40313 } 40314 40315 for (l = 0, h = len - 1; l < h; l++, h--) { 40316 l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval); 40317 if (l_present < 0) 40318 goto exception; 40319 h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval); 40320 if (h_present < 0) 40321 goto exception; 40322 if (h_present) { 40323 if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0) 40324 goto exception; 40325 40326 if (l_present) { 40327 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { 40328 lval = JS_UNDEFINED; 40329 goto exception; 40330 } 40331 lval = JS_UNDEFINED; 40332 } else { 40333 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0) 40334 goto exception; 40335 } 40336 } else { 40337 if (l_present) { 40338 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0) 40339 goto exception; 40340 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) { 40341 lval = JS_UNDEFINED; 40342 goto exception; 40343 } 40344 lval = JS_UNDEFINED; 40345 } 40346 } 40347 } 40348 return obj; 40349 40350 exception: 40351 JS_FreeValue(ctx, lval); 40352 JS_FreeValue(ctx, obj); 40353 return JS_EXCEPTION; 40354 } 40355 40356 // Note: a.toReversed() is a.slice().reverse() with the twist that a.slice() 40357 // leaves holes in sparse arrays intact whereas a.toReversed() replaces them 40358 // with undefined, thus in effect creating a dense array. 40359 // Does not use Array[@@species], always returns a base Array. 40360 static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val, 40361 int argc, JSValueConst *argv) 40362 { 40363 JSValue arr, obj, ret, *arrp, *pval; 40364 JSObject *p; 40365 int64_t i, len; 40366 uint32_t count32; 40367 40368 ret = JS_EXCEPTION; 40369 arr = JS_UNDEFINED; 40370 obj = JS_ToObject(ctx, this_val); 40371 if (js_get_length64(ctx, &len, obj)) 40372 goto exception; 40373 40374 arr = js_allocate_fast_array(ctx, len); 40375 if (JS_IsException(arr)) 40376 goto exception; 40377 40378 if (len > 0) { 40379 p = JS_VALUE_GET_OBJ(arr); 40380 40381 i = len - 1; 40382 pval = p->u.array.u.values; 40383 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 40384 for (; i >= 0; i--, pval++) 40385 *pval = JS_DupValue(ctx, arrp[i]); 40386 } else { 40387 // Query order is observable; test262 expects descending order. 40388 for (; i >= 0; i--, pval++) { 40389 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { 40390 // Exception; initialize remaining elements. 40391 for (; i >= 0; i--, pval++) 40392 *pval = JS_UNDEFINED; 40393 goto exception; 40394 } 40395 } 40396 } 40397 40398 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) 40399 goto exception; 40400 } 40401 40402 ret = arr; 40403 arr = JS_UNDEFINED; 40404 40405 exception: 40406 JS_FreeValue(ctx, arr); 40407 JS_FreeValue(ctx, obj); 40408 return ret; 40409 } 40410 40411 static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, 40412 int argc, JSValueConst *argv, int splice) 40413 { 40414 JSValue obj, arr, val, len_val; 40415 int64_t len, start, k, final, n, count, del_count, new_len; 40416 int kPresent; 40417 JSValue *arrp; 40418 uint32_t count32, i, item_count; 40419 40420 arr = JS_UNDEFINED; 40421 obj = JS_ToObject(ctx, this_val); 40422 if (js_get_length64(ctx, &len, obj)) 40423 goto exception; 40424 40425 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 40426 goto exception; 40427 40428 if (splice) { 40429 if (argc == 0) { 40430 item_count = 0; 40431 del_count = 0; 40432 } else 40433 if (argc == 1) { 40434 item_count = 0; 40435 del_count = len - start; 40436 } else { 40437 item_count = argc - 2; 40438 if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0)) 40439 goto exception; 40440 } 40441 if (len + item_count - del_count > MAX_SAFE_INTEGER) { 40442 JS_ThrowTypeError(ctx, "Array loo long"); 40443 goto exception; 40444 } 40445 count = del_count; 40446 } else { 40447 item_count = 0; /* avoid warning */ 40448 final = len; 40449 if (!JS_IsUndefined(argv[1])) { 40450 if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len)) 40451 goto exception; 40452 } 40453 count = max_int64(final - start, 0); 40454 } 40455 len_val = JS_NewInt64(ctx, count); 40456 arr = JS_ArraySpeciesCreate(ctx, obj, len_val); 40457 JS_FreeValue(ctx, len_val); 40458 if (JS_IsException(arr)) 40459 goto exception; 40460 40461 k = start; 40462 final = start + count; 40463 n = 0; 40464 /* The fast array test on arr ensures that 40465 JS_CreateDataPropertyUint32() won't modify obj in case arr is 40466 an exotic object */ 40467 /* Special case fast arrays */ 40468 if (js_get_fast_array(ctx, obj, &arrp, &count32) && 40469 js_is_fast_array(ctx, arr)) { 40470 /* XXX: should share code with fast array constructor */ 40471 for (; k < final && k < count32; k++, n++) { 40472 if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0) 40473 goto exception; 40474 } 40475 } 40476 /* Copy the remaining elements if any (handle case of inherited properties) */ 40477 for (; k < final; k++, n++) { 40478 kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val); 40479 if (kPresent < 0) 40480 goto exception; 40481 if (kPresent) { 40482 if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0) 40483 goto exception; 40484 } 40485 } 40486 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0) 40487 goto exception; 40488 40489 if (splice) { 40490 new_len = len + item_count - del_count; 40491 if (item_count != del_count) { 40492 if (JS_CopySubArray(ctx, obj, start + item_count, 40493 start + del_count, len - (start + del_count), 40494 item_count <= del_count ? +1 : -1) < 0) 40495 goto exception; 40496 40497 for (k = len; k-- > new_len; ) { 40498 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0) 40499 goto exception; 40500 } 40501 } 40502 for (i = 0; i < item_count; i++) { 40503 if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0) 40504 goto exception; 40505 } 40506 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0) 40507 goto exception; 40508 } 40509 JS_FreeValue(ctx, obj); 40510 return arr; 40511 40512 exception: 40513 JS_FreeValue(ctx, obj); 40514 JS_FreeValue(ctx, arr); 40515 return JS_EXCEPTION; 40516 } 40517 40518 static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val, 40519 int argc, JSValueConst *argv) 40520 { 40521 JSValue arr, obj, ret, *arrp, *pval, *last; 40522 JSObject *p; 40523 int64_t i, j, len, newlen, start, add, del; 40524 uint32_t count32; 40525 40526 pval = NULL; 40527 last = NULL; 40528 ret = JS_EXCEPTION; 40529 arr = JS_UNDEFINED; 40530 40531 obj = JS_ToObject(ctx, this_val); 40532 if (js_get_length64(ctx, &len, obj)) 40533 goto exception; 40534 40535 start = 0; 40536 if (argc > 0) 40537 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 40538 goto exception; 40539 40540 del = 0; 40541 if (argc > 0) 40542 del = len - start; 40543 if (argc > 1) 40544 if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0)) 40545 goto exception; 40546 40547 add = 0; 40548 if (argc > 2) 40549 add = argc - 2; 40550 40551 newlen = len + add - del; 40552 if (newlen > MAX_SAFE_INTEGER) { 40553 JS_ThrowTypeError(ctx, "invalid array length"); 40554 goto exception; 40555 } 40556 40557 arr = js_allocate_fast_array(ctx, newlen); 40558 if (JS_IsException(arr)) 40559 goto exception; 40560 40561 if (newlen <= 0) 40562 goto done; 40563 40564 p = JS_VALUE_GET_OBJ(arr); 40565 pval = &p->u.array.u.values[0]; 40566 last = &p->u.array.u.values[newlen]; 40567 40568 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 40569 for (i = 0; i < start; i++, pval++) 40570 *pval = JS_DupValue(ctx, arrp[i]); 40571 for (j = 0; j < add; j++, pval++) 40572 *pval = JS_DupValue(ctx, argv[2 + j]); 40573 for (i += del; i < len; i++, pval++) 40574 *pval = JS_DupValue(ctx, arrp[i]); 40575 } else { 40576 for (i = 0; i < start; i++, pval++) 40577 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 40578 goto exception; 40579 for (j = 0; j < add; j++, pval++) 40580 *pval = JS_DupValue(ctx, argv[2 + j]); 40581 for (i += del; i < len; i++, pval++) 40582 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) 40583 goto exception; 40584 } 40585 40586 assert(pval == last); 40587 40588 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0) 40589 goto exception; 40590 40591 done: 40592 ret = arr; 40593 arr = JS_UNDEFINED; 40594 40595 exception: 40596 while (pval != last) 40597 *pval++ = JS_UNDEFINED; 40598 40599 JS_FreeValue(ctx, arr); 40600 JS_FreeValue(ctx, obj); 40601 return ret; 40602 } 40603 40604 static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, 40605 int argc, JSValueConst *argv) 40606 { 40607 JSValue obj; 40608 int64_t len, from, to, final, count; 40609 40610 obj = JS_ToObject(ctx, this_val); 40611 if (js_get_length64(ctx, &len, obj)) 40612 goto exception; 40613 40614 if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len)) 40615 goto exception; 40616 40617 if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len)) 40618 goto exception; 40619 40620 final = len; 40621 if (argc > 2 && !JS_IsUndefined(argv[2])) { 40622 if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len)) 40623 goto exception; 40624 } 40625 40626 count = min_int64(final - from, len - to); 40627 40628 if (JS_CopySubArray(ctx, obj, to, from, count, 40629 (from < to && to < from + count) ? -1 : +1)) 40630 goto exception; 40631 40632 return obj; 40633 40634 exception: 40635 JS_FreeValue(ctx, obj); 40636 return JS_EXCEPTION; 40637 } 40638 40639 static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, 40640 JSValueConst source, int64_t sourceLen, 40641 int64_t targetIndex, int depth, 40642 JSValueConst mapperFunction, 40643 JSValueConst thisArg) 40644 { 40645 JSValue element; 40646 int64_t sourceIndex, elementLen; 40647 int present, is_array; 40648 40649 if (js_check_stack_overflow(ctx->rt, 0)) { 40650 JS_ThrowStackOverflow(ctx); 40651 return -1; 40652 } 40653 40654 for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { 40655 present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element); 40656 if (present < 0) 40657 return -1; 40658 if (!present) 40659 continue; 40660 if (!JS_IsUndefined(mapperFunction)) { 40661 JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; 40662 element = JS_Call(ctx, mapperFunction, thisArg, 3, args); 40663 JS_FreeValue(ctx, (JSValue)args[0]); 40664 JS_FreeValue(ctx, (JSValue)args[1]); 40665 if (JS_IsException(element)) 40666 return -1; 40667 } 40668 if (depth > 0) { 40669 is_array = JS_IsArray(ctx, element); 40670 if (is_array < 0) 40671 goto fail; 40672 if (is_array) { 40673 if (js_get_length64(ctx, &elementLen, element) < 0) 40674 goto fail; 40675 targetIndex = JS_FlattenIntoArray(ctx, target, element, 40676 elementLen, targetIndex, 40677 depth - 1, 40678 JS_UNDEFINED, JS_UNDEFINED); 40679 if (targetIndex < 0) 40680 goto fail; 40681 JS_FreeValue(ctx, element); 40682 continue; 40683 } 40684 } 40685 if (targetIndex >= MAX_SAFE_INTEGER) { 40686 JS_ThrowTypeError(ctx, "Array too long"); 40687 goto fail; 40688 } 40689 if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element, 40690 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 40691 return -1; 40692 targetIndex++; 40693 } 40694 return targetIndex; 40695 40696 fail: 40697 JS_FreeValue(ctx, element); 40698 return -1; 40699 } 40700 40701 static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val, 40702 int argc, JSValueConst *argv, int map) 40703 { 40704 JSValue obj, arr; 40705 JSValueConst mapperFunction, thisArg; 40706 int64_t sourceLen; 40707 int depthNum; 40708 40709 arr = JS_UNDEFINED; 40710 obj = JS_ToObject(ctx, this_val); 40711 if (js_get_length64(ctx, &sourceLen, obj)) 40712 goto exception; 40713 40714 depthNum = 1; 40715 mapperFunction = JS_UNDEFINED; 40716 thisArg = JS_UNDEFINED; 40717 if (map) { 40718 mapperFunction = argv[0]; 40719 if (argc > 1) { 40720 thisArg = argv[1]; 40721 } 40722 if (check_function(ctx, mapperFunction)) 40723 goto exception; 40724 } else { 40725 if (argc > 0 && !JS_IsUndefined(argv[0])) { 40726 if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0) 40727 goto exception; 40728 } 40729 } 40730 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0)); 40731 if (JS_IsException(arr)) 40732 goto exception; 40733 if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum, 40734 mapperFunction, thisArg) < 0) 40735 goto exception; 40736 JS_FreeValue(ctx, obj); 40737 return arr; 40738 40739 exception: 40740 JS_FreeValue(ctx, obj); 40741 JS_FreeValue(ctx, arr); 40742 return JS_EXCEPTION; 40743 } 40744 40745 /* Array sort */ 40746 40747 typedef struct ValueSlot { 40748 JSValue val; 40749 JSString *str; 40750 int64_t pos; 40751 } ValueSlot; 40752 40753 struct array_sort_context { 40754 JSContext *ctx; 40755 int exception; 40756 int has_method; 40757 JSValueConst method; 40758 }; 40759 40760 static int js_array_cmp_generic(const void *a, const void *b, void *opaque) { 40761 struct array_sort_context *psc = opaque; 40762 JSContext *ctx = psc->ctx; 40763 JSValueConst argv[2]; 40764 JSValue res; 40765 ValueSlot *ap = (ValueSlot *)(void *)a; 40766 ValueSlot *bp = (ValueSlot *)(void *)b; 40767 int cmp; 40768 40769 if (psc->exception) 40770 return 0; 40771 40772 if (psc->has_method) { 40773 /* custom sort function is specified as returning 0 for identical 40774 * objects: avoid method call overhead. 40775 */ 40776 if (!memcmp(&ap->val, &bp->val, sizeof(ap->val))) 40777 goto cmp_same; 40778 argv[0] = ap->val; 40779 argv[1] = bp->val; 40780 res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv); 40781 if (JS_IsException(res)) 40782 goto exception; 40783 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { 40784 int val = JS_VALUE_GET_INT(res); 40785 cmp = (val > 0) - (val < 0); 40786 } else { 40787 double val; 40788 if (JS_ToFloat64Free(ctx, &val, res) < 0) 40789 goto exception; 40790 cmp = (val > 0) - (val < 0); 40791 } 40792 } else { 40793 /* Not supposed to bypass ToString even for identical objects as 40794 * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js 40795 */ 40796 if (!ap->str) { 40797 JSValue str = JS_ToString(ctx, ap->val); 40798 if (JS_IsException(str)) 40799 goto exception; 40800 ap->str = JS_VALUE_GET_STRING(str); 40801 } 40802 if (!bp->str) { 40803 JSValue str = JS_ToString(ctx, bp->val); 40804 if (JS_IsException(str)) 40805 goto exception; 40806 bp->str = JS_VALUE_GET_STRING(str); 40807 } 40808 cmp = js_string_compare(ctx, ap->str, bp->str); 40809 } 40810 if (cmp != 0) 40811 return cmp; 40812 cmp_same: 40813 /* make sort stable: compare array offsets */ 40814 return (ap->pos > bp->pos) - (ap->pos < bp->pos); 40815 40816 exception: 40817 psc->exception = 1; 40818 return 0; 40819 } 40820 40821 static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val, 40822 int argc, JSValueConst *argv) 40823 { 40824 struct array_sort_context asc = { ctx, 0, 0, argv[0] }; 40825 JSValue obj = JS_UNDEFINED; 40826 ValueSlot *array = NULL; 40827 size_t array_size = 0, pos = 0, n = 0; 40828 int64_t i, len, undefined_count = 0; 40829 int present; 40830 40831 if (!JS_IsUndefined(asc.method)) { 40832 if (check_function(ctx, asc.method)) 40833 goto exception; 40834 asc.has_method = 1; 40835 } 40836 obj = JS_ToObject(ctx, this_val); 40837 if (js_get_length64(ctx, &len, obj)) 40838 goto exception; 40839 40840 /* XXX: should special case fast arrays */ 40841 for (i = 0; i < len; i++) { 40842 if (pos >= array_size) { 40843 size_t new_size, slack; 40844 ValueSlot *new_array; 40845 new_size = (array_size + (array_size >> 1) + 31) & ~15; 40846 new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack); 40847 if (new_array == NULL) 40848 goto exception; 40849 new_size += slack / sizeof(*new_array); 40850 array = new_array; 40851 array_size = new_size; 40852 } 40853 present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val); 40854 if (present < 0) 40855 goto exception; 40856 if (present == 0) 40857 continue; 40858 if (JS_IsUndefined(array[pos].val)) { 40859 undefined_count++; 40860 continue; 40861 } 40862 array[pos].str = NULL; 40863 array[pos].pos = i; 40864 pos++; 40865 } 40866 rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc); 40867 if (asc.exception) 40868 goto exception; 40869 40870 /* XXX: should special case fast arrays */ 40871 while (n < pos) { 40872 if (array[n].str) 40873 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); 40874 if (array[n].pos == n) { 40875 JS_FreeValue(ctx, array[n].val); 40876 } else { 40877 if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) { 40878 n++; 40879 goto exception; 40880 } 40881 } 40882 n++; 40883 } 40884 js_free(ctx, array); 40885 for (i = n; undefined_count-- > 0; i++) { 40886 if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0) 40887 goto fail; 40888 } 40889 for (; i < len; i++) { 40890 if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0) 40891 goto fail; 40892 } 40893 return obj; 40894 40895 exception: 40896 for (; n < pos; n++) { 40897 JS_FreeValue(ctx, array[n].val); 40898 if (array[n].str) 40899 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str)); 40900 } 40901 js_free(ctx, array); 40902 fail: 40903 JS_FreeValue(ctx, obj); 40904 return JS_EXCEPTION; 40905 } 40906 40907 // Note: a.toSorted() is a.slice().sort() with the twist that a.slice() 40908 // leaves holes in sparse arrays intact whereas a.toSorted() replaces them 40909 // with undefined, thus in effect creating a dense array. 40910 // Does not use Array[@@species], always returns a base Array. 40911 static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, 40912 int argc, JSValueConst *argv) 40913 { 40914 JSValue arr, obj, ret, *arrp, *pval; 40915 JSObject *p; 40916 int64_t i, len; 40917 uint32_t count32; 40918 int ok; 40919 40920 ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); 40921 if (!ok) 40922 return JS_ThrowTypeError(ctx, "not a function"); 40923 40924 ret = JS_EXCEPTION; 40925 arr = JS_UNDEFINED; 40926 obj = JS_ToObject(ctx, this_val); 40927 if (js_get_length64(ctx, &len, obj)) 40928 goto exception; 40929 40930 arr = js_allocate_fast_array(ctx, len); 40931 if (JS_IsException(arr)) 40932 goto exception; 40933 40934 if (len > 0) { 40935 p = JS_VALUE_GET_OBJ(arr); 40936 i = 0; 40937 pval = p->u.array.u.values; 40938 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { 40939 for (; i < len; i++, pval++) 40940 *pval = JS_DupValue(ctx, arrp[i]); 40941 } else { 40942 for (; i < len; i++, pval++) { 40943 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { 40944 for (; i < len; i++, pval++) 40945 *pval = JS_UNDEFINED; 40946 goto exception; 40947 } 40948 } 40949 } 40950 40951 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) 40952 goto exception; 40953 } 40954 40955 ret = js_array_sort(ctx, arr, argc, argv); 40956 if (JS_IsException(ret)) 40957 goto exception; 40958 JS_FreeValue(ctx, ret); 40959 40960 ret = arr; 40961 arr = JS_UNDEFINED; 40962 40963 exception: 40964 JS_FreeValue(ctx, arr); 40965 JS_FreeValue(ctx, obj); 40966 return ret; 40967 } 40968 40969 typedef struct JSArrayIteratorData { 40970 JSValue obj; 40971 JSIteratorKindEnum kind; 40972 uint32_t idx; 40973 } JSArrayIteratorData; 40974 40975 static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val) 40976 { 40977 JSObject *p = JS_VALUE_GET_OBJ(val); 40978 JSArrayIteratorData *it = p->u.array_iterator_data; 40979 if (it) { 40980 JS_FreeValueRT(rt, it->obj); 40981 js_free_rt(rt, it); 40982 } 40983 } 40984 40985 static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val, 40986 JS_MarkFunc *mark_func) 40987 { 40988 JSObject *p = JS_VALUE_GET_OBJ(val); 40989 JSArrayIteratorData *it = p->u.array_iterator_data; 40990 if (it) { 40991 JS_MarkValue(rt, it->obj, mark_func); 40992 } 40993 } 40994 40995 static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab) 40996 { 40997 JSValue obj; 40998 int i; 40999 41000 obj = JS_NewArray(ctx); 41001 if (JS_IsException(obj)) 41002 return JS_EXCEPTION; 41003 for(i = 0; i < len; i++) { 41004 if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) { 41005 JS_FreeValue(ctx, obj); 41006 return JS_EXCEPTION; 41007 } 41008 } 41009 return obj; 41010 } 41011 41012 static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val, 41013 int argc, JSValueConst *argv, int magic) 41014 { 41015 JSValue enum_obj, arr; 41016 JSArrayIteratorData *it; 41017 JSIteratorKindEnum kind; 41018 int class_id; 41019 41020 kind = magic & 3; 41021 if (magic & 4) { 41022 /* string iterator case */ 41023 arr = JS_ToStringCheckObject(ctx, this_val); 41024 class_id = JS_CLASS_STRING_ITERATOR; 41025 } else { 41026 arr = JS_ToObject(ctx, this_val); 41027 class_id = JS_CLASS_ARRAY_ITERATOR; 41028 } 41029 if (JS_IsException(arr)) 41030 goto fail; 41031 enum_obj = JS_NewObjectClass(ctx, class_id); 41032 if (JS_IsException(enum_obj)) 41033 goto fail; 41034 it = js_malloc(ctx, sizeof(*it)); 41035 if (!it) 41036 goto fail1; 41037 it->obj = arr; 41038 it->kind = kind; 41039 it->idx = 0; 41040 JS_SetOpaque(enum_obj, it); 41041 return enum_obj; 41042 fail1: 41043 JS_FreeValue(ctx, enum_obj); 41044 fail: 41045 JS_FreeValue(ctx, arr); 41046 return JS_EXCEPTION; 41047 } 41048 41049 static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val, 41050 int argc, JSValueConst *argv, 41051 BOOL *pdone, int magic) 41052 { 41053 JSArrayIteratorData *it; 41054 uint32_t len, idx; 41055 JSValue val, obj; 41056 JSObject *p; 41057 41058 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR); 41059 if (!it) 41060 goto fail1; 41061 if (JS_IsUndefined(it->obj)) 41062 goto done; 41063 p = JS_VALUE_GET_OBJ(it->obj); 41064 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 41065 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 41066 if (typed_array_is_detached(ctx, p)) { 41067 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 41068 goto fail1; 41069 } 41070 len = p->u.array.count; 41071 } else { 41072 if (js_get_length32(ctx, &len, it->obj)) { 41073 fail1: 41074 *pdone = FALSE; 41075 return JS_EXCEPTION; 41076 } 41077 } 41078 idx = it->idx; 41079 if (idx >= len) { 41080 JS_FreeValue(ctx, it->obj); 41081 it->obj = JS_UNDEFINED; 41082 done: 41083 *pdone = TRUE; 41084 return JS_UNDEFINED; 41085 } 41086 it->idx = idx + 1; 41087 *pdone = FALSE; 41088 if (it->kind == JS_ITERATOR_KIND_KEY) { 41089 return JS_NewUint32(ctx, idx); 41090 } else { 41091 val = JS_GetPropertyUint32(ctx, it->obj, idx); 41092 if (JS_IsException(val)) 41093 return JS_EXCEPTION; 41094 if (it->kind == JS_ITERATOR_KIND_VALUE) { 41095 return val; 41096 } else { 41097 JSValueConst args[2]; 41098 JSValue num; 41099 num = JS_NewUint32(ctx, idx); 41100 args[0] = num; 41101 args[1] = val; 41102 obj = js_create_array(ctx, 2, args); 41103 JS_FreeValue(ctx, val); 41104 JS_FreeValue(ctx, num); 41105 return obj; 41106 } 41107 } 41108 } 41109 41110 static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val, 41111 int argc, JSValueConst *argv) 41112 { 41113 return JS_DupValue(ctx, this_val); 41114 } 41115 41116 static const JSCFunctionListEntry js_iterator_proto_funcs[] = { 41117 JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ), 41118 }; 41119 41120 static const JSCFunctionListEntry js_array_proto_funcs[] = { 41121 JS_CFUNC_DEF("at", 1, js_array_at ), 41122 JS_CFUNC_DEF("with", 2, js_array_with ), 41123 JS_CFUNC_DEF("concat", 1, js_array_concat ), 41124 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), 41125 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), 41126 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ), 41127 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ), 41128 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ), 41129 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), 41130 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), 41131 JS_CFUNC_DEF("fill", 1, js_array_fill ), 41132 JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ), 41133 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ), 41134 JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ), 41135 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ), 41136 JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), 41137 JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), 41138 JS_CFUNC_DEF("includes", 1, js_array_includes ), 41139 JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ), 41140 JS_CFUNC_DEF("toString", 0, js_array_toString ), 41141 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ), 41142 JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ), 41143 JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ), 41144 JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), 41145 JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), 41146 JS_CFUNC_DEF("reverse", 0, js_array_reverse ), 41147 JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), 41148 JS_CFUNC_DEF("sort", 1, js_array_sort ), 41149 JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), 41150 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), 41151 JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), 41152 JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), 41153 JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), 41154 JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), 41155 JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), 41156 JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ), 41157 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 41158 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ), 41159 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), 41160 }; 41161 41162 static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = { 41163 JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ), 41164 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ), 41165 }; 41166 41167 /* Number */ 41168 41169 static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, 41170 int argc, JSValueConst *argv) 41171 { 41172 JSValue val, obj; 41173 if (argc == 0) { 41174 val = JS_NewInt32(ctx, 0); 41175 } else { 41176 val = JS_ToNumeric(ctx, argv[0]); 41177 if (JS_IsException(val)) 41178 return val; 41179 switch(JS_VALUE_GET_TAG(val)) { 41180 case JS_TAG_BIG_INT: 41181 #ifdef CONFIG_BIGNUM 41182 case JS_TAG_BIG_FLOAT: 41183 #endif 41184 { 41185 JSBigFloat *p = JS_VALUE_GET_PTR(val); 41186 double d; 41187 bf_get_float64(&p->num, &d, BF_RNDN); 41188 JS_FreeValue(ctx, val); 41189 val = __JS_NewFloat64(ctx, d); 41190 } 41191 break; 41192 #ifdef CONFIG_BIGNUM 41193 case JS_TAG_BIG_DECIMAL: 41194 val = JS_ToStringFree(ctx, val); 41195 if (JS_IsException(val)) 41196 return val; 41197 val = JS_ToNumberFree(ctx, val); 41198 if (JS_IsException(val)) 41199 return val; 41200 break; 41201 #endif 41202 default: 41203 break; 41204 } 41205 } 41206 if (!JS_IsUndefined(new_target)) { 41207 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER); 41208 if (!JS_IsException(obj)) 41209 JS_SetObjectData(ctx, obj, val); 41210 return obj; 41211 } else { 41212 return val; 41213 } 41214 } 41215 41216 #if 0 41217 static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val, 41218 int argc, JSValueConst *argv) 41219 { 41220 return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0])); 41221 } 41222 41223 static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val, 41224 int argc, JSValueConst *argv) 41225 { 41226 int64_t v; 41227 if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0]))) 41228 return JS_EXCEPTION; 41229 return JS_NewInt64(ctx, v); 41230 } 41231 #endif 41232 41233 static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val, 41234 int argc, JSValueConst *argv) 41235 { 41236 if (!JS_IsNumber(argv[0])) 41237 return JS_FALSE; 41238 return js_global_isNaN(ctx, this_val, argc, argv); 41239 } 41240 41241 static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val, 41242 int argc, JSValueConst *argv) 41243 { 41244 if (!JS_IsNumber(argv[0])) 41245 return JS_FALSE; 41246 return js_global_isFinite(ctx, this_val, argc, argv); 41247 } 41248 41249 static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val, 41250 int argc, JSValueConst *argv) 41251 { 41252 int ret; 41253 ret = JS_NumberIsInteger(ctx, argv[0]); 41254 if (ret < 0) 41255 return JS_EXCEPTION; 41256 else 41257 return JS_NewBool(ctx, ret); 41258 } 41259 41260 static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val, 41261 int argc, JSValueConst *argv) 41262 { 41263 double d; 41264 if (!JS_IsNumber(argv[0])) 41265 return JS_FALSE; 41266 if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) 41267 return JS_EXCEPTION; 41268 return JS_NewBool(ctx, is_safe_integer(d)); 41269 } 41270 41271 static const JSCFunctionListEntry js_number_funcs[] = { 41272 /* global ParseInt and parseFloat should be defined already or delayed */ 41273 JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ), 41274 JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ), 41275 JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ), 41276 JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ), 41277 JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ), 41278 JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ), 41279 JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ), 41280 JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ), 41281 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), 41282 JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ), 41283 JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ), 41284 JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */ 41285 JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */ 41286 JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */ 41287 //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ), 41288 //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ), 41289 }; 41290 41291 static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val) 41292 { 41293 if (JS_IsNumber(this_val)) 41294 return JS_DupValue(ctx, this_val); 41295 41296 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 41297 JSObject *p = JS_VALUE_GET_OBJ(this_val); 41298 if (p->class_id == JS_CLASS_NUMBER) { 41299 if (JS_IsNumber(p->u.object_data)) 41300 return JS_DupValue(ctx, p->u.object_data); 41301 } 41302 } 41303 return JS_ThrowTypeError(ctx, "not a number"); 41304 } 41305 41306 static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val, 41307 int argc, JSValueConst *argv) 41308 { 41309 return js_thisNumberValue(ctx, this_val); 41310 } 41311 41312 static int js_get_radix(JSContext *ctx, JSValueConst val) 41313 { 41314 int radix; 41315 if (JS_ToInt32Sat(ctx, &radix, val)) 41316 return -1; 41317 if (radix < 2 || radix > 36) { 41318 JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); 41319 return -1; 41320 } 41321 return radix; 41322 } 41323 41324 static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, 41325 int argc, JSValueConst *argv, int magic) 41326 { 41327 JSValue val; 41328 int base; 41329 double d; 41330 41331 val = js_thisNumberValue(ctx, this_val); 41332 if (JS_IsException(val)) 41333 return val; 41334 if (magic || JS_IsUndefined(argv[0])) { 41335 base = 10; 41336 } else { 41337 base = js_get_radix(ctx, argv[0]); 41338 if (base < 0) 41339 goto fail; 41340 } 41341 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { 41342 char buf1[70], *ptr; 41343 ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base); 41344 return JS_NewString(ctx, ptr); 41345 } 41346 if (JS_ToFloat64Free(ctx, &d, val)) 41347 return JS_EXCEPTION; 41348 if (base != 10 && isfinite(d)) { 41349 return js_dtoa_radix(ctx, d, base); 41350 } 41351 return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); 41352 fail: 41353 JS_FreeValue(ctx, val); 41354 return JS_EXCEPTION; 41355 } 41356 41357 static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, 41358 int argc, JSValueConst *argv) 41359 { 41360 JSValue val; 41361 int f; 41362 double d; 41363 41364 val = js_thisNumberValue(ctx, this_val); 41365 if (JS_IsException(val)) 41366 return val; 41367 if (JS_ToFloat64Free(ctx, &d, val)) 41368 return JS_EXCEPTION; 41369 if (JS_ToInt32Sat(ctx, &f, argv[0])) 41370 return JS_EXCEPTION; 41371 if (f < 0 || f > 100) 41372 return JS_ThrowRangeError(ctx, "invalid number of digits"); 41373 if (fabs(d) >= 1e21) { 41374 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); 41375 } else { 41376 return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); 41377 } 41378 } 41379 41380 static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, 41381 int argc, JSValueConst *argv) 41382 { 41383 JSValue val; 41384 int f, flags; 41385 double d; 41386 41387 val = js_thisNumberValue(ctx, this_val); 41388 if (JS_IsException(val)) 41389 return val; 41390 if (JS_ToFloat64Free(ctx, &d, val)) 41391 return JS_EXCEPTION; 41392 if (JS_ToInt32Sat(ctx, &f, argv[0])) 41393 return JS_EXCEPTION; 41394 if (!isfinite(d)) { 41395 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); 41396 } 41397 if (JS_IsUndefined(argv[0])) { 41398 flags = 0; 41399 f = 0; 41400 } else { 41401 if (f < 0 || f > 100) 41402 return JS_ThrowRangeError(ctx, "invalid number of digits"); 41403 f++; 41404 flags = JS_DTOA_FIXED_FORMAT; 41405 } 41406 return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP); 41407 } 41408 41409 static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, 41410 int argc, JSValueConst *argv) 41411 { 41412 JSValue val; 41413 int p; 41414 double d; 41415 41416 val = js_thisNumberValue(ctx, this_val); 41417 if (JS_IsException(val)) 41418 return val; 41419 if (JS_ToFloat64Free(ctx, &d, val)) 41420 return JS_EXCEPTION; 41421 if (JS_IsUndefined(argv[0])) 41422 goto to_string; 41423 if (JS_ToInt32Sat(ctx, &p, argv[0])) 41424 return JS_EXCEPTION; 41425 if (!isfinite(d)) { 41426 to_string: 41427 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); 41428 } 41429 if (p < 1 || p > 100) 41430 return JS_ThrowRangeError(ctx, "invalid number of digits"); 41431 return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT); 41432 } 41433 41434 static const JSCFunctionListEntry js_number_proto_funcs[] = { 41435 JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ), 41436 JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ), 41437 JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ), 41438 JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ), 41439 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ), 41440 JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ), 41441 }; 41442 41443 static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val, 41444 int argc, JSValueConst *argv) 41445 { 41446 const char *str, *p; 41447 int radix, flags; 41448 JSValue ret; 41449 41450 str = JS_ToCString(ctx, argv[0]); 41451 if (!str) 41452 return JS_EXCEPTION; 41453 if (JS_ToInt32(ctx, &radix, argv[1])) { 41454 JS_FreeCString(ctx, str); 41455 return JS_EXCEPTION; 41456 } 41457 if (radix != 0 && (radix < 2 || radix > 36)) { 41458 ret = JS_NAN; 41459 } else { 41460 p = str; 41461 p += skip_spaces(p); 41462 flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN; 41463 ret = js_atof(ctx, p, NULL, radix, flags); 41464 } 41465 JS_FreeCString(ctx, str); 41466 return ret; 41467 } 41468 41469 static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val, 41470 int argc, JSValueConst *argv) 41471 { 41472 const char *str, *p; 41473 JSValue ret; 41474 41475 str = JS_ToCString(ctx, argv[0]); 41476 if (!str) 41477 return JS_EXCEPTION; 41478 p = str; 41479 p += skip_spaces(p); 41480 ret = js_atof(ctx, p, NULL, 10, 0); 41481 JS_FreeCString(ctx, str); 41482 return ret; 41483 } 41484 41485 /* Boolean */ 41486 static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target, 41487 int argc, JSValueConst *argv) 41488 { 41489 JSValue val, obj; 41490 val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0])); 41491 if (!JS_IsUndefined(new_target)) { 41492 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN); 41493 if (!JS_IsException(obj)) 41494 JS_SetObjectData(ctx, obj, val); 41495 return obj; 41496 } else { 41497 return val; 41498 } 41499 } 41500 41501 static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val) 41502 { 41503 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL) 41504 return JS_DupValue(ctx, this_val); 41505 41506 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 41507 JSObject *p = JS_VALUE_GET_OBJ(this_val); 41508 if (p->class_id == JS_CLASS_BOOLEAN) { 41509 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL) 41510 return p->u.object_data; 41511 } 41512 } 41513 return JS_ThrowTypeError(ctx, "not a boolean"); 41514 } 41515 41516 static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val, 41517 int argc, JSValueConst *argv) 41518 { 41519 JSValue val = js_thisBooleanValue(ctx, this_val); 41520 if (JS_IsException(val)) 41521 return val; 41522 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? 41523 JS_ATOM_true : JS_ATOM_false); 41524 } 41525 41526 static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val, 41527 int argc, JSValueConst *argv) 41528 { 41529 return js_thisBooleanValue(ctx, this_val); 41530 } 41531 41532 static const JSCFunctionListEntry js_boolean_proto_funcs[] = { 41533 JS_CFUNC_DEF("toString", 0, js_boolean_toString ), 41534 JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ), 41535 }; 41536 41537 /* String */ 41538 41539 static int js_string_get_own_property(JSContext *ctx, 41540 JSPropertyDescriptor *desc, 41541 JSValueConst obj, JSAtom prop) 41542 { 41543 JSObject *p; 41544 JSString *p1; 41545 uint32_t idx, ch; 41546 41547 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ 41548 if (__JS_AtomIsTaggedInt(prop)) { 41549 p = JS_VALUE_GET_OBJ(obj); 41550 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { 41551 p1 = JS_VALUE_GET_STRING(p->u.object_data); 41552 idx = __JS_AtomToUInt32(prop); 41553 if (idx < p1->len) { 41554 if (desc) { 41555 ch = string_get(p1, idx); 41556 desc->flags = JS_PROP_ENUMERABLE; 41557 desc->value = js_new_string_char(ctx, ch); 41558 desc->getter = JS_UNDEFINED; 41559 desc->setter = JS_UNDEFINED; 41560 } 41561 return TRUE; 41562 } 41563 } 41564 } 41565 return FALSE; 41566 } 41567 41568 static int js_string_define_own_property(JSContext *ctx, 41569 JSValueConst this_obj, 41570 JSAtom prop, JSValueConst val, 41571 JSValueConst getter, 41572 JSValueConst setter, int flags) 41573 { 41574 uint32_t idx; 41575 JSObject *p; 41576 JSString *p1, *p2; 41577 41578 if (__JS_AtomIsTaggedInt(prop)) { 41579 idx = __JS_AtomToUInt32(prop); 41580 p = JS_VALUE_GET_OBJ(this_obj); 41581 if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) 41582 goto def; 41583 p1 = JS_VALUE_GET_STRING(p->u.object_data); 41584 if (idx >= p1->len) 41585 goto def; 41586 if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags)) 41587 goto fail; 41588 /* check that the same value is configured */ 41589 if (flags & JS_PROP_HAS_VALUE) { 41590 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) 41591 goto fail; 41592 p2 = JS_VALUE_GET_STRING(val); 41593 if (p2->len != 1) 41594 goto fail; 41595 if (string_get(p1, idx) != string_get(p2, 0)) { 41596 fail: 41597 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); 41598 } 41599 } 41600 return TRUE; 41601 } else { 41602 def: 41603 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, 41604 flags | JS_PROP_NO_EXOTIC); 41605 } 41606 } 41607 41608 static int js_string_delete_property(JSContext *ctx, 41609 JSValueConst obj, JSAtom prop) 41610 { 41611 uint32_t idx; 41612 41613 if (__JS_AtomIsTaggedInt(prop)) { 41614 idx = __JS_AtomToUInt32(prop); 41615 if (idx < js_string_obj_get_length(ctx, obj)) { 41616 return FALSE; 41617 } 41618 } 41619 return TRUE; 41620 } 41621 41622 static const JSClassExoticMethods js_string_exotic_methods = { 41623 .get_own_property = js_string_get_own_property, 41624 .define_own_property = js_string_define_own_property, 41625 .delete_property = js_string_delete_property, 41626 }; 41627 41628 static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target, 41629 int argc, JSValueConst *argv) 41630 { 41631 JSValue val, obj; 41632 if (argc == 0) { 41633 val = JS_AtomToString(ctx, JS_ATOM_empty_string); 41634 } else { 41635 if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) { 41636 JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]); 41637 val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")"); 41638 } else { 41639 val = JS_ToString(ctx, argv[0]); 41640 } 41641 if (JS_IsException(val)) 41642 return val; 41643 } 41644 if (!JS_IsUndefined(new_target)) { 41645 JSString *p1 = JS_VALUE_GET_STRING(val); 41646 41647 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING); 41648 if (!JS_IsException(obj)) { 41649 JS_SetObjectData(ctx, obj, val); 41650 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0); 41651 } 41652 return obj; 41653 } else { 41654 return val; 41655 } 41656 } 41657 41658 static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val) 41659 { 41660 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING) 41661 return JS_DupValue(ctx, this_val); 41662 41663 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 41664 JSObject *p = JS_VALUE_GET_OBJ(this_val); 41665 if (p->class_id == JS_CLASS_STRING) { 41666 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) 41667 return JS_DupValue(ctx, p->u.object_data); 41668 } 41669 } 41670 return JS_ThrowTypeError(ctx, "not a string"); 41671 } 41672 41673 static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val, 41674 int argc, JSValueConst *argv) 41675 { 41676 int i; 41677 StringBuffer b_s, *b = &b_s; 41678 41679 string_buffer_init(ctx, b, argc); 41680 41681 for(i = 0; i < argc; i++) { 41682 int32_t c; 41683 if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) { 41684 string_buffer_free(b); 41685 return JS_EXCEPTION; 41686 } 41687 } 41688 return string_buffer_end(b); 41689 } 41690 41691 static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, 41692 int argc, JSValueConst *argv) 41693 { 41694 double d; 41695 int i, c; 41696 StringBuffer b_s, *b = &b_s; 41697 41698 /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */ 41699 41700 if (string_buffer_init(ctx, b, argc)) 41701 goto fail; 41702 for(i = 0; i < argc; i++) { 41703 if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) { 41704 c = JS_VALUE_GET_INT(argv[i]); 41705 if (c < 0 || c > 0x10ffff) 41706 goto range_error; 41707 } else { 41708 if (JS_ToFloat64(ctx, &d, argv[i])) 41709 goto fail; 41710 if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d) 41711 goto range_error; 41712 } 41713 if (string_buffer_putc(b, c)) 41714 goto fail; 41715 } 41716 return string_buffer_end(b); 41717 41718 range_error: 41719 JS_ThrowRangeError(ctx, "invalid code point"); 41720 fail: 41721 string_buffer_free(b); 41722 return JS_EXCEPTION; 41723 } 41724 41725 static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val, 41726 int argc, JSValueConst *argv) 41727 { 41728 // raw(temp,...a) 41729 JSValue cooked, val, raw; 41730 StringBuffer b_s, *b = &b_s; 41731 int64_t i, n; 41732 41733 string_buffer_init(ctx, b, 0); 41734 raw = JS_UNDEFINED; 41735 cooked = JS_ToObject(ctx, argv[0]); 41736 if (JS_IsException(cooked)) 41737 goto exception; 41738 raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw)); 41739 if (JS_IsException(raw)) 41740 goto exception; 41741 if (js_get_length64(ctx, &n, raw) < 0) 41742 goto exception; 41743 41744 for (i = 0; i < n; i++) { 41745 val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i)); 41746 if (JS_IsException(val)) 41747 goto exception; 41748 string_buffer_concat_value_free(b, val); 41749 if (i < n - 1 && i + 1 < argc) { 41750 if (string_buffer_concat_value(b, argv[i + 1])) 41751 goto exception; 41752 } 41753 } 41754 JS_FreeValue(ctx, cooked); 41755 JS_FreeValue(ctx, raw); 41756 return string_buffer_end(b); 41757 41758 exception: 41759 JS_FreeValue(ctx, cooked); 41760 JS_FreeValue(ctx, raw); 41761 string_buffer_free(b); 41762 return JS_EXCEPTION; 41763 } 41764 41765 /* only used in test262 */ 41766 JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, 41767 int argc, JSValueConst *argv) 41768 { 41769 uint32_t start, end, i, n; 41770 StringBuffer b_s, *b = &b_s; 41771 41772 if (JS_ToUint32(ctx, &start, argv[0]) || 41773 JS_ToUint32(ctx, &end, argv[1])) 41774 return JS_EXCEPTION; 41775 end = min_uint32(end, 0x10ffff + 1); 41776 41777 if (start > end) { 41778 start = end; 41779 } 41780 n = end - start; 41781 if (end > 0x10000) { 41782 n += end - max_uint32(start, 0x10000); 41783 } 41784 if (string_buffer_init2(ctx, b, n, end >= 0x100)) 41785 return JS_EXCEPTION; 41786 for(i = start; i < end; i++) { 41787 string_buffer_putc(b, i); 41788 } 41789 return string_buffer_end(b); 41790 } 41791 41792 #if 0 41793 static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val, 41794 int argc, JSValueConst *argv) 41795 { 41796 int c; 41797 if (JS_ToInt32(ctx, &c, argv[0])) 41798 return JS_EXCEPTION; 41799 return JS_NewBool(ctx, lre_is_space(c)); 41800 } 41801 #endif 41802 41803 static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, 41804 int argc, JSValueConst *argv) 41805 { 41806 JSValue val, ret; 41807 JSString *p; 41808 int idx, c; 41809 41810 val = JS_ToStringCheckObject(ctx, this_val); 41811 if (JS_IsException(val)) 41812 return val; 41813 p = JS_VALUE_GET_STRING(val); 41814 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 41815 JS_FreeValue(ctx, val); 41816 return JS_EXCEPTION; 41817 } 41818 if (idx < 0 || idx >= p->len) { 41819 ret = JS_NAN; 41820 } else { 41821 c = string_get(p, idx); 41822 ret = JS_NewInt32(ctx, c); 41823 } 41824 JS_FreeValue(ctx, val); 41825 return ret; 41826 } 41827 41828 static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, 41829 int argc, JSValueConst *argv, int is_at) 41830 { 41831 JSValue val, ret; 41832 JSString *p; 41833 int idx, c; 41834 41835 val = JS_ToStringCheckObject(ctx, this_val); 41836 if (JS_IsException(val)) 41837 return val; 41838 p = JS_VALUE_GET_STRING(val); 41839 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 41840 JS_FreeValue(ctx, val); 41841 return JS_EXCEPTION; 41842 } 41843 if (idx < 0 && is_at) 41844 idx += p->len; 41845 if (idx < 0 || idx >= p->len) { 41846 if (is_at) 41847 ret = JS_UNDEFINED; 41848 else 41849 ret = js_new_string8(ctx, NULL, 0); 41850 } else { 41851 c = string_get(p, idx); 41852 ret = js_new_string_char(ctx, c); 41853 } 41854 JS_FreeValue(ctx, val); 41855 return ret; 41856 } 41857 41858 static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val, 41859 int argc, JSValueConst *argv) 41860 { 41861 JSValue val, ret; 41862 JSString *p; 41863 int idx, c; 41864 41865 val = JS_ToStringCheckObject(ctx, this_val); 41866 if (JS_IsException(val)) 41867 return val; 41868 p = JS_VALUE_GET_STRING(val); 41869 if (JS_ToInt32Sat(ctx, &idx, argv[0])) { 41870 JS_FreeValue(ctx, val); 41871 return JS_EXCEPTION; 41872 } 41873 if (idx < 0 || idx >= p->len) { 41874 ret = JS_UNDEFINED; 41875 } else { 41876 c = string_getc(p, &idx); 41877 ret = JS_NewInt32(ctx, c); 41878 } 41879 JS_FreeValue(ctx, val); 41880 return ret; 41881 } 41882 41883 static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val, 41884 int argc, JSValueConst *argv) 41885 { 41886 JSValue r; 41887 int i; 41888 41889 /* XXX: Use more efficient method */ 41890 /* XXX: This method is OK if r has a single refcount */ 41891 /* XXX: should use string_buffer? */ 41892 r = JS_ToStringCheckObject(ctx, this_val); 41893 for (i = 0; i < argc; i++) { 41894 if (JS_IsException(r)) 41895 break; 41896 r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i])); 41897 } 41898 return r; 41899 } 41900 41901 static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len) 41902 { 41903 int i, c1, c2; 41904 for (i = 0; i < len; i++) { 41905 if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i))) 41906 return c1 - c2; 41907 } 41908 return 0; 41909 } 41910 41911 static int string_indexof_char(JSString *p, int c, int from) 41912 { 41913 /* assuming 0 <= from <= p->len */ 41914 int i, len = p->len; 41915 if (p->is_wide_char) { 41916 for (i = from; i < len; i++) { 41917 if (p->u.str16[i] == c) 41918 return i; 41919 } 41920 } else { 41921 if ((c & ~0xff) == 0) { 41922 for (i = from; i < len; i++) { 41923 if (p->u.str8[i] == (uint8_t)c) 41924 return i; 41925 } 41926 } 41927 } 41928 return -1; 41929 } 41930 41931 static int string_indexof(JSString *p1, JSString *p2, int from) 41932 { 41933 /* assuming 0 <= from <= p1->len */ 41934 int c, i, j, len1 = p1->len, len2 = p2->len; 41935 if (len2 == 0) 41936 return from; 41937 for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) { 41938 j = string_indexof_char(p1, c, i); 41939 if (j < 0 || j + len2 > len1) 41940 break; 41941 if (!string_cmp(p1, p2, j + 1, 1, len2 - 1)) 41942 return j; 41943 } 41944 return -1; 41945 } 41946 41947 static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) 41948 { 41949 if (!unicode || index >= p->len || !p->is_wide_char) { 41950 index++; 41951 } else { 41952 int index32 = (int)index; 41953 string_getc(p, &index32); 41954 index = index32; 41955 } 41956 return index; 41957 } 41958 41959 /* return the position of the first invalid character in the string or 41960 -1 if none */ 41961 static int js_string_find_invalid_codepoint(JSString *p) 41962 { 41963 int i; 41964 if (!p->is_wide_char) 41965 return -1; 41966 for(i = 0; i < p->len; i++) { 41967 uint32_t c = p->u.str16[i]; 41968 if (is_surrogate(c)) { 41969 if (is_hi_surrogate(c) && (i + 1) < p->len 41970 && is_lo_surrogate(p->u.str16[i + 1])) { 41971 i++; 41972 } else { 41973 return i; 41974 } 41975 } 41976 } 41977 return -1; 41978 } 41979 41980 static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val, 41981 int argc, JSValueConst *argv) 41982 { 41983 JSValue str; 41984 JSString *p; 41985 BOOL ret; 41986 41987 str = JS_ToStringCheckObject(ctx, this_val); 41988 if (JS_IsException(str)) 41989 return JS_EXCEPTION; 41990 p = JS_VALUE_GET_STRING(str); 41991 ret = (js_string_find_invalid_codepoint(p) < 0); 41992 JS_FreeValue(ctx, str); 41993 return JS_NewBool(ctx, ret); 41994 } 41995 41996 static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val, 41997 int argc, JSValueConst *argv) 41998 { 41999 JSValue str, ret; 42000 JSString *p; 42001 int i; 42002 42003 str = JS_ToStringCheckObject(ctx, this_val); 42004 if (JS_IsException(str)) 42005 return JS_EXCEPTION; 42006 42007 p = JS_VALUE_GET_STRING(str); 42008 /* avoid reallocating the string if it is well-formed */ 42009 i = js_string_find_invalid_codepoint(p); 42010 if (i < 0) 42011 return str; 42012 42013 ret = js_new_string16(ctx, p->u.str16, p->len); 42014 JS_FreeValue(ctx, str); 42015 if (JS_IsException(ret)) 42016 return JS_EXCEPTION; 42017 42018 p = JS_VALUE_GET_STRING(ret); 42019 for (; i < p->len; i++) { 42020 uint32_t c = p->u.str16[i]; 42021 if (is_surrogate(c)) { 42022 if (is_hi_surrogate(c) && (i + 1) < p->len 42023 && is_lo_surrogate(p->u.str16[i + 1])) { 42024 i++; 42025 } else { 42026 p->u.str16[i] = 0xFFFD; 42027 } 42028 } 42029 } 42030 return ret; 42031 } 42032 42033 static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, 42034 int argc, JSValueConst *argv, int lastIndexOf) 42035 { 42036 JSValue str, v; 42037 int i, len, v_len, pos, start, stop, ret, inc; 42038 JSString *p; 42039 JSString *p1; 42040 42041 str = JS_ToStringCheckObject(ctx, this_val); 42042 if (JS_IsException(str)) 42043 return str; 42044 v = JS_ToString(ctx, argv[0]); 42045 if (JS_IsException(v)) 42046 goto fail; 42047 p = JS_VALUE_GET_STRING(str); 42048 p1 = JS_VALUE_GET_STRING(v); 42049 len = p->len; 42050 v_len = p1->len; 42051 if (lastIndexOf) { 42052 pos = len - v_len; 42053 if (argc > 1) { 42054 double d; 42055 if (JS_ToFloat64(ctx, &d, argv[1])) 42056 goto fail; 42057 if (!isnan(d)) { 42058 if (d <= 0) 42059 pos = 0; 42060 else if (d < pos) 42061 pos = d; 42062 } 42063 } 42064 start = pos; 42065 stop = 0; 42066 inc = -1; 42067 } else { 42068 pos = 0; 42069 if (argc > 1) { 42070 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) 42071 goto fail; 42072 } 42073 start = pos; 42074 stop = len - v_len; 42075 inc = 1; 42076 } 42077 ret = -1; 42078 if (len >= v_len && inc * (stop - start) >= 0) { 42079 for (i = start;; i += inc) { 42080 if (!string_cmp(p, p1, i, 0, v_len)) { 42081 ret = i; 42082 break; 42083 } 42084 if (i == stop) 42085 break; 42086 } 42087 } 42088 JS_FreeValue(ctx, str); 42089 JS_FreeValue(ctx, v); 42090 return JS_NewInt32(ctx, ret); 42091 42092 fail: 42093 JS_FreeValue(ctx, str); 42094 JS_FreeValue(ctx, v); 42095 return JS_EXCEPTION; 42096 } 42097 42098 /* return < 0 if exception or TRUE/FALSE */ 42099 static int js_is_regexp(JSContext *ctx, JSValueConst obj); 42100 42101 static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, 42102 int argc, JSValueConst *argv, int magic) 42103 { 42104 JSValue str, v = JS_UNDEFINED; 42105 int i, len, v_len, pos, start, stop, ret; 42106 JSString *p; 42107 JSString *p1; 42108 42109 str = JS_ToStringCheckObject(ctx, this_val); 42110 if (JS_IsException(str)) 42111 return str; 42112 ret = js_is_regexp(ctx, argv[0]); 42113 if (ret) { 42114 if (ret > 0) 42115 JS_ThrowTypeError(ctx, "regexp not supported"); 42116 goto fail; 42117 } 42118 v = JS_ToString(ctx, argv[0]); 42119 if (JS_IsException(v)) 42120 goto fail; 42121 p = JS_VALUE_GET_STRING(str); 42122 p1 = JS_VALUE_GET_STRING(v); 42123 len = p->len; 42124 v_len = p1->len; 42125 pos = (magic == 2) ? len : 0; 42126 if (argc > 1 && !JS_IsUndefined(argv[1])) { 42127 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0)) 42128 goto fail; 42129 } 42130 len -= v_len; 42131 ret = 0; 42132 if (magic == 0) { 42133 start = pos; 42134 stop = len; 42135 } else { 42136 if (magic == 1) { 42137 if (pos > len) 42138 goto done; 42139 } else { 42140 pos -= v_len; 42141 } 42142 start = stop = pos; 42143 } 42144 if (start >= 0 && start <= stop) { 42145 for (i = start;; i++) { 42146 if (!string_cmp(p, p1, i, 0, v_len)) { 42147 ret = 1; 42148 break; 42149 } 42150 if (i == stop) 42151 break; 42152 } 42153 } 42154 done: 42155 JS_FreeValue(ctx, str); 42156 JS_FreeValue(ctx, v); 42157 return JS_NewBool(ctx, ret); 42158 42159 fail: 42160 JS_FreeValue(ctx, str); 42161 JS_FreeValue(ctx, v); 42162 return JS_EXCEPTION; 42163 } 42164 42165 static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp) 42166 { 42167 int ret; 42168 JSValue flags; 42169 42170 ret = js_is_regexp(ctx, regexp); 42171 if (ret < 0) 42172 return -1; 42173 if (ret) { 42174 flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags); 42175 if (JS_IsException(flags)) 42176 return -1; 42177 if (JS_IsUndefined(flags) || JS_IsNull(flags)) { 42178 JS_ThrowTypeError(ctx, "cannot convert to object"); 42179 return -1; 42180 } 42181 flags = JS_ToStringFree(ctx, flags); 42182 if (JS_IsException(flags)) 42183 return -1; 42184 ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0); 42185 JS_FreeValue(ctx, flags); 42186 if (ret < 0) { 42187 JS_ThrowTypeError(ctx, "regexp must have the 'g' flag"); 42188 return -1; 42189 } 42190 } 42191 return 0; 42192 } 42193 42194 static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, 42195 int argc, JSValueConst *argv, int atom) 42196 { 42197 // match(rx), search(rx), matchAll(rx) 42198 // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll 42199 JSValueConst O = this_val, regexp = argv[0], args[2]; 42200 JSValue matcher, S, rx, result, str; 42201 int args_len; 42202 42203 if (JS_IsUndefined(O) || JS_IsNull(O)) 42204 return JS_ThrowTypeError(ctx, "cannot convert to object"); 42205 42206 if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) { 42207 matcher = JS_GetProperty(ctx, regexp, atom); 42208 if (JS_IsException(matcher)) 42209 return JS_EXCEPTION; 42210 if (atom == JS_ATOM_Symbol_matchAll) { 42211 if (check_regexp_g_flag(ctx, regexp) < 0) { 42212 JS_FreeValue(ctx, matcher); 42213 return JS_EXCEPTION; 42214 } 42215 } 42216 if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) { 42217 return JS_CallFree(ctx, matcher, regexp, 1, &O); 42218 } 42219 } 42220 S = JS_ToString(ctx, O); 42221 if (JS_IsException(S)) 42222 return JS_EXCEPTION; 42223 args_len = 1; 42224 args[0] = regexp; 42225 str = JS_UNDEFINED; 42226 if (atom == JS_ATOM_Symbol_matchAll) { 42227 str = JS_NewString(ctx, "g"); 42228 if (JS_IsException(str)) 42229 goto fail; 42230 args[args_len++] = (JSValueConst)str; 42231 } 42232 rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); 42233 JS_FreeValue(ctx, str); 42234 if (JS_IsException(rx)) { 42235 fail: 42236 JS_FreeValue(ctx, S); 42237 return JS_EXCEPTION; 42238 } 42239 result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); 42240 JS_FreeValue(ctx, S); 42241 return result; 42242 } 42243 42244 static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val, 42245 int argc, JSValueConst *argv) 42246 { 42247 // GetSubstitution(matched, str, position, captures, namedCaptures, rep) 42248 JSValueConst matched, str, captures, namedCaptures, rep; 42249 JSValue capture, name, s; 42250 uint32_t position, len, matched_len, captures_len; 42251 int i, j, j0, k, k1; 42252 int c, c1; 42253 StringBuffer b_s, *b = &b_s; 42254 JSString *sp, *rp; 42255 42256 matched = argv[0]; 42257 str = argv[1]; 42258 captures = argv[3]; 42259 namedCaptures = argv[4]; 42260 rep = argv[5]; 42261 42262 if (!JS_IsString(rep) || !JS_IsString(str)) 42263 return JS_ThrowTypeError(ctx, "not a string"); 42264 42265 sp = JS_VALUE_GET_STRING(str); 42266 rp = JS_VALUE_GET_STRING(rep); 42267 42268 string_buffer_init(ctx, b, 0); 42269 42270 captures_len = 0; 42271 if (!JS_IsUndefined(captures)) { 42272 if (js_get_length32(ctx, &captures_len, captures)) 42273 goto exception; 42274 } 42275 if (js_get_length32(ctx, &matched_len, matched)) 42276 goto exception; 42277 if (JS_ToUint32(ctx, &position, argv[2]) < 0) 42278 goto exception; 42279 42280 len = rp->len; 42281 i = 0; 42282 for(;;) { 42283 j = string_indexof_char(rp, '$', i); 42284 if (j < 0 || j + 1 >= len) 42285 break; 42286 string_buffer_concat(b, rp, i, j); 42287 j0 = j++; 42288 c = string_get(rp, j++); 42289 if (c == '$') { 42290 string_buffer_putc8(b, '$'); 42291 } else if (c == '&') { 42292 if (string_buffer_concat_value(b, matched)) 42293 goto exception; 42294 } else if (c == '`') { 42295 string_buffer_concat(b, sp, 0, position); 42296 } else if (c == '\'') { 42297 string_buffer_concat(b, sp, position + matched_len, sp->len); 42298 } else if (c >= '0' && c <= '9') { 42299 k = c - '0'; 42300 if (j < len) { 42301 c1 = string_get(rp, j); 42302 if (c1 >= '0' && c1 <= '9') { 42303 /* This behavior is specified in ES6 and refined in ECMA 2019 */ 42304 /* ECMA 2019 does not have the extra test, but 42305 Test262 S15.5.4.11_A3_T1..3 require this behavior */ 42306 k1 = k * 10 + c1 - '0'; 42307 if (k1 >= 1 && k1 < captures_len) { 42308 k = k1; 42309 j++; 42310 } 42311 } 42312 } 42313 if (k >= 1 && k < captures_len) { 42314 s = JS_GetPropertyInt64(ctx, captures, k); 42315 if (JS_IsException(s)) 42316 goto exception; 42317 if (!JS_IsUndefined(s)) { 42318 if (string_buffer_concat_value_free(b, s)) 42319 goto exception; 42320 } 42321 } else { 42322 goto norep; 42323 } 42324 } else if (c == '<' && !JS_IsUndefined(namedCaptures)) { 42325 k = string_indexof_char(rp, '>', j); 42326 if (k < 0) 42327 goto norep; 42328 name = js_sub_string(ctx, rp, j, k); 42329 if (JS_IsException(name)) 42330 goto exception; 42331 capture = JS_GetPropertyValue(ctx, namedCaptures, name); 42332 if (JS_IsException(capture)) 42333 goto exception; 42334 if (!JS_IsUndefined(capture)) { 42335 if (string_buffer_concat_value_free(b, capture)) 42336 goto exception; 42337 } 42338 j = k + 1; 42339 } else { 42340 norep: 42341 string_buffer_concat(b, rp, j0, j); 42342 } 42343 i = j; 42344 } 42345 string_buffer_concat(b, rp, i, rp->len); 42346 return string_buffer_end(b); 42347 exception: 42348 string_buffer_free(b); 42349 return JS_EXCEPTION; 42350 } 42351 42352 static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val, 42353 int argc, JSValueConst *argv, 42354 int is_replaceAll) 42355 { 42356 // replace(rx, rep) 42357 JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1]; 42358 JSValueConst args[6]; 42359 JSValue str, search_str, replaceValue_str, repl_str; 42360 JSString *sp, *searchp; 42361 StringBuffer b_s, *b = &b_s; 42362 int pos, functionalReplace, endOfLastMatch; 42363 BOOL is_first; 42364 42365 if (JS_IsUndefined(O) || JS_IsNull(O)) 42366 return JS_ThrowTypeError(ctx, "cannot convert to object"); 42367 42368 search_str = JS_UNDEFINED; 42369 replaceValue_str = JS_UNDEFINED; 42370 repl_str = JS_UNDEFINED; 42371 42372 if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) { 42373 JSValue replacer; 42374 if (is_replaceAll) { 42375 if (check_regexp_g_flag(ctx, searchValue) < 0) 42376 return JS_EXCEPTION; 42377 } 42378 replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace); 42379 if (JS_IsException(replacer)) 42380 return JS_EXCEPTION; 42381 if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) { 42382 args[0] = O; 42383 args[1] = replaceValue; 42384 return JS_CallFree(ctx, replacer, searchValue, 2, args); 42385 } 42386 } 42387 string_buffer_init(ctx, b, 0); 42388 42389 str = JS_ToString(ctx, O); 42390 if (JS_IsException(str)) 42391 goto exception; 42392 search_str = JS_ToString(ctx, searchValue); 42393 if (JS_IsException(search_str)) 42394 goto exception; 42395 functionalReplace = JS_IsFunction(ctx, replaceValue); 42396 if (!functionalReplace) { 42397 replaceValue_str = JS_ToString(ctx, replaceValue); 42398 if (JS_IsException(replaceValue_str)) 42399 goto exception; 42400 } 42401 42402 sp = JS_VALUE_GET_STRING(str); 42403 searchp = JS_VALUE_GET_STRING(search_str); 42404 endOfLastMatch = 0; 42405 is_first = TRUE; 42406 for(;;) { 42407 if (unlikely(searchp->len == 0)) { 42408 if (is_first) 42409 pos = 0; 42410 else if (endOfLastMatch >= sp->len) 42411 pos = -1; 42412 else 42413 pos = endOfLastMatch + 1; 42414 } else { 42415 pos = string_indexof(sp, searchp, endOfLastMatch); 42416 } 42417 if (pos < 0) { 42418 if (is_first) { 42419 string_buffer_free(b); 42420 JS_FreeValue(ctx, search_str); 42421 JS_FreeValue(ctx, replaceValue_str); 42422 return str; 42423 } else { 42424 break; 42425 } 42426 } 42427 if (functionalReplace) { 42428 args[0] = search_str; 42429 args[1] = JS_NewInt32(ctx, pos); 42430 args[2] = str; 42431 repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args)); 42432 } else { 42433 args[0] = search_str; 42434 args[1] = str; 42435 args[2] = JS_NewInt32(ctx, pos); 42436 args[3] = JS_UNDEFINED; 42437 args[4] = JS_UNDEFINED; 42438 args[5] = replaceValue_str; 42439 repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); 42440 } 42441 if (JS_IsException(repl_str)) 42442 goto exception; 42443 42444 string_buffer_concat(b, sp, endOfLastMatch, pos); 42445 string_buffer_concat_value_free(b, repl_str); 42446 endOfLastMatch = pos + searchp->len; 42447 is_first = FALSE; 42448 if (!is_replaceAll) 42449 break; 42450 } 42451 string_buffer_concat(b, sp, endOfLastMatch, sp->len); 42452 JS_FreeValue(ctx, search_str); 42453 JS_FreeValue(ctx, replaceValue_str); 42454 JS_FreeValue(ctx, str); 42455 return string_buffer_end(b); 42456 42457 exception: 42458 string_buffer_free(b); 42459 JS_FreeValue(ctx, search_str); 42460 JS_FreeValue(ctx, replaceValue_str); 42461 JS_FreeValue(ctx, str); 42462 return JS_EXCEPTION; 42463 } 42464 42465 static JSValue js_string_split(JSContext *ctx, JSValueConst this_val, 42466 int argc, JSValueConst *argv) 42467 { 42468 // split(sep, limit) 42469 JSValueConst O = this_val, separator = argv[0], limit = argv[1]; 42470 JSValueConst args[2]; 42471 JSValue S, A, R, T; 42472 uint32_t lim, lengthA; 42473 int64_t p, q, s, r, e; 42474 JSString *sp, *rp; 42475 42476 if (JS_IsUndefined(O) || JS_IsNull(O)) 42477 return JS_ThrowTypeError(ctx, "cannot convert to object"); 42478 42479 S = JS_UNDEFINED; 42480 A = JS_UNDEFINED; 42481 R = JS_UNDEFINED; 42482 42483 if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) { 42484 JSValue splitter; 42485 splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split); 42486 if (JS_IsException(splitter)) 42487 return JS_EXCEPTION; 42488 if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) { 42489 args[0] = O; 42490 args[1] = limit; 42491 return JS_CallFree(ctx, splitter, separator, 2, args); 42492 } 42493 } 42494 S = JS_ToString(ctx, O); 42495 if (JS_IsException(S)) 42496 goto exception; 42497 A = JS_NewArray(ctx); 42498 if (JS_IsException(A)) 42499 goto exception; 42500 lengthA = 0; 42501 if (JS_IsUndefined(limit)) { 42502 lim = 0xffffffff; 42503 } else { 42504 if (JS_ToUint32(ctx, &lim, limit) < 0) 42505 goto exception; 42506 } 42507 sp = JS_VALUE_GET_STRING(S); 42508 s = sp->len; 42509 R = JS_ToString(ctx, separator); 42510 if (JS_IsException(R)) 42511 goto exception; 42512 rp = JS_VALUE_GET_STRING(R); 42513 r = rp->len; 42514 p = 0; 42515 if (lim == 0) 42516 goto done; 42517 if (JS_IsUndefined(separator)) 42518 goto add_tail; 42519 if (s == 0) { 42520 if (r != 0) 42521 goto add_tail; 42522 goto done; 42523 } 42524 q = p; 42525 for (q = p; (q += !r) <= s - r - !r; q = p = e + r) { 42526 e = string_indexof(sp, rp, q); 42527 if (e < 0) 42528 break; 42529 T = js_sub_string(ctx, sp, p, e); 42530 if (JS_IsException(T)) 42531 goto exception; 42532 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0) 42533 goto exception; 42534 if (lengthA == lim) 42535 goto done; 42536 } 42537 add_tail: 42538 T = js_sub_string(ctx, sp, p, s); 42539 if (JS_IsException(T)) 42540 goto exception; 42541 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0) 42542 goto exception; 42543 done: 42544 JS_FreeValue(ctx, S); 42545 JS_FreeValue(ctx, R); 42546 return A; 42547 42548 exception: 42549 JS_FreeValue(ctx, A); 42550 JS_FreeValue(ctx, S); 42551 JS_FreeValue(ctx, R); 42552 return JS_EXCEPTION; 42553 } 42554 42555 static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val, 42556 int argc, JSValueConst *argv) 42557 { 42558 JSValue str, ret; 42559 int a, b, start, end; 42560 JSString *p; 42561 42562 str = JS_ToStringCheckObject(ctx, this_val); 42563 if (JS_IsException(str)) 42564 return str; 42565 p = JS_VALUE_GET_STRING(str); 42566 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) { 42567 JS_FreeValue(ctx, str); 42568 return JS_EXCEPTION; 42569 } 42570 b = p->len; 42571 if (!JS_IsUndefined(argv[1])) { 42572 if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) { 42573 JS_FreeValue(ctx, str); 42574 return JS_EXCEPTION; 42575 } 42576 } 42577 if (a < b) { 42578 start = a; 42579 end = b; 42580 } else { 42581 start = b; 42582 end = a; 42583 } 42584 ret = js_sub_string(ctx, p, start, end); 42585 JS_FreeValue(ctx, str); 42586 return ret; 42587 } 42588 42589 static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val, 42590 int argc, JSValueConst *argv) 42591 { 42592 JSValue str, ret; 42593 int a, len, n; 42594 JSString *p; 42595 42596 str = JS_ToStringCheckObject(ctx, this_val); 42597 if (JS_IsException(str)) 42598 return str; 42599 p = JS_VALUE_GET_STRING(str); 42600 len = p->len; 42601 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) { 42602 JS_FreeValue(ctx, str); 42603 return JS_EXCEPTION; 42604 } 42605 n = len - a; 42606 if (!JS_IsUndefined(argv[1])) { 42607 if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) { 42608 JS_FreeValue(ctx, str); 42609 return JS_EXCEPTION; 42610 } 42611 } 42612 ret = js_sub_string(ctx, p, a, a + n); 42613 JS_FreeValue(ctx, str); 42614 return ret; 42615 } 42616 42617 static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val, 42618 int argc, JSValueConst *argv) 42619 { 42620 JSValue str, ret; 42621 int len, start, end; 42622 JSString *p; 42623 42624 str = JS_ToStringCheckObject(ctx, this_val); 42625 if (JS_IsException(str)) 42626 return str; 42627 p = JS_VALUE_GET_STRING(str); 42628 len = p->len; 42629 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) { 42630 JS_FreeValue(ctx, str); 42631 return JS_EXCEPTION; 42632 } 42633 end = len; 42634 if (!JS_IsUndefined(argv[1])) { 42635 if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) { 42636 JS_FreeValue(ctx, str); 42637 return JS_EXCEPTION; 42638 } 42639 } 42640 ret = js_sub_string(ctx, p, start, max_int(end, start)); 42641 JS_FreeValue(ctx, str); 42642 return ret; 42643 } 42644 42645 static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, 42646 int argc, JSValueConst *argv, int padEnd) 42647 { 42648 JSValue str, v = JS_UNDEFINED; 42649 StringBuffer b_s, *b = &b_s; 42650 JSString *p, *p1 = NULL; 42651 int n, len, c = ' '; 42652 42653 str = JS_ToStringCheckObject(ctx, this_val); 42654 if (JS_IsException(str)) 42655 goto fail1; 42656 if (JS_ToInt32Sat(ctx, &n, argv[0])) 42657 goto fail2; 42658 p = JS_VALUE_GET_STRING(str); 42659 len = p->len; 42660 if (len >= n) 42661 return str; 42662 if (argc > 1 && !JS_IsUndefined(argv[1])) { 42663 v = JS_ToString(ctx, argv[1]); 42664 if (JS_IsException(v)) 42665 goto fail2; 42666 p1 = JS_VALUE_GET_STRING(v); 42667 if (p1->len == 0) { 42668 JS_FreeValue(ctx, v); 42669 return str; 42670 } 42671 if (p1->len == 1) { 42672 c = string_get(p1, 0); 42673 p1 = NULL; 42674 } 42675 } 42676 if (n > JS_STRING_LEN_MAX) { 42677 JS_ThrowRangeError(ctx, "invalid string length"); 42678 goto fail2; 42679 } 42680 if (string_buffer_init(ctx, b, n)) 42681 goto fail3; 42682 n -= len; 42683 if (padEnd) { 42684 if (string_buffer_concat(b, p, 0, len)) 42685 goto fail; 42686 } 42687 if (p1) { 42688 while (n > 0) { 42689 int chunk = min_int(n, p1->len); 42690 if (string_buffer_concat(b, p1, 0, chunk)) 42691 goto fail; 42692 n -= chunk; 42693 } 42694 } else { 42695 if (string_buffer_fill(b, c, n)) 42696 goto fail; 42697 } 42698 if (!padEnd) { 42699 if (string_buffer_concat(b, p, 0, len)) 42700 goto fail; 42701 } 42702 JS_FreeValue(ctx, v); 42703 JS_FreeValue(ctx, str); 42704 return string_buffer_end(b); 42705 42706 fail: 42707 string_buffer_free(b); 42708 fail3: 42709 JS_FreeValue(ctx, v); 42710 fail2: 42711 JS_FreeValue(ctx, str); 42712 fail1: 42713 return JS_EXCEPTION; 42714 } 42715 42716 static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, 42717 int argc, JSValueConst *argv) 42718 { 42719 JSValue str; 42720 StringBuffer b_s, *b = &b_s; 42721 JSString *p; 42722 int64_t val; 42723 int n, len; 42724 42725 str = JS_ToStringCheckObject(ctx, this_val); 42726 if (JS_IsException(str)) 42727 goto fail; 42728 if (JS_ToInt64Sat(ctx, &val, argv[0])) 42729 goto fail; 42730 if (val < 0 || val > 2147483647) { 42731 JS_ThrowRangeError(ctx, "invalid repeat count"); 42732 goto fail; 42733 } 42734 n = val; 42735 p = JS_VALUE_GET_STRING(str); 42736 len = p->len; 42737 if (len == 0 || n == 1) 42738 return str; 42739 // XXX: potential arithmetic overflow 42740 if (val * len > JS_STRING_LEN_MAX) { 42741 JS_ThrowRangeError(ctx, "invalid string length"); 42742 goto fail; 42743 } 42744 if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) 42745 goto fail; 42746 if (len == 1) { 42747 string_buffer_fill(b, string_get(p, 0), n); 42748 } else { 42749 while (n-- > 0) { 42750 string_buffer_concat(b, p, 0, len); 42751 } 42752 } 42753 JS_FreeValue(ctx, str); 42754 return string_buffer_end(b); 42755 42756 fail: 42757 JS_FreeValue(ctx, str); 42758 return JS_EXCEPTION; 42759 } 42760 42761 static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val, 42762 int argc, JSValueConst *argv, int magic) 42763 { 42764 JSValue str, ret; 42765 int a, b, len; 42766 JSString *p; 42767 42768 str = JS_ToStringCheckObject(ctx, this_val); 42769 if (JS_IsException(str)) 42770 return str; 42771 p = JS_VALUE_GET_STRING(str); 42772 a = 0; 42773 b = len = p->len; 42774 if (magic & 1) { 42775 while (a < len && lre_is_space(string_get(p, a))) 42776 a++; 42777 } 42778 if (magic & 2) { 42779 while (b > a && lre_is_space(string_get(p, b - 1))) 42780 b--; 42781 } 42782 ret = js_sub_string(ctx, p, a, b); 42783 JS_FreeValue(ctx, str); 42784 return ret; 42785 } 42786 42787 static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val, 42788 int argc, JSValueConst *argv) 42789 { 42790 return JS_ToQuotedString(ctx, this_val); 42791 } 42792 42793 /* return 0 if before the first char */ 42794 static int string_prevc(JSString *p, int *pidx) 42795 { 42796 int idx, c, c1; 42797 42798 idx = *pidx; 42799 if (idx <= 0) 42800 return 0; 42801 idx--; 42802 if (p->is_wide_char) { 42803 c = p->u.str16[idx]; 42804 if (is_lo_surrogate(c) && idx > 0) { 42805 c1 = p->u.str16[idx - 1]; 42806 if (is_hi_surrogate(c1)) { 42807 c = from_surrogate(c1, c); 42808 idx--; 42809 } 42810 } 42811 } else { 42812 c = p->u.str8[idx]; 42813 } 42814 *pidx = idx; 42815 return c; 42816 } 42817 42818 static BOOL test_final_sigma(JSString *p, int sigma_pos) 42819 { 42820 int k, c1; 42821 42822 /* before C: skip case ignorable chars and check there is 42823 a cased letter */ 42824 k = sigma_pos; 42825 for(;;) { 42826 c1 = string_prevc(p, &k); 42827 if (!lre_is_case_ignorable(c1)) 42828 break; 42829 } 42830 if (!lre_is_cased(c1)) 42831 return FALSE; 42832 42833 /* after C: skip case ignorable chars and check there is 42834 no cased letter */ 42835 k = sigma_pos + 1; 42836 for(;;) { 42837 if (k >= p->len) 42838 return TRUE; 42839 c1 = string_getc(p, &k); 42840 if (!lre_is_case_ignorable(c1)) 42841 break; 42842 } 42843 return !lre_is_cased(c1); 42844 } 42845 42846 static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, 42847 int argc, JSValueConst *argv, int to_lower) 42848 { 42849 JSValue val; 42850 StringBuffer b_s, *b = &b_s; 42851 JSString *p; 42852 int i, c, j, l; 42853 uint32_t res[LRE_CC_RES_LEN_MAX]; 42854 42855 val = JS_ToStringCheckObject(ctx, this_val); 42856 if (JS_IsException(val)) 42857 return val; 42858 p = JS_VALUE_GET_STRING(val); 42859 if (p->len == 0) 42860 return val; 42861 if (string_buffer_init(ctx, b, p->len)) 42862 goto fail; 42863 for(i = 0; i < p->len;) { 42864 c = string_getc(p, &i); 42865 if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) { 42866 res[0] = 0x3c2; /* final sigma */ 42867 l = 1; 42868 } else { 42869 l = lre_case_conv(res, c, to_lower); 42870 } 42871 for(j = 0; j < l; j++) { 42872 if (string_buffer_putc(b, res[j])) 42873 goto fail; 42874 } 42875 } 42876 JS_FreeValue(ctx, val); 42877 return string_buffer_end(b); 42878 fail: 42879 JS_FreeValue(ctx, val); 42880 string_buffer_free(b); 42881 return JS_EXCEPTION; 42882 } 42883 42884 #ifdef CONFIG_ALL_UNICODE 42885 42886 /* return (-1, NULL) if exception, otherwise (len, buf) */ 42887 static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1) 42888 { 42889 JSValue val; 42890 JSString *p; 42891 uint32_t *buf; 42892 int i, j, len; 42893 42894 val = JS_ToString(ctx, val1); 42895 if (JS_IsException(val)) 42896 return -1; 42897 p = JS_VALUE_GET_STRING(val); 42898 len = p->len; 42899 /* UTF32 buffer length is len minus the number of correct surrogates pairs */ 42900 buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1)); 42901 if (!buf) { 42902 JS_FreeValue(ctx, val); 42903 goto fail; 42904 } 42905 for(i = j = 0; i < len;) 42906 buf[j++] = string_getc(p, &i); 42907 JS_FreeValue(ctx, val); 42908 *pbuf = buf; 42909 return j; 42910 fail: 42911 *pbuf = NULL; 42912 return -1; 42913 } 42914 42915 static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) 42916 { 42917 int i; 42918 StringBuffer b_s, *b = &b_s; 42919 if (string_buffer_init(ctx, b, len)) 42920 return JS_EXCEPTION; 42921 for(i = 0; i < len; i++) { 42922 if (string_buffer_putc(b, buf[i])) 42923 goto fail; 42924 } 42925 return string_buffer_end(b); 42926 fail: 42927 string_buffer_free(b); 42928 return JS_EXCEPTION; 42929 } 42930 42931 static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf, 42932 JSValueConst val, 42933 UnicodeNormalizationEnum n_type) 42934 { 42935 int buf_len, out_len; 42936 uint32_t *buf, *out_buf; 42937 42938 buf_len = JS_ToUTF32String(ctx, &buf, val); 42939 if (buf_len < 0) 42940 return -1; 42941 out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, 42942 ctx->rt, (DynBufReallocFunc *)js_realloc_rt); 42943 js_free(ctx, buf); 42944 if (out_len < 0) 42945 return -1; 42946 *pout_buf = out_buf; 42947 return out_len; 42948 } 42949 42950 static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, 42951 int argc, JSValueConst *argv) 42952 { 42953 const char *form, *p; 42954 size_t form_len; 42955 int is_compat, out_len; 42956 UnicodeNormalizationEnum n_type; 42957 JSValue val; 42958 uint32_t *out_buf; 42959 42960 val = JS_ToStringCheckObject(ctx, this_val); 42961 if (JS_IsException(val)) 42962 return val; 42963 42964 if (argc == 0 || JS_IsUndefined(argv[0])) { 42965 n_type = UNICODE_NFC; 42966 } else { 42967 form = JS_ToCStringLen(ctx, &form_len, argv[0]); 42968 if (!form) 42969 goto fail1; 42970 p = form; 42971 if (p[0] != 'N' || p[1] != 'F') 42972 goto bad_form; 42973 p += 2; 42974 is_compat = FALSE; 42975 if (*p == 'K') { 42976 is_compat = TRUE; 42977 p++; 42978 } 42979 if (*p == 'C' || *p == 'D') { 42980 n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C'); 42981 if ((p + 1 - form) != form_len) 42982 goto bad_form; 42983 } else { 42984 bad_form: 42985 JS_FreeCString(ctx, form); 42986 JS_ThrowRangeError(ctx, "bad normalization form"); 42987 fail1: 42988 JS_FreeValue(ctx, val); 42989 return JS_EXCEPTION; 42990 } 42991 JS_FreeCString(ctx, form); 42992 } 42993 42994 out_len = js_string_normalize1(ctx, &out_buf, val, n_type); 42995 JS_FreeValue(ctx, val); 42996 if (out_len < 0) 42997 return JS_EXCEPTION; 42998 val = JS_NewUTF32String(ctx, out_buf, out_len); 42999 js_free(ctx, out_buf); 43000 return val; 43001 } 43002 43003 /* return < 0, 0 or > 0 */ 43004 static int js_UTF32_compare(const uint32_t *buf1, int buf1_len, 43005 const uint32_t *buf2, int buf2_len) 43006 { 43007 int i, len, c, res; 43008 len = min_int(buf1_len, buf2_len); 43009 for(i = 0; i < len; i++) { 43010 /* Note: range is limited so a subtraction is valid */ 43011 c = buf1[i] - buf2[i]; 43012 if (c != 0) 43013 return c; 43014 } 43015 if (buf1_len == buf2_len) 43016 res = 0; 43017 else if (buf1_len < buf2_len) 43018 res = -1; 43019 else 43020 res = 1; 43021 return res; 43022 } 43023 43024 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, 43025 int argc, JSValueConst *argv) 43026 { 43027 JSValue a, b; 43028 int cmp, a_len, b_len; 43029 uint32_t *a_buf, *b_buf; 43030 43031 a = JS_ToStringCheckObject(ctx, this_val); 43032 if (JS_IsException(a)) 43033 return JS_EXCEPTION; 43034 b = JS_ToString(ctx, argv[0]); 43035 if (JS_IsException(b)) { 43036 JS_FreeValue(ctx, a); 43037 return JS_EXCEPTION; 43038 } 43039 a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC); 43040 JS_FreeValue(ctx, a); 43041 if (a_len < 0) { 43042 JS_FreeValue(ctx, b); 43043 return JS_EXCEPTION; 43044 } 43045 43046 b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC); 43047 JS_FreeValue(ctx, b); 43048 if (b_len < 0) { 43049 js_free(ctx, a_buf); 43050 return JS_EXCEPTION; 43051 } 43052 cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len); 43053 js_free(ctx, a_buf); 43054 js_free(ctx, b_buf); 43055 return JS_NewInt32(ctx, cmp); 43056 } 43057 #else /* CONFIG_ALL_UNICODE */ 43058 static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, 43059 int argc, JSValueConst *argv) 43060 { 43061 JSValue a, b; 43062 int cmp; 43063 43064 a = JS_ToStringCheckObject(ctx, this_val); 43065 if (JS_IsException(a)) 43066 return JS_EXCEPTION; 43067 b = JS_ToString(ctx, argv[0]); 43068 if (JS_IsException(b)) { 43069 JS_FreeValue(ctx, a); 43070 return JS_EXCEPTION; 43071 } 43072 cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); 43073 JS_FreeValue(ctx, a); 43074 JS_FreeValue(ctx, b); 43075 return JS_NewInt32(ctx, cmp); 43076 } 43077 #endif /* !CONFIG_ALL_UNICODE */ 43078 43079 /* also used for String.prototype.valueOf */ 43080 static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, 43081 int argc, JSValueConst *argv) 43082 { 43083 return js_thisStringValue(ctx, this_val); 43084 } 43085 43086 #if 0 43087 static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val, 43088 int argc, JSValueConst *argv) 43089 { 43090 return JS_ToStringCheckObject(ctx, argv[0]); 43091 } 43092 43093 static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val, 43094 int argc, JSValueConst *argv) 43095 { 43096 return JS_ToString(ctx, argv[0]); 43097 } 43098 43099 static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst 43100 this_val, 43101 int argc, JSValueConst *argv) 43102 { 43103 JSValue str; 43104 int idx; 43105 BOOL is_unicode; 43106 JSString *p; 43107 43108 str = JS_ToString(ctx, argv[0]); 43109 if (JS_IsException(str)) 43110 return str; 43111 if (JS_ToInt32Sat(ctx, &idx, argv[1])) { 43112 JS_FreeValue(ctx, str); 43113 return JS_EXCEPTION; 43114 } 43115 is_unicode = JS_ToBool(ctx, argv[2]); 43116 p = JS_VALUE_GET_STRING(str); 43117 if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) { 43118 idx++; 43119 } else { 43120 string_getc(p, &idx); 43121 } 43122 JS_FreeValue(ctx, str); 43123 return JS_NewInt32(ctx, idx); 43124 } 43125 #endif 43126 43127 /* String Iterator */ 43128 43129 static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val, 43130 int argc, JSValueConst *argv, 43131 BOOL *pdone, int magic) 43132 { 43133 JSArrayIteratorData *it; 43134 uint32_t idx, c, start; 43135 JSString *p; 43136 43137 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR); 43138 if (!it) { 43139 *pdone = FALSE; 43140 return JS_EXCEPTION; 43141 } 43142 if (JS_IsUndefined(it->obj)) 43143 goto done; 43144 p = JS_VALUE_GET_STRING(it->obj); 43145 idx = it->idx; 43146 if (idx >= p->len) { 43147 JS_FreeValue(ctx, it->obj); 43148 it->obj = JS_UNDEFINED; 43149 done: 43150 *pdone = TRUE; 43151 return JS_UNDEFINED; 43152 } 43153 43154 start = idx; 43155 c = string_getc(p, (int *)&idx); 43156 it->idx = idx; 43157 *pdone = FALSE; 43158 if (c <= 0xffff) { 43159 return js_new_string_char(ctx, c); 43160 } else { 43161 return js_new_string16(ctx, p->u.str16 + start, 2); 43162 } 43163 } 43164 43165 /* ES6 Annex B 2.3.2 etc. */ 43166 enum { 43167 magic_string_anchor, 43168 magic_string_big, 43169 magic_string_blink, 43170 magic_string_bold, 43171 magic_string_fixed, 43172 magic_string_fontcolor, 43173 magic_string_fontsize, 43174 magic_string_italics, 43175 magic_string_link, 43176 magic_string_small, 43177 magic_string_strike, 43178 magic_string_sub, 43179 magic_string_sup, 43180 }; 43181 43182 static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val, 43183 int argc, JSValueConst *argv, int magic) 43184 { 43185 JSValue str; 43186 const JSString *p; 43187 StringBuffer b_s, *b = &b_s; 43188 static struct { const char *tag, *attr; } const defs[] = { 43189 { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL }, 43190 { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL }, 43191 { "a", "href" }, { "small", NULL }, { "strike", NULL }, 43192 { "sub", NULL }, { "sup", NULL }, 43193 }; 43194 43195 str = JS_ToStringCheckObject(ctx, this_val); 43196 if (JS_IsException(str)) 43197 return JS_EXCEPTION; 43198 string_buffer_init(ctx, b, 7); 43199 string_buffer_putc8(b, '<'); 43200 string_buffer_puts8(b, defs[magic].tag); 43201 if (defs[magic].attr) { 43202 // r += " " + attr + "=\"" + value + "\""; 43203 JSValue value; 43204 int i; 43205 43206 string_buffer_putc8(b, ' '); 43207 string_buffer_puts8(b, defs[magic].attr); 43208 string_buffer_puts8(b, "=\""); 43209 value = JS_ToStringCheckObject(ctx, argv[0]); 43210 if (JS_IsException(value)) { 43211 JS_FreeValue(ctx, str); 43212 string_buffer_free(b); 43213 return JS_EXCEPTION; 43214 } 43215 p = JS_VALUE_GET_STRING(value); 43216 for (i = 0; i < p->len; i++) { 43217 int c = string_get(p, i); 43218 if (c == '"') { 43219 string_buffer_puts8(b, """); 43220 } else { 43221 string_buffer_putc16(b, c); 43222 } 43223 } 43224 JS_FreeValue(ctx, value); 43225 string_buffer_putc8(b, '\"'); 43226 } 43227 // return r + ">" + str + "</" + tag + ">"; 43228 string_buffer_putc8(b, '>'); 43229 string_buffer_concat_value_free(b, str); 43230 string_buffer_puts8(b, "</"); 43231 string_buffer_puts8(b, defs[magic].tag); 43232 string_buffer_putc8(b, '>'); 43233 return string_buffer_end(b); 43234 } 43235 43236 static const JSCFunctionListEntry js_string_funcs[] = { 43237 JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ), 43238 JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ), 43239 JS_CFUNC_DEF("raw", 1, js_string_raw ), 43240 //JS_CFUNC_DEF("__toString", 1, js_string___toString ), 43241 //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ), 43242 //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ), 43243 //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ), 43244 //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ), 43245 }; 43246 43247 static const JSCFunctionListEntry js_string_proto_funcs[] = { 43248 JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), 43249 JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ), 43250 JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), 43251 JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ), 43252 JS_CFUNC_DEF("concat", 1, js_string_concat ), 43253 JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), 43254 JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ), 43255 JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ), 43256 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), 43257 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), 43258 JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), 43259 JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ), 43260 JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ), 43261 JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ), 43262 JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ), 43263 JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ), 43264 JS_CFUNC_DEF("split", 2, js_string_split ), 43265 JS_CFUNC_DEF("substring", 2, js_string_substring ), 43266 JS_CFUNC_DEF("substr", 2, js_string_substr ), 43267 JS_CFUNC_DEF("slice", 2, js_string_slice ), 43268 JS_CFUNC_DEF("repeat", 1, js_string_repeat ), 43269 JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ), 43270 JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ), 43271 JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ), 43272 JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ), 43273 JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ), 43274 JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ), 43275 JS_ALIAS_DEF("trimRight", "trimEnd" ), 43276 JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ), 43277 JS_ALIAS_DEF("trimLeft", "trimStart" ), 43278 JS_CFUNC_DEF("toString", 0, js_string_toString ), 43279 JS_CFUNC_DEF("valueOf", 0, js_string_toString ), 43280 JS_CFUNC_DEF("__quote", 1, js_string___quote ), 43281 JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ), 43282 JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ), 43283 JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ), 43284 JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ), 43285 JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ), 43286 JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ), 43287 /* ES6 Annex B 2.3.2 etc. */ 43288 JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ), 43289 JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ), 43290 JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ), 43291 JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ), 43292 JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ), 43293 JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ), 43294 JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ), 43295 JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ), 43296 JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ), 43297 JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ), 43298 JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ), 43299 JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ), 43300 JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ), 43301 }; 43302 43303 static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = { 43304 JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ), 43305 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ), 43306 }; 43307 43308 #ifdef CONFIG_ALL_UNICODE 43309 static const JSCFunctionListEntry js_string_proto_normalize[] = { 43310 JS_CFUNC_DEF("normalize", 0, js_string_normalize ), 43311 }; 43312 #endif 43313 43314 void JS_AddIntrinsicStringNormalize(JSContext *ctx) 43315 { 43316 #ifdef CONFIG_ALL_UNICODE 43317 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize, 43318 countof(js_string_proto_normalize)); 43319 #endif 43320 } 43321 43322 /* Math */ 43323 43324 /* precondition: a and b are not NaN */ 43325 static double js_fmin(double a, double b) 43326 { 43327 if (a == 0 && b == 0) { 43328 JSFloat64Union a1, b1; 43329 a1.d = a; 43330 b1.d = b; 43331 a1.u64 |= b1.u64; 43332 return a1.d; 43333 } else { 43334 return fmin(a, b); 43335 } 43336 } 43337 43338 /* precondition: a and b are not NaN */ 43339 static double js_fmax(double a, double b) 43340 { 43341 if (a == 0 && b == 0) { 43342 JSFloat64Union a1, b1; 43343 a1.d = a; 43344 b1.d = b; 43345 a1.u64 &= b1.u64; 43346 return a1.d; 43347 } else { 43348 return fmax(a, b); 43349 } 43350 } 43351 43352 static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, 43353 int argc, JSValueConst *argv, int magic) 43354 { 43355 BOOL is_max = magic; 43356 double r, a; 43357 int i; 43358 uint32_t tag; 43359 43360 if (unlikely(argc == 0)) { 43361 return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); 43362 } 43363 43364 tag = JS_VALUE_GET_TAG(argv[0]); 43365 if (tag == JS_TAG_INT) { 43366 int a1, r1 = JS_VALUE_GET_INT(argv[0]); 43367 for(i = 1; i < argc; i++) { 43368 tag = JS_VALUE_GET_TAG(argv[i]); 43369 if (tag != JS_TAG_INT) { 43370 r = r1; 43371 goto generic_case; 43372 } 43373 a1 = JS_VALUE_GET_INT(argv[i]); 43374 if (is_max) 43375 r1 = max_int(r1, a1); 43376 else 43377 r1 = min_int(r1, a1); 43378 43379 } 43380 return JS_NewInt32(ctx, r1); 43381 } else { 43382 if (JS_ToFloat64(ctx, &r, argv[0])) 43383 return JS_EXCEPTION; 43384 i = 1; 43385 generic_case: 43386 while (i < argc) { 43387 if (JS_ToFloat64(ctx, &a, argv[i])) 43388 return JS_EXCEPTION; 43389 if (!isnan(r)) { 43390 if (isnan(a)) { 43391 r = a; 43392 } else { 43393 if (is_max) 43394 r = js_fmax(r, a); 43395 else 43396 r = js_fmin(r, a); 43397 } 43398 } 43399 i++; 43400 } 43401 return JS_NewFloat64(ctx, r); 43402 } 43403 } 43404 43405 static double js_math_sign(double a) 43406 { 43407 if (isnan(a) || a == 0.0) 43408 return a; 43409 if (a < 0) 43410 return -1; 43411 else 43412 return 1; 43413 } 43414 43415 static double js_math_round(double a) 43416 { 43417 JSFloat64Union u; 43418 uint64_t frac_mask, one; 43419 unsigned int e, s; 43420 43421 u.d = a; 43422 e = (u.u64 >> 52) & 0x7ff; 43423 if (e < 1023) { 43424 /* abs(a) < 1 */ 43425 if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) { 43426 /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */ 43427 u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52); 43428 } else { 43429 /* return +/-0.0 */ 43430 u.u64 &= (uint64_t)1 << 63; 43431 } 43432 } else if (e < (1023 + 52)) { 43433 s = u.u64 >> 63; 43434 one = (uint64_t)1 << (52 - (e - 1023)); 43435 frac_mask = one - 1; 43436 u.u64 += (one >> 1) - s; 43437 u.u64 &= ~frac_mask; /* truncate to an integer */ 43438 } 43439 /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */ 43440 return u.d; 43441 } 43442 43443 static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val, 43444 int argc, JSValueConst *argv) 43445 { 43446 double r, a; 43447 int i; 43448 43449 r = 0; 43450 if (argc > 0) { 43451 if (JS_ToFloat64(ctx, &r, argv[0])) 43452 return JS_EXCEPTION; 43453 if (argc == 1) { 43454 r = fabs(r); 43455 } else { 43456 /* use the built-in function to minimize precision loss */ 43457 for (i = 1; i < argc; i++) { 43458 if (JS_ToFloat64(ctx, &a, argv[i])) 43459 return JS_EXCEPTION; 43460 r = hypot(r, a); 43461 } 43462 } 43463 } 43464 return JS_NewFloat64(ctx, r); 43465 } 43466 43467 static double js_math_fround(double a) 43468 { 43469 return (float)a; 43470 } 43471 43472 static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, 43473 int argc, JSValueConst *argv) 43474 { 43475 uint32_t a, b, c; 43476 int32_t d; 43477 43478 if (JS_ToUint32(ctx, &a, argv[0])) 43479 return JS_EXCEPTION; 43480 if (JS_ToUint32(ctx, &b, argv[1])) 43481 return JS_EXCEPTION; 43482 c = a * b; 43483 memcpy(&d, &c, sizeof(d)); 43484 return JS_NewInt32(ctx, d); 43485 } 43486 43487 static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, 43488 int argc, JSValueConst *argv) 43489 { 43490 uint32_t a, r; 43491 43492 if (JS_ToUint32(ctx, &a, argv[0])) 43493 return JS_EXCEPTION; 43494 if (a == 0) 43495 r = 32; 43496 else 43497 r = clz32(a); 43498 return JS_NewInt32(ctx, r); 43499 } 43500 43501 /* xorshift* random number generator by Marsaglia */ 43502 static uint64_t xorshift64star(uint64_t *pstate) 43503 { 43504 uint64_t x; 43505 x = *pstate; 43506 x ^= x >> 12; 43507 x ^= x << 25; 43508 x ^= x >> 27; 43509 *pstate = x; 43510 return x * 0x2545F4914F6CDD1D; 43511 } 43512 43513 static void js_random_init(JSContext *ctx) 43514 { 43515 struct timeval tv; 43516 gettimeofday(&tv, NULL); 43517 ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; 43518 /* the state must be non zero */ 43519 if (ctx->random_state == 0) 43520 ctx->random_state = 1; 43521 } 43522 43523 static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, 43524 int argc, JSValueConst *argv) 43525 { 43526 JSFloat64Union u; 43527 uint64_t v; 43528 43529 v = xorshift64star(&ctx->random_state); 43530 /* 1.0 <= u.d < 2 */ 43531 u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); 43532 return __JS_NewFloat64(ctx, u.d - 1.0); 43533 } 43534 43535 static const JSCFunctionListEntry js_math_funcs[] = { 43536 JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), 43537 JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), 43538 JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), 43539 JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), 43540 JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), 43541 JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), 43542 JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), 43543 43544 JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ), 43545 JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ), 43546 JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ), 43547 JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ), 43548 JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ), 43549 JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ), 43550 JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ), 43551 JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ), 43552 JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ), 43553 JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ), 43554 /* ES6 */ 43555 JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ), 43556 JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ), 43557 JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ), 43558 JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ), 43559 JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ), 43560 JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ), 43561 JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ), 43562 JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), 43563 JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), 43564 JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), 43565 JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), 43566 JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), 43567 JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), 43568 JS_CFUNC_DEF("hypot", 2, js_math_hypot ), 43569 JS_CFUNC_DEF("random", 0, js_math_random ), 43570 JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ), 43571 JS_CFUNC_DEF("imul", 2, js_math_imul ), 43572 JS_CFUNC_DEF("clz32", 1, js_math_clz32 ), 43573 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ), 43574 JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ), 43575 JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ), 43576 JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ), 43577 JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ), 43578 JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ), 43579 JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ), 43580 JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ), 43581 JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ), 43582 }; 43583 43584 static const JSCFunctionListEntry js_math_obj[] = { 43585 JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 43586 }; 43587 43588 /* Date */ 43589 43590 /* OS dependent. d = argv[0] is in ms from 1970. Return the difference 43591 between UTC time and local time 'd' in minutes */ 43592 static int getTimezoneOffset(int64_t time) 43593 { 43594 time_t ti; 43595 int res; 43596 43597 time /= 1000; /* convert to seconds */ 43598 if (sizeof(time_t) == 4) { 43599 /* on 32-bit systems, we need to clamp the time value to the 43600 range of `time_t`. This is better than truncating values to 43601 32 bits and hopefully provides the same result as 64-bit 43602 implementation of localtime_r. 43603 */ 43604 if ((time_t)-1 < 0) { 43605 if (time < INT32_MIN) { 43606 time = INT32_MIN; 43607 } else if (time > INT32_MAX) { 43608 time = INT32_MAX; 43609 } 43610 } else { 43611 if (time < 0) { 43612 time = 0; 43613 } else if (time > UINT32_MAX) { 43614 time = UINT32_MAX; 43615 } 43616 } 43617 } 43618 ti = time; 43619 #if defined(_WIN32) 43620 { 43621 struct tm *tm; 43622 time_t gm_ti, loc_ti; 43623 43624 tm = gmtime(&ti); 43625 gm_ti = mktime(tm); 43626 43627 tm = localtime(&ti); 43628 loc_ti = mktime(tm); 43629 43630 res = (gm_ti - loc_ti) / 60; 43631 } 43632 #else 43633 { 43634 struct tm tm; 43635 localtime_r(&ti, &tm); 43636 res = -tm.tm_gmtoff / 60; 43637 } 43638 #endif 43639 return res; 43640 } 43641 43642 #if 0 43643 static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, 43644 int argc, JSValueConst *argv) 43645 { 43646 double dd; 43647 43648 if (JS_ToFloat64(ctx, &dd, argv[0])) 43649 return JS_EXCEPTION; 43650 if (isnan(dd)) 43651 return __JS_NewFloat64(ctx, dd); 43652 else 43653 return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); 43654 } 43655 43656 static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor, 43657 JSValueConst def_proto) 43658 { 43659 JSValue proto; 43660 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); 43661 if (JS_IsException(proto)) 43662 return proto; 43663 if (!JS_IsObject(proto)) { 43664 JS_FreeValue(ctx, proto); 43665 proto = JS_DupValue(ctx, def_proto); 43666 } 43667 return proto; 43668 } 43669 43670 /* create a new date object */ 43671 static JSValue js___date_create(JSContext *ctx, JSValueConst this_val, 43672 int argc, JSValueConst *argv) 43673 { 43674 JSValue obj, proto; 43675 proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]); 43676 if (JS_IsException(proto)) 43677 return proto; 43678 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE); 43679 JS_FreeValue(ctx, proto); 43680 if (!JS_IsException(obj)) 43681 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2])); 43682 return obj; 43683 } 43684 #endif 43685 43686 /* RegExp */ 43687 43688 static void js_regexp_finalizer(JSRuntime *rt, JSValue val) 43689 { 43690 JSObject *p = JS_VALUE_GET_OBJ(val); 43691 JSRegExp *re = &p->u.regexp; 43692 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 43693 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern)); 43694 } 43695 43696 /* create a string containing the RegExp bytecode */ 43697 static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, 43698 JSValueConst flags) 43699 { 43700 const char *str; 43701 int re_flags, mask; 43702 uint8_t *re_bytecode_buf; 43703 size_t i, len; 43704 int re_bytecode_len; 43705 JSValue ret; 43706 char error_msg[64]; 43707 43708 re_flags = 0; 43709 if (!JS_IsUndefined(flags)) { 43710 str = JS_ToCStringLen(ctx, &len, flags); 43711 if (!str) 43712 return JS_EXCEPTION; 43713 /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ 43714 for (i = 0; i < len; i++) { 43715 switch(str[i]) { 43716 case 'd': 43717 mask = LRE_FLAG_INDICES; 43718 break; 43719 case 'g': 43720 mask = LRE_FLAG_GLOBAL; 43721 break; 43722 case 'i': 43723 mask = LRE_FLAG_IGNORECASE; 43724 break; 43725 case 'm': 43726 mask = LRE_FLAG_MULTILINE; 43727 break; 43728 case 's': 43729 mask = LRE_FLAG_DOTALL; 43730 break; 43731 case 'u': 43732 mask = LRE_FLAG_UNICODE; 43733 break; 43734 case 'y': 43735 mask = LRE_FLAG_STICKY; 43736 break; 43737 default: 43738 goto bad_flags; 43739 } 43740 if ((re_flags & mask) != 0) { 43741 bad_flags: 43742 JS_FreeCString(ctx, str); 43743 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags"); 43744 } 43745 re_flags |= mask; 43746 } 43747 JS_FreeCString(ctx, str); 43748 } 43749 43750 str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE)); 43751 if (!str) 43752 return JS_EXCEPTION; 43753 re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, 43754 sizeof(error_msg), str, len, re_flags, ctx); 43755 JS_FreeCString(ctx, str); 43756 if (!re_bytecode_buf) { 43757 JS_ThrowSyntaxError(ctx, "%s", error_msg); 43758 return JS_EXCEPTION; 43759 } 43760 43761 ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len); 43762 js_free(ctx, re_bytecode_buf); 43763 return ret; 43764 } 43765 43766 /* create a RegExp object from a string containing the RegExp bytecode 43767 and the source pattern */ 43768 static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor, 43769 JSValue pattern, JSValue bc) 43770 { 43771 JSValue obj; 43772 JSObject *p; 43773 JSRegExp *re; 43774 43775 /* sanity check */ 43776 if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING || 43777 JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) { 43778 JS_ThrowTypeError(ctx, "string expected"); 43779 fail: 43780 JS_FreeValue(ctx, bc); 43781 JS_FreeValue(ctx, pattern); 43782 return JS_EXCEPTION; 43783 } 43784 43785 obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP); 43786 if (JS_IsException(obj)) 43787 goto fail; 43788 p = JS_VALUE_GET_OBJ(obj); 43789 re = &p->u.regexp; 43790 re->pattern = JS_VALUE_GET_STRING(pattern); 43791 re->bytecode = JS_VALUE_GET_STRING(bc); 43792 JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0), 43793 JS_PROP_WRITABLE); 43794 return obj; 43795 } 43796 43797 static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error) 43798 { 43799 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { 43800 JSObject *p = JS_VALUE_GET_OBJ(obj); 43801 if (p->class_id == JS_CLASS_REGEXP) 43802 return &p->u.regexp; 43803 } 43804 if (throw_error) { 43805 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); 43806 } 43807 return NULL; 43808 } 43809 43810 /* return < 0 if exception or TRUE/FALSE */ 43811 static int js_is_regexp(JSContext *ctx, JSValueConst obj) 43812 { 43813 JSValue m; 43814 43815 if (!JS_IsObject(obj)) 43816 return FALSE; 43817 m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match); 43818 if (JS_IsException(m)) 43819 return -1; 43820 if (!JS_IsUndefined(m)) 43821 return JS_ToBoolFree(ctx, m); 43822 return js_get_regexp(ctx, obj, FALSE) != NULL; 43823 } 43824 43825 static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target, 43826 int argc, JSValueConst *argv) 43827 { 43828 JSValue pattern, flags, bc, val; 43829 JSValueConst pat, flags1; 43830 JSRegExp *re; 43831 int pat_is_regexp; 43832 43833 pat = argv[0]; 43834 flags1 = argv[1]; 43835 pat_is_regexp = js_is_regexp(ctx, pat); 43836 if (pat_is_regexp < 0) 43837 return JS_EXCEPTION; 43838 if (JS_IsUndefined(new_target)) { 43839 /* called as a function */ 43840 new_target = JS_GetActiveFunction(ctx); 43841 if (pat_is_regexp && JS_IsUndefined(flags1)) { 43842 JSValue ctor; 43843 BOOL res; 43844 ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor); 43845 if (JS_IsException(ctor)) 43846 return ctor; 43847 res = js_same_value(ctx, ctor, new_target); 43848 JS_FreeValue(ctx, ctor); 43849 if (res) 43850 return JS_DupValue(ctx, pat); 43851 } 43852 } 43853 re = js_get_regexp(ctx, pat, FALSE); 43854 if (re) { 43855 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); 43856 if (JS_IsUndefined(flags1)) { 43857 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 43858 goto no_compilation; 43859 } else { 43860 flags = JS_ToString(ctx, flags1); 43861 if (JS_IsException(flags)) 43862 goto fail; 43863 } 43864 } else { 43865 flags = JS_UNDEFINED; 43866 if (pat_is_regexp) { 43867 pattern = JS_GetProperty(ctx, pat, JS_ATOM_source); 43868 if (JS_IsException(pattern)) 43869 goto fail; 43870 if (JS_IsUndefined(flags1)) { 43871 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags); 43872 if (JS_IsException(flags)) 43873 goto fail; 43874 } else { 43875 flags = JS_DupValue(ctx, flags1); 43876 } 43877 } else { 43878 pattern = JS_DupValue(ctx, pat); 43879 flags = JS_DupValue(ctx, flags1); 43880 } 43881 if (JS_IsUndefined(pattern)) { 43882 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); 43883 } else { 43884 val = pattern; 43885 pattern = JS_ToString(ctx, val); 43886 JS_FreeValue(ctx, val); 43887 if (JS_IsException(pattern)) 43888 goto fail; 43889 } 43890 } 43891 bc = js_compile_regexp(ctx, pattern, flags); 43892 if (JS_IsException(bc)) 43893 goto fail; 43894 JS_FreeValue(ctx, flags); 43895 no_compilation: 43896 return js_regexp_constructor_internal(ctx, new_target, pattern, bc); 43897 fail: 43898 JS_FreeValue(ctx, pattern); 43899 JS_FreeValue(ctx, flags); 43900 return JS_EXCEPTION; 43901 } 43902 43903 static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val, 43904 int argc, JSValueConst *argv) 43905 { 43906 JSRegExp *re1, *re; 43907 JSValueConst pattern1, flags1; 43908 JSValue bc, pattern; 43909 43910 re = js_get_regexp(ctx, this_val, TRUE); 43911 if (!re) 43912 return JS_EXCEPTION; 43913 pattern1 = argv[0]; 43914 flags1 = argv[1]; 43915 re1 = js_get_regexp(ctx, pattern1, FALSE); 43916 if (re1) { 43917 if (!JS_IsUndefined(flags1)) 43918 return JS_ThrowTypeError(ctx, "flags must be undefined"); 43919 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern)); 43920 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode)); 43921 } else { 43922 bc = JS_UNDEFINED; 43923 if (JS_IsUndefined(pattern1)) 43924 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string); 43925 else 43926 pattern = JS_ToString(ctx, pattern1); 43927 if (JS_IsException(pattern)) 43928 goto fail; 43929 bc = js_compile_regexp(ctx, pattern, flags1); 43930 if (JS_IsException(bc)) 43931 goto fail; 43932 } 43933 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); 43934 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode)); 43935 re->pattern = JS_VALUE_GET_STRING(pattern); 43936 re->bytecode = JS_VALUE_GET_STRING(bc); 43937 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 43938 JS_NewInt32(ctx, 0)) < 0) 43939 return JS_EXCEPTION; 43940 return JS_DupValue(ctx, this_val); 43941 fail: 43942 JS_FreeValue(ctx, pattern); 43943 JS_FreeValue(ctx, bc); 43944 return JS_EXCEPTION; 43945 } 43946 43947 #if 0 43948 static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val) 43949 { 43950 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 43951 if (!re) 43952 return JS_EXCEPTION; 43953 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern)); 43954 } 43955 43956 static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val) 43957 { 43958 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 43959 int flags; 43960 43961 if (!re) 43962 return JS_EXCEPTION; 43963 flags = lre_get_flags(re->bytecode->u.str8); 43964 return JS_NewInt32(ctx, flags); 43965 } 43966 #endif 43967 43968 static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val) 43969 { 43970 JSRegExp *re; 43971 JSString *p; 43972 StringBuffer b_s, *b = &b_s; 43973 int i, n, c, c2, bra; 43974 43975 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 43976 return JS_ThrowTypeErrorNotAnObject(ctx); 43977 43978 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) 43979 goto empty_regex; 43980 43981 re = js_get_regexp(ctx, this_val, TRUE); 43982 if (!re) 43983 return JS_EXCEPTION; 43984 43985 p = re->pattern; 43986 43987 if (p->len == 0) { 43988 empty_regex: 43989 return JS_NewString(ctx, "(?:)"); 43990 } 43991 string_buffer_init2(ctx, b, p->len, p->is_wide_char); 43992 43993 /* Escape '/' and newline sequences as needed */ 43994 bra = 0; 43995 for (i = 0, n = p->len; i < n;) { 43996 c2 = -1; 43997 switch (c = string_get(p, i++)) { 43998 case '\\': 43999 if (i < n) 44000 c2 = string_get(p, i++); 44001 break; 44002 case ']': 44003 bra = 0; 44004 break; 44005 case '[': 44006 if (!bra) { 44007 if (i < n && string_get(p, i) == ']') 44008 c2 = string_get(p, i++); 44009 bra = 1; 44010 } 44011 break; 44012 case '\n': 44013 c = '\\'; 44014 c2 = 'n'; 44015 break; 44016 case '\r': 44017 c = '\\'; 44018 c2 = 'r'; 44019 break; 44020 case '/': 44021 if (!bra) { 44022 c = '\\'; 44023 c2 = '/'; 44024 } 44025 break; 44026 } 44027 string_buffer_putc16(b, c); 44028 if (c2 >= 0) 44029 string_buffer_putc16(b, c2); 44030 } 44031 return string_buffer_end(b); 44032 } 44033 44034 static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask) 44035 { 44036 JSRegExp *re; 44037 int flags; 44038 44039 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 44040 return JS_ThrowTypeErrorNotAnObject(ctx); 44041 44042 re = js_get_regexp(ctx, this_val, FALSE); 44043 if (!re) { 44044 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP])) 44045 return JS_UNDEFINED; 44046 else 44047 return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP); 44048 } 44049 44050 flags = lre_get_flags(re->bytecode->u.str8); 44051 return JS_NewBool(ctx, flags & mask); 44052 } 44053 44054 static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) 44055 { 44056 char str[8], *p = str; 44057 int res; 44058 44059 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 44060 return JS_ThrowTypeErrorNotAnObject(ctx); 44061 44062 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices")); 44063 if (res < 0) 44064 goto exception; 44065 if (res) 44066 *p++ = 'd'; 44067 res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); 44068 if (res < 0) 44069 goto exception; 44070 if (res) 44071 *p++ = 'g'; 44072 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase")); 44073 if (res < 0) 44074 goto exception; 44075 if (res) 44076 *p++ = 'i'; 44077 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline")); 44078 if (res < 0) 44079 goto exception; 44080 if (res) 44081 *p++ = 'm'; 44082 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll")); 44083 if (res < 0) 44084 goto exception; 44085 if (res) 44086 *p++ = 's'; 44087 res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode)); 44088 if (res < 0) 44089 goto exception; 44090 if (res) 44091 *p++ = 'u'; 44092 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky")); 44093 if (res < 0) 44094 goto exception; 44095 if (res) 44096 *p++ = 'y'; 44097 return JS_NewStringLen(ctx, str, p - str); 44098 44099 exception: 44100 return JS_EXCEPTION; 44101 } 44102 44103 static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val, 44104 int argc, JSValueConst *argv) 44105 { 44106 JSValue pattern, flags; 44107 StringBuffer b_s, *b = &b_s; 44108 44109 if (!JS_IsObject(this_val)) 44110 return JS_ThrowTypeErrorNotAnObject(ctx); 44111 44112 string_buffer_init(ctx, b, 0); 44113 string_buffer_putc8(b, '/'); 44114 pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source); 44115 if (string_buffer_concat_value_free(b, pattern)) 44116 goto fail; 44117 string_buffer_putc8(b, '/'); 44118 flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags); 44119 if (string_buffer_concat_value_free(b, flags)) 44120 goto fail; 44121 return string_buffer_end(b); 44122 44123 fail: 44124 string_buffer_free(b); 44125 return JS_EXCEPTION; 44126 } 44127 44128 BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) 44129 { 44130 JSContext *ctx = opaque; 44131 return js_check_stack_overflow(ctx->rt, alloca_size); 44132 } 44133 44134 void *lre_realloc(void *opaque, void *ptr, size_t size) 44135 { 44136 JSContext *ctx = opaque; 44137 /* No JS exception is raised here */ 44138 return js_realloc_rt(ctx->rt, ptr, size); 44139 } 44140 44141 static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, 44142 int argc, JSValueConst *argv) 44143 { 44144 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 44145 JSString *str; 44146 JSValue t, ret, str_val, obj, val, groups; 44147 JSValue indices, indices_groups; 44148 uint8_t *re_bytecode; 44149 uint8_t **capture, *str_buf; 44150 int rc, capture_count, shift, i, re_flags; 44151 int64_t last_index; 44152 const char *group_name_ptr; 44153 44154 if (!re) 44155 return JS_EXCEPTION; 44156 44157 str_val = JS_ToString(ctx, argv[0]); 44158 if (JS_IsException(str_val)) 44159 return JS_EXCEPTION; 44160 44161 ret = JS_EXCEPTION; 44162 obj = JS_NULL; 44163 groups = JS_UNDEFINED; 44164 indices = JS_UNDEFINED; 44165 indices_groups = JS_UNDEFINED; 44166 capture = NULL; 44167 44168 val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); 44169 if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) 44170 goto fail; 44171 44172 re_bytecode = re->bytecode->u.str8; 44173 re_flags = lre_get_flags(re_bytecode); 44174 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { 44175 last_index = 0; 44176 } 44177 str = JS_VALUE_GET_STRING(str_val); 44178 capture_count = lre_get_capture_count(re_bytecode); 44179 if (capture_count > 0) { 44180 capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); 44181 if (!capture) 44182 goto fail; 44183 } 44184 shift = str->is_wide_char; 44185 str_buf = str->u.str8; 44186 if (last_index > str->len) { 44187 rc = 2; 44188 } else { 44189 rc = lre_exec(capture, re_bytecode, 44190 str_buf, last_index, str->len, 44191 shift, ctx); 44192 } 44193 if (rc != 1) { 44194 if (rc >= 0) { 44195 if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { 44196 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 44197 JS_NewInt32(ctx, 0)) < 0) 44198 goto fail; 44199 } 44200 } else { 44201 JS_ThrowInternalError(ctx, "out of memory in regexp execution"); 44202 goto fail; 44203 } 44204 } else { 44205 int prop_flags; 44206 if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { 44207 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 44208 JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0) 44209 goto fail; 44210 } 44211 obj = JS_NewArray(ctx); 44212 if (JS_IsException(obj)) 44213 goto fail; 44214 prop_flags = JS_PROP_C_W_E | JS_PROP_THROW; 44215 group_name_ptr = lre_get_groupnames(re_bytecode); 44216 if (group_name_ptr) { 44217 groups = JS_NewObjectProto(ctx, JS_NULL); 44218 if (JS_IsException(groups)) 44219 goto fail; 44220 } 44221 if (re_flags & LRE_FLAG_INDICES) { 44222 indices = JS_NewArray(ctx); 44223 if (JS_IsException(indices)) 44224 goto fail; 44225 if (group_name_ptr) { 44226 indices_groups = JS_NewObjectProto(ctx, JS_NULL); 44227 if (JS_IsException(indices_groups)) 44228 goto fail; 44229 } 44230 } 44231 44232 for(i = 0; i < capture_count; i++) { 44233 const char *name = NULL; 44234 uint8_t **match = &capture[2 * i]; 44235 int start = -1; 44236 int end = -1; 44237 JSValue val; 44238 44239 if (group_name_ptr && i > 0) { 44240 if (*group_name_ptr) name = group_name_ptr; 44241 group_name_ptr += strlen(group_name_ptr) + 1; 44242 } 44243 44244 if (match[0] && match[1]) { 44245 start = (match[0] - str_buf) >> shift; 44246 end = (match[1] - str_buf) >> shift; 44247 } 44248 44249 if (!JS_IsUndefined(indices)) { 44250 val = JS_UNDEFINED; 44251 if (start != -1) { 44252 val = JS_NewArray(ctx); 44253 if (JS_IsException(val)) 44254 goto fail; 44255 if (JS_DefinePropertyValueUint32(ctx, val, 0, 44256 JS_NewInt32(ctx, start), 44257 prop_flags) < 0) { 44258 JS_FreeValue(ctx, val); 44259 goto fail; 44260 } 44261 if (JS_DefinePropertyValueUint32(ctx, val, 1, 44262 JS_NewInt32(ctx, end), 44263 prop_flags) < 0) { 44264 JS_FreeValue(ctx, val); 44265 goto fail; 44266 } 44267 } 44268 if (name && !JS_IsUndefined(indices_groups)) { 44269 val = JS_DupValue(ctx, val); 44270 if (JS_DefinePropertyValueStr(ctx, indices_groups, 44271 name, val, prop_flags) < 0) { 44272 JS_FreeValue(ctx, val); 44273 goto fail; 44274 } 44275 } 44276 if (JS_DefinePropertyValueUint32(ctx, indices, i, val, 44277 prop_flags) < 0) { 44278 goto fail; 44279 } 44280 } 44281 44282 val = JS_UNDEFINED; 44283 if (start != -1) { 44284 val = js_sub_string(ctx, str, start, end); 44285 if (JS_IsException(val)) 44286 goto fail; 44287 } 44288 44289 if (name) { 44290 if (JS_DefinePropertyValueStr(ctx, groups, name, 44291 JS_DupValue(ctx, val), 44292 prop_flags) < 0) { 44293 JS_FreeValue(ctx, val); 44294 goto fail; 44295 } 44296 } 44297 44298 if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) 44299 goto fail; 44300 } 44301 44302 t = groups, groups = JS_UNDEFINED; 44303 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, 44304 t, prop_flags) < 0) { 44305 goto fail; 44306 } 44307 44308 t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); 44309 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0) 44310 goto fail; 44311 44312 t = str_val, str_val = JS_UNDEFINED; 44313 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0) 44314 goto fail; 44315 44316 if (!JS_IsUndefined(indices)) { 44317 t = indices_groups, indices_groups = JS_UNDEFINED; 44318 if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, 44319 t, prop_flags) < 0) { 44320 goto fail; 44321 } 44322 t = indices, indices = JS_UNDEFINED; 44323 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, 44324 t, prop_flags) < 0) { 44325 goto fail; 44326 } 44327 } 44328 } 44329 ret = obj; 44330 obj = JS_UNDEFINED; 44331 fail: 44332 JS_FreeValue(ctx, indices_groups); 44333 JS_FreeValue(ctx, indices); 44334 JS_FreeValue(ctx, str_val); 44335 JS_FreeValue(ctx, groups); 44336 JS_FreeValue(ctx, obj); 44337 js_free(ctx, capture); 44338 return ret; 44339 } 44340 44341 /* delete portions of a string that match a given regex */ 44342 static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg) 44343 { 44344 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); 44345 JSString *str; 44346 JSValue str_val, val; 44347 uint8_t *re_bytecode; 44348 int ret; 44349 uint8_t **capture, *str_buf; 44350 int capture_count, shift, re_flags; 44351 int next_src_pos, start, end; 44352 int64_t last_index; 44353 StringBuffer b_s, *b = &b_s; 44354 44355 if (!re) 44356 return JS_EXCEPTION; 44357 44358 string_buffer_init(ctx, b, 0); 44359 44360 capture = NULL; 44361 str_val = JS_ToString(ctx, arg); 44362 if (JS_IsException(str_val)) 44363 goto fail; 44364 str = JS_VALUE_GET_STRING(str_val); 44365 re_bytecode = re->bytecode->u.str8; 44366 re_flags = lre_get_flags(re_bytecode); 44367 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { 44368 last_index = 0; 44369 } else { 44370 val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); 44371 if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) 44372 goto fail; 44373 } 44374 capture_count = lre_get_capture_count(re_bytecode); 44375 if (capture_count > 0) { 44376 capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); 44377 if (!capture) 44378 goto fail; 44379 } 44380 shift = str->is_wide_char; 44381 str_buf = str->u.str8; 44382 next_src_pos = 0; 44383 for (;;) { 44384 if (last_index > str->len) 44385 break; 44386 44387 ret = lre_exec(capture, re_bytecode, 44388 str_buf, last_index, str->len, shift, ctx); 44389 if (ret != 1) { 44390 if (ret >= 0) { 44391 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { 44392 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 44393 JS_NewInt32(ctx, 0)) < 0) 44394 goto fail; 44395 } 44396 } else { 44397 JS_ThrowInternalError(ctx, "out of memory in regexp execution"); 44398 goto fail; 44399 } 44400 break; 44401 } 44402 start = (capture[0] - str_buf) >> shift; 44403 end = (capture[1] - str_buf) >> shift; 44404 last_index = end; 44405 if (next_src_pos < start) { 44406 if (string_buffer_concat(b, str, next_src_pos, start)) 44407 goto fail; 44408 } 44409 next_src_pos = end; 44410 if (!(re_flags & LRE_FLAG_GLOBAL)) { 44411 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, 44412 JS_NewInt32(ctx, end)) < 0) 44413 goto fail; 44414 break; 44415 } 44416 if (end == start) { 44417 if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) { 44418 end++; 44419 } else { 44420 string_getc(str, &end); 44421 } 44422 } 44423 last_index = end; 44424 } 44425 if (string_buffer_concat(b, str, next_src_pos, str->len)) 44426 goto fail; 44427 JS_FreeValue(ctx, str_val); 44428 js_free(ctx, capture); 44429 return string_buffer_end(b); 44430 fail: 44431 JS_FreeValue(ctx, str_val); 44432 js_free(ctx, capture); 44433 string_buffer_free(b); 44434 return JS_EXCEPTION; 44435 } 44436 44437 static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s) 44438 { 44439 JSValue method, ret; 44440 44441 method = JS_GetProperty(ctx, r, JS_ATOM_exec); 44442 if (JS_IsException(method)) 44443 return method; 44444 if (JS_IsFunction(ctx, method)) { 44445 ret = JS_CallFree(ctx, method, r, 1, &s); 44446 if (JS_IsException(ret)) 44447 return ret; 44448 if (!JS_IsObject(ret) && !JS_IsNull(ret)) { 44449 JS_FreeValue(ctx, ret); 44450 return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null"); 44451 } 44452 return ret; 44453 } 44454 JS_FreeValue(ctx, method); 44455 return js_regexp_exec(ctx, r, 1, &s); 44456 } 44457 44458 #if 0 44459 static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val, 44460 int argc, JSValueConst *argv) 44461 { 44462 return JS_RegExpExec(ctx, argv[0], argv[1]); 44463 } 44464 static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val, 44465 int argc, JSValueConst *argv) 44466 { 44467 return JS_RegExpDelete(ctx, argv[0], argv[1]); 44468 } 44469 #endif 44470 44471 static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val, 44472 int argc, JSValueConst *argv) 44473 { 44474 JSValue val; 44475 BOOL ret; 44476 44477 val = JS_RegExpExec(ctx, this_val, argv[0]); 44478 if (JS_IsException(val)) 44479 return JS_EXCEPTION; 44480 ret = !JS_IsNull(val); 44481 JS_FreeValue(ctx, val); 44482 return JS_NewBool(ctx, ret); 44483 } 44484 44485 static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, 44486 int argc, JSValueConst *argv) 44487 { 44488 // [Symbol.match](str) 44489 JSValueConst rx = this_val; 44490 JSValue A, S, flags, result, matchStr; 44491 int global, n, fullUnicode, isEmpty; 44492 JSString *p; 44493 44494 if (!JS_IsObject(rx)) 44495 return JS_ThrowTypeErrorNotAnObject(ctx); 44496 44497 A = JS_UNDEFINED; 44498 flags = JS_UNDEFINED; 44499 result = JS_UNDEFINED; 44500 matchStr = JS_UNDEFINED; 44501 S = JS_ToString(ctx, argv[0]); 44502 if (JS_IsException(S)) 44503 goto exception; 44504 44505 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); 44506 if (JS_IsException(flags)) 44507 goto exception; 44508 flags = JS_ToStringFree(ctx, flags); 44509 if (JS_IsException(flags)) 44510 goto exception; 44511 p = JS_VALUE_GET_STRING(flags); 44512 44513 // TODO(bnoordhuis) query 'u' flag the same way? 44514 global = (-1 != string_indexof_char(p, 'g', 0)); 44515 if (!global) { 44516 A = JS_RegExpExec(ctx, rx, S); 44517 } else { 44518 fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); 44519 if (fullUnicode < 0) 44520 goto exception; 44521 44522 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) 44523 goto exception; 44524 A = JS_NewArray(ctx); 44525 if (JS_IsException(A)) 44526 goto exception; 44527 n = 0; 44528 for(;;) { 44529 JS_FreeValue(ctx, result); 44530 result = JS_RegExpExec(ctx, rx, S); 44531 if (JS_IsException(result)) 44532 goto exception; 44533 if (JS_IsNull(result)) 44534 break; 44535 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 44536 if (JS_IsException(matchStr)) 44537 goto exception; 44538 isEmpty = JS_IsEmptyString(matchStr); 44539 if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0) 44540 goto exception; 44541 if (isEmpty) { 44542 int64_t thisIndex, nextIndex; 44543 if (JS_ToLengthFree(ctx, &thisIndex, 44544 JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) 44545 goto exception; 44546 p = JS_VALUE_GET_STRING(S); 44547 nextIndex = string_advance_index(p, thisIndex, fullUnicode); 44548 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) 44549 goto exception; 44550 } 44551 } 44552 if (n == 0) { 44553 JS_FreeValue(ctx, A); 44554 A = JS_NULL; 44555 } 44556 } 44557 JS_FreeValue(ctx, result); 44558 JS_FreeValue(ctx, flags); 44559 JS_FreeValue(ctx, S); 44560 return A; 44561 44562 exception: 44563 JS_FreeValue(ctx, A); 44564 JS_FreeValue(ctx, result); 44565 JS_FreeValue(ctx, flags); 44566 JS_FreeValue(ctx, S); 44567 return JS_EXCEPTION; 44568 } 44569 44570 typedef struct JSRegExpStringIteratorData { 44571 JSValue iterating_regexp; 44572 JSValue iterated_string; 44573 BOOL global; 44574 BOOL unicode; 44575 BOOL done; 44576 } JSRegExpStringIteratorData; 44577 44578 static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val) 44579 { 44580 JSObject *p = JS_VALUE_GET_OBJ(val); 44581 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; 44582 if (it) { 44583 JS_FreeValueRT(rt, it->iterating_regexp); 44584 JS_FreeValueRT(rt, it->iterated_string); 44585 js_free_rt(rt, it); 44586 } 44587 } 44588 44589 static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val, 44590 JS_MarkFunc *mark_func) 44591 { 44592 JSObject *p = JS_VALUE_GET_OBJ(val); 44593 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data; 44594 if (it) { 44595 JS_MarkValue(rt, it->iterating_regexp, mark_func); 44596 JS_MarkValue(rt, it->iterated_string, mark_func); 44597 } 44598 } 44599 44600 static JSValue js_regexp_string_iterator_next(JSContext *ctx, 44601 JSValueConst this_val, 44602 int argc, JSValueConst *argv, 44603 BOOL *pdone, int magic) 44604 { 44605 JSRegExpStringIteratorData *it; 44606 JSValueConst R, S; 44607 JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED; 44608 JSString *sp; 44609 44610 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR); 44611 if (!it) 44612 goto exception; 44613 if (it->done) { 44614 *pdone = TRUE; 44615 return JS_UNDEFINED; 44616 } 44617 R = it->iterating_regexp; 44618 S = it->iterated_string; 44619 match = JS_RegExpExec(ctx, R, S); 44620 if (JS_IsException(match)) 44621 goto exception; 44622 if (JS_IsNull(match)) { 44623 it->done = TRUE; 44624 *pdone = TRUE; 44625 return JS_UNDEFINED; 44626 } else if (it->global) { 44627 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0)); 44628 if (JS_IsException(matchStr)) 44629 goto exception; 44630 if (JS_IsEmptyString(matchStr)) { 44631 int64_t thisIndex, nextIndex; 44632 if (JS_ToLengthFree(ctx, &thisIndex, 44633 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0) 44634 goto exception; 44635 sp = JS_VALUE_GET_STRING(S); 44636 nextIndex = string_advance_index(sp, thisIndex, it->unicode); 44637 if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex, 44638 JS_NewInt64(ctx, nextIndex)) < 0) 44639 goto exception; 44640 } 44641 JS_FreeValue(ctx, matchStr); 44642 } else { 44643 it->done = TRUE; 44644 } 44645 *pdone = FALSE; 44646 return match; 44647 exception: 44648 JS_FreeValue(ctx, match); 44649 JS_FreeValue(ctx, matchStr); 44650 *pdone = FALSE; 44651 return JS_EXCEPTION; 44652 } 44653 44654 static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val, 44655 int argc, JSValueConst *argv) 44656 { 44657 // [Symbol.matchAll](str) 44658 JSValueConst R = this_val; 44659 JSValue S, C, flags, matcher, iter; 44660 JSValueConst args[2]; 44661 JSString *strp; 44662 int64_t lastIndex; 44663 JSRegExpStringIteratorData *it; 44664 44665 if (!JS_IsObject(R)) 44666 return JS_ThrowTypeErrorNotAnObject(ctx); 44667 44668 C = JS_UNDEFINED; 44669 flags = JS_UNDEFINED; 44670 matcher = JS_UNDEFINED; 44671 iter = JS_UNDEFINED; 44672 44673 S = JS_ToString(ctx, argv[0]); 44674 if (JS_IsException(S)) 44675 goto exception; 44676 C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor); 44677 if (JS_IsException(C)) 44678 goto exception; 44679 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags)); 44680 if (JS_IsException(flags)) 44681 goto exception; 44682 args[0] = R; 44683 args[1] = flags; 44684 matcher = JS_CallConstructor(ctx, C, 2, args); 44685 if (JS_IsException(matcher)) 44686 goto exception; 44687 if (JS_ToLengthFree(ctx, &lastIndex, 44688 JS_GetProperty(ctx, R, JS_ATOM_lastIndex))) 44689 goto exception; 44690 if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex, 44691 JS_NewInt64(ctx, lastIndex)) < 0) 44692 goto exception; 44693 44694 iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR); 44695 if (JS_IsException(iter)) 44696 goto exception; 44697 it = js_malloc(ctx, sizeof(*it)); 44698 if (!it) 44699 goto exception; 44700 it->iterating_regexp = matcher; 44701 it->iterated_string = S; 44702 strp = JS_VALUE_GET_STRING(flags); 44703 it->global = string_indexof_char(strp, 'g', 0) >= 0; 44704 it->unicode = string_indexof_char(strp, 'u', 0) >= 0; 44705 it->done = FALSE; 44706 JS_SetOpaque(iter, it); 44707 44708 JS_FreeValue(ctx, C); 44709 JS_FreeValue(ctx, flags); 44710 return iter; 44711 exception: 44712 JS_FreeValue(ctx, S); 44713 JS_FreeValue(ctx, C); 44714 JS_FreeValue(ctx, flags); 44715 JS_FreeValue(ctx, matcher); 44716 JS_FreeValue(ctx, iter); 44717 return JS_EXCEPTION; 44718 } 44719 44720 typedef struct ValueBuffer { 44721 JSContext *ctx; 44722 JSValue *arr; 44723 JSValue def[4]; 44724 int len; 44725 int size; 44726 int error_status; 44727 } ValueBuffer; 44728 44729 static int value_buffer_init(JSContext *ctx, ValueBuffer *b) 44730 { 44731 b->ctx = ctx; 44732 b->len = 0; 44733 b->size = 4; 44734 b->error_status = 0; 44735 b->arr = b->def; 44736 return 0; 44737 } 44738 44739 static void value_buffer_free(ValueBuffer *b) 44740 { 44741 while (b->len > 0) 44742 JS_FreeValue(b->ctx, b->arr[--b->len]); 44743 if (b->arr != b->def) 44744 js_free(b->ctx, b->arr); 44745 b->arr = b->def; 44746 b->size = 4; 44747 } 44748 44749 static int value_buffer_append(ValueBuffer *b, JSValue val) 44750 { 44751 if (b->error_status) 44752 return -1; 44753 44754 if (b->len >= b->size) { 44755 int new_size = (b->len + (b->len >> 1) + 31) & ~16; 44756 size_t slack; 44757 JSValue *new_arr; 44758 44759 if (b->arr == b->def) { 44760 new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack); 44761 if (new_arr) 44762 memcpy(new_arr, b->def, sizeof b->def); 44763 } else { 44764 new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack); 44765 } 44766 if (!new_arr) { 44767 value_buffer_free(b); 44768 JS_FreeValue(b->ctx, val); 44769 b->error_status = -1; 44770 return -1; 44771 } 44772 new_size += slack / sizeof(*new_arr); 44773 b->arr = new_arr; 44774 b->size = new_size; 44775 } 44776 b->arr[b->len++] = val; 44777 return 0; 44778 } 44779 44780 static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx) 44781 { 44782 JSValue val; 44783 int res; 44784 44785 val = JS_GetProperty(ctx, rx, JS_ATOM_constructor); 44786 if (JS_IsException(val)) 44787 return -1; 44788 // rx.constructor === RegExp 44789 res = js_same_value(ctx, val, ctx->regexp_ctor); 44790 JS_FreeValue(ctx, val); 44791 if (res) { 44792 val = JS_GetProperty(ctx, rx, JS_ATOM_exec); 44793 if (JS_IsException(val)) 44794 return -1; 44795 // rx.exec === RE_exec 44796 res = JS_IsCFunction(ctx, val, js_regexp_exec, 0); 44797 JS_FreeValue(ctx, val); 44798 } 44799 return res; 44800 } 44801 44802 static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, 44803 int argc, JSValueConst *argv) 44804 { 44805 // [Symbol.replace](str, rep) 44806 JSValueConst rx = this_val, rep = argv[1]; 44807 JSValueConst args[6]; 44808 JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; 44809 JSString *p, *sp, *rp; 44810 StringBuffer b_s, *b = &b_s; 44811 ValueBuffer v_b, *results = &v_b; 44812 int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; 44813 uint32_t nCaptures; 44814 int64_t position; 44815 44816 if (!JS_IsObject(rx)) 44817 return JS_ThrowTypeErrorNotAnObject(ctx); 44818 44819 string_buffer_init(ctx, b, 0); 44820 value_buffer_init(ctx, results); 44821 44822 rep_val = JS_UNDEFINED; 44823 matched = JS_UNDEFINED; 44824 tab = JS_UNDEFINED; 44825 flags = JS_UNDEFINED; 44826 rep_str = JS_UNDEFINED; 44827 namedCaptures = JS_UNDEFINED; 44828 44829 str = JS_ToString(ctx, argv[0]); 44830 if (JS_IsException(str)) 44831 goto exception; 44832 44833 sp = JS_VALUE_GET_STRING(str); 44834 rp = NULL; 44835 functionalReplace = JS_IsFunction(ctx, rep); 44836 if (!functionalReplace) { 44837 rep_val = JS_ToString(ctx, rep); 44838 if (JS_IsException(rep_val)) 44839 goto exception; 44840 rp = JS_VALUE_GET_STRING(rep_val); 44841 } 44842 44843 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); 44844 if (JS_IsException(flags)) 44845 goto exception; 44846 flags = JS_ToStringFree(ctx, flags); 44847 if (JS_IsException(flags)) 44848 goto exception; 44849 p = JS_VALUE_GET_STRING(flags); 44850 44851 // TODO(bnoordhuis) query 'u' flag the same way? 44852 fullUnicode = 0; 44853 is_global = (-1 != string_indexof_char(p, 'g', 0)); 44854 if (is_global) { 44855 fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); 44856 if (fullUnicode < 0) 44857 goto exception; 44858 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) 44859 goto exception; 44860 } 44861 44862 if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) { 44863 /* use faster version for simple cases */ 44864 res = JS_RegExpDelete(ctx, rx, str); 44865 goto done; 44866 } 44867 for(;;) { 44868 JSValue result; 44869 result = JS_RegExpExec(ctx, rx, str); 44870 if (JS_IsException(result)) 44871 goto exception; 44872 if (JS_IsNull(result)) 44873 break; 44874 if (value_buffer_append(results, result) < 0) 44875 goto exception; 44876 if (!is_global) 44877 break; 44878 JS_FreeValue(ctx, matched); 44879 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 44880 if (JS_IsException(matched)) 44881 goto exception; 44882 if (JS_IsEmptyString(matched)) { 44883 /* always advance of at least one char */ 44884 int64_t thisIndex, nextIndex; 44885 if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0) 44886 goto exception; 44887 nextIndex = string_advance_index(sp, thisIndex, fullUnicode); 44888 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0) 44889 goto exception; 44890 } 44891 } 44892 nextSourcePosition = 0; 44893 for(j = 0; j < results->len; j++) { 44894 JSValueConst result; 44895 result = results->arr[j]; 44896 if (js_get_length32(ctx, &nCaptures, result) < 0) 44897 goto exception; 44898 JS_FreeValue(ctx, matched); 44899 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0)); 44900 if (JS_IsException(matched)) 44901 goto exception; 44902 if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index))) 44903 goto exception; 44904 if (position > sp->len) 44905 position = sp->len; 44906 else if (position < 0) 44907 position = 0; 44908 /* ignore substition if going backward (can happen 44909 with custom regexp object) */ 44910 JS_FreeValue(ctx, tab); 44911 tab = JS_NewArray(ctx); 44912 if (JS_IsException(tab)) 44913 goto exception; 44914 if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched), 44915 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44916 goto exception; 44917 for(n = 1; n < nCaptures; n++) { 44918 JSValue capN; 44919 capN = JS_GetPropertyInt64(ctx, result, n); 44920 if (JS_IsException(capN)) 44921 goto exception; 44922 if (!JS_IsUndefined(capN)) { 44923 capN = JS_ToStringFree(ctx, capN); 44924 if (JS_IsException(capN)) 44925 goto exception; 44926 } 44927 if (JS_DefinePropertyValueInt64(ctx, tab, n, capN, 44928 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44929 goto exception; 44930 } 44931 JS_FreeValue(ctx, namedCaptures); 44932 namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups); 44933 if (JS_IsException(namedCaptures)) 44934 goto exception; 44935 if (functionalReplace) { 44936 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44937 goto exception; 44938 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44939 goto exception; 44940 if (!JS_IsUndefined(namedCaptures)) { 44941 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0) 44942 goto exception; 44943 } 44944 args[0] = JS_UNDEFINED; 44945 args[1] = tab; 44946 JS_FreeValue(ctx, rep_str); 44947 rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0)); 44948 } else { 44949 JSValue namedCaptures1; 44950 if (!JS_IsUndefined(namedCaptures)) { 44951 namedCaptures1 = JS_ToObject(ctx, namedCaptures); 44952 if (JS_IsException(namedCaptures1)) 44953 goto exception; 44954 } else { 44955 namedCaptures1 = JS_UNDEFINED; 44956 } 44957 args[0] = matched; 44958 args[1] = str; 44959 args[2] = JS_NewInt32(ctx, position); 44960 args[3] = tab; 44961 args[4] = namedCaptures1; 44962 args[5] = rep_val; 44963 JS_FreeValue(ctx, rep_str); 44964 rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args); 44965 JS_FreeValue(ctx, namedCaptures1); 44966 } 44967 if (JS_IsException(rep_str)) 44968 goto exception; 44969 if (position >= nextSourcePosition) { 44970 string_buffer_concat(b, sp, nextSourcePosition, position); 44971 string_buffer_concat_value(b, rep_str); 44972 nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len; 44973 } 44974 } 44975 string_buffer_concat(b, sp, nextSourcePosition, sp->len); 44976 res = string_buffer_end(b); 44977 goto done1; 44978 44979 exception: 44980 res = JS_EXCEPTION; 44981 done: 44982 string_buffer_free(b); 44983 done1: 44984 value_buffer_free(results); 44985 JS_FreeValue(ctx, rep_val); 44986 JS_FreeValue(ctx, matched); 44987 JS_FreeValue(ctx, flags); 44988 JS_FreeValue(ctx, tab); 44989 JS_FreeValue(ctx, rep_str); 44990 JS_FreeValue(ctx, namedCaptures); 44991 JS_FreeValue(ctx, str); 44992 return res; 44993 } 44994 44995 static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val, 44996 int argc, JSValueConst *argv) 44997 { 44998 JSValueConst rx = this_val; 44999 JSValue str, previousLastIndex, currentLastIndex, result, index; 45000 45001 if (!JS_IsObject(rx)) 45002 return JS_ThrowTypeErrorNotAnObject(ctx); 45003 45004 result = JS_UNDEFINED; 45005 currentLastIndex = JS_UNDEFINED; 45006 previousLastIndex = JS_UNDEFINED; 45007 str = JS_ToString(ctx, argv[0]); 45008 if (JS_IsException(str)) 45009 goto exception; 45010 45011 previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); 45012 if (JS_IsException(previousLastIndex)) 45013 goto exception; 45014 45015 if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) { 45016 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) { 45017 goto exception; 45018 } 45019 } 45020 result = JS_RegExpExec(ctx, rx, str); 45021 if (JS_IsException(result)) 45022 goto exception; 45023 currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex); 45024 if (JS_IsException(currentLastIndex)) 45025 goto exception; 45026 if (js_same_value(ctx, currentLastIndex, previousLastIndex)) { 45027 JS_FreeValue(ctx, previousLastIndex); 45028 } else { 45029 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) { 45030 previousLastIndex = JS_UNDEFINED; 45031 goto exception; 45032 } 45033 } 45034 JS_FreeValue(ctx, str); 45035 JS_FreeValue(ctx, currentLastIndex); 45036 45037 if (JS_IsNull(result)) { 45038 return JS_NewInt32(ctx, -1); 45039 } else { 45040 index = JS_GetProperty(ctx, result, JS_ATOM_index); 45041 JS_FreeValue(ctx, result); 45042 return index; 45043 } 45044 45045 exception: 45046 JS_FreeValue(ctx, result); 45047 JS_FreeValue(ctx, str); 45048 JS_FreeValue(ctx, currentLastIndex); 45049 JS_FreeValue(ctx, previousLastIndex); 45050 return JS_EXCEPTION; 45051 } 45052 45053 static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val, 45054 int argc, JSValueConst *argv) 45055 { 45056 // [Symbol.split](str, limit) 45057 JSValueConst rx = this_val; 45058 JSValueConst args[2]; 45059 JSValue str, ctor, splitter, A, flags, z, sub; 45060 JSString *strp; 45061 uint32_t lim, size, p, q; 45062 int unicodeMatching; 45063 int64_t lengthA, e, numberOfCaptures, i; 45064 45065 if (!JS_IsObject(rx)) 45066 return JS_ThrowTypeErrorNotAnObject(ctx); 45067 45068 ctor = JS_UNDEFINED; 45069 splitter = JS_UNDEFINED; 45070 A = JS_UNDEFINED; 45071 flags = JS_UNDEFINED; 45072 z = JS_UNDEFINED; 45073 str = JS_ToString(ctx, argv[0]); 45074 if (JS_IsException(str)) 45075 goto exception; 45076 ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor); 45077 if (JS_IsException(ctor)) 45078 goto exception; 45079 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags)); 45080 if (JS_IsException(flags)) 45081 goto exception; 45082 strp = JS_VALUE_GET_STRING(flags); 45083 unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0; 45084 if (string_indexof_char(strp, 'y', 0) < 0) { 45085 flags = JS_ConcatString3(ctx, "", flags, "y"); 45086 if (JS_IsException(flags)) 45087 goto exception; 45088 } 45089 args[0] = rx; 45090 args[1] = flags; 45091 splitter = JS_CallConstructor(ctx, ctor, 2, args); 45092 if (JS_IsException(splitter)) 45093 goto exception; 45094 A = JS_NewArray(ctx); 45095 if (JS_IsException(A)) 45096 goto exception; 45097 lengthA = 0; 45098 if (JS_IsUndefined(argv[1])) { 45099 lim = 0xffffffff; 45100 } else { 45101 if (JS_ToUint32(ctx, &lim, argv[1]) < 0) 45102 goto exception; 45103 if (lim == 0) 45104 goto done; 45105 } 45106 strp = JS_VALUE_GET_STRING(str); 45107 p = q = 0; 45108 size = strp->len; 45109 if (size == 0) { 45110 z = JS_RegExpExec(ctx, splitter, str); 45111 if (JS_IsException(z)) 45112 goto exception; 45113 if (JS_IsNull(z)) 45114 goto add_tail; 45115 goto done; 45116 } 45117 while (q < size) { 45118 if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0) 45119 goto exception; 45120 JS_FreeValue(ctx, z); 45121 z = JS_RegExpExec(ctx, splitter, str); 45122 if (JS_IsException(z)) 45123 goto exception; 45124 if (JS_IsNull(z)) { 45125 q = string_advance_index(strp, q, unicodeMatching); 45126 } else { 45127 if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex))) 45128 goto exception; 45129 if (e > size) 45130 e = size; 45131 if (e == p) { 45132 q = string_advance_index(strp, q, unicodeMatching); 45133 } else { 45134 sub = js_sub_string(ctx, strp, p, q); 45135 if (JS_IsException(sub)) 45136 goto exception; 45137 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, 45138 JS_PROP_C_W_E | JS_PROP_THROW) < 0) 45139 goto exception; 45140 if (lengthA == lim) 45141 goto done; 45142 p = e; 45143 if (js_get_length64(ctx, &numberOfCaptures, z)) 45144 goto exception; 45145 for(i = 1; i < numberOfCaptures; i++) { 45146 sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i)); 45147 if (JS_IsException(sub)) 45148 goto exception; 45149 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 45150 goto exception; 45151 if (lengthA == lim) 45152 goto done; 45153 } 45154 q = p; 45155 } 45156 } 45157 } 45158 add_tail: 45159 if (p > size) 45160 p = size; 45161 sub = js_sub_string(ctx, strp, p, size); 45162 if (JS_IsException(sub)) 45163 goto exception; 45164 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 45165 goto exception; 45166 goto done; 45167 exception: 45168 JS_FreeValue(ctx, A); 45169 A = JS_EXCEPTION; 45170 done: 45171 JS_FreeValue(ctx, str); 45172 JS_FreeValue(ctx, ctor); 45173 JS_FreeValue(ctx, splitter); 45174 JS_FreeValue(ctx, flags); 45175 JS_FreeValue(ctx, z); 45176 return A; 45177 } 45178 45179 static const JSCFunctionListEntry js_regexp_funcs[] = { 45180 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 45181 //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ), 45182 //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ), 45183 }; 45184 45185 static const JSCFunctionListEntry js_regexp_proto_funcs[] = { 45186 JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), 45187 JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), 45188 JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ), 45189 JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ), 45190 JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ), 45191 JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ), 45192 JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ), 45193 JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ), 45194 JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ), 45195 JS_CFUNC_DEF("exec", 1, js_regexp_exec ), 45196 JS_CFUNC_DEF("compile", 2, js_regexp_compile ), 45197 JS_CFUNC_DEF("test", 1, js_regexp_test ), 45198 JS_CFUNC_DEF("toString", 0, js_regexp_toString ), 45199 JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ), 45200 JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ), 45201 JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ), 45202 JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ), 45203 JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ), 45204 //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ), 45205 //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ), 45206 }; 45207 45208 static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = { 45209 JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ), 45210 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ), 45211 }; 45212 45213 void JS_AddIntrinsicRegExpCompiler(JSContext *ctx) 45214 { 45215 ctx->compile_regexp = js_compile_regexp; 45216 } 45217 45218 void JS_AddIntrinsicRegExp(JSContext *ctx) 45219 { 45220 JSValueConst obj; 45221 45222 JS_AddIntrinsicRegExpCompiler(ctx); 45223 45224 ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx); 45225 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs, 45226 countof(js_regexp_proto_funcs)); 45227 obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2, 45228 ctx->class_proto[JS_CLASS_REGEXP]); 45229 ctx->regexp_ctor = JS_DupValue(ctx, obj); 45230 JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs)); 45231 45232 ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] = 45233 JS_NewObjectProto(ctx, ctx->iterator_proto); 45234 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR], 45235 js_regexp_string_iterator_proto_funcs, 45236 countof(js_regexp_string_iterator_proto_funcs)); 45237 } 45238 45239 /* JSON */ 45240 45241 static int json_parse_expect(JSParseState *s, int tok) 45242 { 45243 if (s->token.val != tok) { 45244 /* XXX: dump token correctly in all cases */ 45245 return js_parse_error(s, "expecting '%c'", tok); 45246 } 45247 return json_next_token(s); 45248 } 45249 45250 static JSValue json_parse_value(JSParseState *s) 45251 { 45252 JSContext *ctx = s->ctx; 45253 JSValue val = JS_NULL; 45254 int ret; 45255 45256 switch(s->token.val) { 45257 case '{': 45258 { 45259 JSValue prop_val; 45260 JSAtom prop_name; 45261 45262 if (json_next_token(s)) 45263 goto fail; 45264 val = JS_NewObject(ctx); 45265 if (JS_IsException(val)) 45266 goto fail; 45267 if (s->token.val != '}') { 45268 for(;;) { 45269 if (s->token.val == TOK_STRING) { 45270 prop_name = JS_ValueToAtom(ctx, s->token.u.str.str); 45271 if (prop_name == JS_ATOM_NULL) 45272 goto fail; 45273 } else if (s->ext_json && s->token.val == TOK_IDENT) { 45274 prop_name = JS_DupAtom(ctx, s->token.u.ident.atom); 45275 } else { 45276 js_parse_error(s, "expecting property name"); 45277 goto fail; 45278 } 45279 if (json_next_token(s)) 45280 goto fail1; 45281 if (json_parse_expect(s, ':')) 45282 goto fail1; 45283 prop_val = json_parse_value(s); 45284 if (JS_IsException(prop_val)) { 45285 fail1: 45286 JS_FreeAtom(ctx, prop_name); 45287 goto fail; 45288 } 45289 ret = JS_DefinePropertyValue(ctx, val, prop_name, 45290 prop_val, JS_PROP_C_W_E); 45291 JS_FreeAtom(ctx, prop_name); 45292 if (ret < 0) 45293 goto fail; 45294 45295 if (s->token.val != ',') 45296 break; 45297 if (json_next_token(s)) 45298 goto fail; 45299 if (s->ext_json && s->token.val == '}') 45300 break; 45301 } 45302 } 45303 if (json_parse_expect(s, '}')) 45304 goto fail; 45305 } 45306 break; 45307 case '[': 45308 { 45309 JSValue el; 45310 uint32_t idx; 45311 45312 if (json_next_token(s)) 45313 goto fail; 45314 val = JS_NewArray(ctx); 45315 if (JS_IsException(val)) 45316 goto fail; 45317 if (s->token.val != ']') { 45318 idx = 0; 45319 for(;;) { 45320 el = json_parse_value(s); 45321 if (JS_IsException(el)) 45322 goto fail; 45323 ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E); 45324 if (ret < 0) 45325 goto fail; 45326 if (s->token.val != ',') 45327 break; 45328 if (json_next_token(s)) 45329 goto fail; 45330 idx++; 45331 if (s->ext_json && s->token.val == ']') 45332 break; 45333 } 45334 } 45335 if (json_parse_expect(s, ']')) 45336 goto fail; 45337 } 45338 break; 45339 case TOK_STRING: 45340 val = JS_DupValue(ctx, s->token.u.str.str); 45341 if (json_next_token(s)) 45342 goto fail; 45343 break; 45344 case TOK_NUMBER: 45345 val = s->token.u.num.val; 45346 if (json_next_token(s)) 45347 goto fail; 45348 break; 45349 case TOK_IDENT: 45350 if (s->token.u.ident.atom == JS_ATOM_false || 45351 s->token.u.ident.atom == JS_ATOM_true) { 45352 val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true); 45353 } else if (s->token.u.ident.atom == JS_ATOM_null) { 45354 val = JS_NULL; 45355 } else { 45356 goto def_token; 45357 } 45358 if (json_next_token(s)) 45359 goto fail; 45360 break; 45361 default: 45362 def_token: 45363 if (s->token.val == TOK_EOF) { 45364 js_parse_error(s, "Unexpected end of JSON input"); 45365 } else { 45366 js_parse_error(s, "unexpected token: '%.*s'", 45367 (int)(s->buf_ptr - s->token.ptr), s->token.ptr); 45368 } 45369 goto fail; 45370 } 45371 return val; 45372 fail: 45373 JS_FreeValue(ctx, val); 45374 return JS_EXCEPTION; 45375 } 45376 45377 JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, 45378 const char *filename, int flags) 45379 { 45380 JSParseState s1, *s = &s1; 45381 JSValue val = JS_UNDEFINED; 45382 45383 js_parse_init(ctx, s, buf, buf_len, filename); 45384 s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); 45385 if (json_next_token(s)) 45386 goto fail; 45387 val = json_parse_value(s); 45388 if (JS_IsException(val)) 45389 goto fail; 45390 if (s->token.val != TOK_EOF) { 45391 if (js_parse_error(s, "unexpected data at the end")) 45392 goto fail; 45393 } 45394 return val; 45395 fail: 45396 JS_FreeValue(ctx, val); 45397 free_token(s, &s->token); 45398 return JS_EXCEPTION; 45399 } 45400 45401 JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len, 45402 const char *filename) 45403 { 45404 return JS_ParseJSON2(ctx, buf, buf_len, filename, 0); 45405 } 45406 45407 static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder, 45408 JSAtom name, JSValueConst reviver) 45409 { 45410 JSValue val, new_el, name_val, res; 45411 JSValueConst args[2]; 45412 int ret, is_array; 45413 uint32_t i, len = 0; 45414 JSAtom prop; 45415 JSPropertyEnum *atoms = NULL; 45416 45417 if (js_check_stack_overflow(ctx->rt, 0)) { 45418 return JS_ThrowStackOverflow(ctx); 45419 } 45420 45421 val = JS_GetProperty(ctx, holder, name); 45422 if (JS_IsException(val)) 45423 return val; 45424 if (JS_IsObject(val)) { 45425 is_array = JS_IsArray(ctx, val); 45426 if (is_array < 0) 45427 goto fail; 45428 if (is_array) { 45429 if (js_get_length32(ctx, &len, val)) 45430 goto fail; 45431 } else { 45432 ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK); 45433 if (ret < 0) 45434 goto fail; 45435 } 45436 for(i = 0; i < len; i++) { 45437 if (is_array) { 45438 prop = JS_NewAtomUInt32(ctx, i); 45439 if (prop == JS_ATOM_NULL) 45440 goto fail; 45441 } else { 45442 prop = JS_DupAtom(ctx, atoms[i].atom); 45443 } 45444 new_el = internalize_json_property(ctx, val, prop, reviver); 45445 if (JS_IsException(new_el)) { 45446 JS_FreeAtom(ctx, prop); 45447 goto fail; 45448 } 45449 if (JS_IsUndefined(new_el)) { 45450 ret = JS_DeleteProperty(ctx, val, prop, 0); 45451 } else { 45452 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E); 45453 } 45454 JS_FreeAtom(ctx, prop); 45455 if (ret < 0) 45456 goto fail; 45457 } 45458 } 45459 js_free_prop_enum(ctx, atoms, len); 45460 atoms = NULL; 45461 name_val = JS_AtomToValue(ctx, name); 45462 if (JS_IsException(name_val)) 45463 goto fail; 45464 args[0] = name_val; 45465 args[1] = val; 45466 res = JS_Call(ctx, reviver, holder, 2, args); 45467 JS_FreeValue(ctx, name_val); 45468 JS_FreeValue(ctx, val); 45469 return res; 45470 fail: 45471 js_free_prop_enum(ctx, atoms, len); 45472 JS_FreeValue(ctx, val); 45473 return JS_EXCEPTION; 45474 } 45475 45476 static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val, 45477 int argc, JSValueConst *argv) 45478 { 45479 JSValue obj, root; 45480 JSValueConst reviver; 45481 const char *str; 45482 size_t len; 45483 45484 str = JS_ToCStringLen(ctx, &len, argv[0]); 45485 if (!str) 45486 return JS_EXCEPTION; 45487 obj = JS_ParseJSON(ctx, str, len, "<input>"); 45488 JS_FreeCString(ctx, str); 45489 if (JS_IsException(obj)) 45490 return obj; 45491 if (argc > 1 && JS_IsFunction(ctx, argv[1])) { 45492 reviver = argv[1]; 45493 root = JS_NewObject(ctx); 45494 if (JS_IsException(root)) { 45495 JS_FreeValue(ctx, obj); 45496 return JS_EXCEPTION; 45497 } 45498 if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj, 45499 JS_PROP_C_W_E) < 0) { 45500 JS_FreeValue(ctx, root); 45501 return JS_EXCEPTION; 45502 } 45503 obj = internalize_json_property(ctx, root, JS_ATOM_empty_string, 45504 reviver); 45505 JS_FreeValue(ctx, root); 45506 } 45507 return obj; 45508 } 45509 45510 typedef struct JSONStringifyContext { 45511 JSValueConst replacer_func; 45512 JSValue stack; 45513 JSValue property_list; 45514 JSValue gap; 45515 JSValue empty; 45516 StringBuffer *b; 45517 } JSONStringifyContext; 45518 45519 static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) { 45520 JSValue r = JS_ToQuotedString(ctx, val); 45521 JS_FreeValue(ctx, val); 45522 return r; 45523 } 45524 45525 static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, 45526 JSValueConst holder, JSValue val, JSValueConst key) 45527 { 45528 JSValue v; 45529 JSValueConst args[2]; 45530 45531 /* check for object.toJSON method */ 45532 /* ECMA specifies this is done only for Object and BigInt */ 45533 /* we do it for BigFloat and BigDecimal as an extension */ 45534 if (JS_IsObject(val) || JS_IsBigInt(ctx, val) 45535 #ifdef CONFIG_BIGNUM 45536 || JS_IsBigFloat(val) || JS_IsBigDecimal(val) 45537 #endif 45538 ) { 45539 JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); 45540 if (JS_IsException(f)) 45541 goto exception; 45542 if (JS_IsFunction(ctx, f)) { 45543 v = JS_CallFree(ctx, f, val, 1, &key); 45544 JS_FreeValue(ctx, val); 45545 val = v; 45546 if (JS_IsException(val)) 45547 goto exception; 45548 } else { 45549 JS_FreeValue(ctx, f); 45550 } 45551 } 45552 45553 if (!JS_IsUndefined(jsc->replacer_func)) { 45554 args[0] = key; 45555 args[1] = val; 45556 v = JS_Call(ctx, jsc->replacer_func, holder, 2, args); 45557 JS_FreeValue(ctx, val); 45558 val = v; 45559 if (JS_IsException(val)) 45560 goto exception; 45561 } 45562 45563 switch (JS_VALUE_GET_NORM_TAG(val)) { 45564 case JS_TAG_OBJECT: 45565 if (JS_IsFunction(ctx, val)) 45566 break; 45567 case JS_TAG_STRING: 45568 case JS_TAG_INT: 45569 case JS_TAG_FLOAT64: 45570 case JS_TAG_BOOL: 45571 case JS_TAG_NULL: 45572 case JS_TAG_BIG_INT: 45573 #ifdef CONFIG_BIGNUM 45574 case JS_TAG_BIG_FLOAT: 45575 case JS_TAG_BIG_DECIMAL: 45576 #endif 45577 case JS_TAG_EXCEPTION: 45578 return val; 45579 default: 45580 break; 45581 } 45582 JS_FreeValue(ctx, val); 45583 return JS_UNDEFINED; 45584 45585 exception: 45586 JS_FreeValue(ctx, val); 45587 return JS_EXCEPTION; 45588 } 45589 45590 static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, 45591 JSValueConst holder, JSValue val, 45592 JSValueConst indent) 45593 { 45594 JSValue indent1, sep, sep1, tab, v, prop; 45595 JSObject *p; 45596 int64_t i, len; 45597 int cl, ret; 45598 BOOL has_content; 45599 45600 indent1 = JS_UNDEFINED; 45601 sep = JS_UNDEFINED; 45602 sep1 = JS_UNDEFINED; 45603 tab = JS_UNDEFINED; 45604 prop = JS_UNDEFINED; 45605 45606 if (JS_IsObject(val)) { 45607 p = JS_VALUE_GET_OBJ(val); 45608 cl = p->class_id; 45609 if (cl == JS_CLASS_STRING) { 45610 val = JS_ToStringFree(ctx, val); 45611 if (JS_IsException(val)) 45612 goto exception; 45613 goto concat_primitive; 45614 } else if (cl == JS_CLASS_NUMBER) { 45615 val = JS_ToNumberFree(ctx, val); 45616 if (JS_IsException(val)) 45617 goto exception; 45618 goto concat_primitive; 45619 } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT 45620 #ifdef CONFIG_BIGNUM 45621 || cl == JS_CLASS_BIG_FLOAT 45622 || cl == JS_CLASS_BIG_DECIMAL 45623 #endif 45624 ) 45625 { 45626 /* This will thow the same error as for the primitive object */ 45627 set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data)); 45628 goto concat_primitive; 45629 } 45630 v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); 45631 if (JS_IsException(v)) 45632 goto exception; 45633 if (JS_ToBoolFree(ctx, v)) { 45634 JS_ThrowTypeError(ctx, "circular reference"); 45635 goto exception; 45636 } 45637 indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap)); 45638 if (JS_IsException(indent1)) 45639 goto exception; 45640 if (!JS_IsEmptyString(jsc->gap)) { 45641 sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), ""); 45642 if (JS_IsException(sep)) 45643 goto exception; 45644 sep1 = JS_NewString(ctx, " "); 45645 if (JS_IsException(sep1)) 45646 goto exception; 45647 } else { 45648 sep = JS_DupValue(ctx, jsc->empty); 45649 sep1 = JS_DupValue(ctx, jsc->empty); 45650 } 45651 v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); 45652 if (check_exception_free(ctx, v)) 45653 goto exception; 45654 ret = JS_IsArray(ctx, val); 45655 if (ret < 0) 45656 goto exception; 45657 if (ret) { 45658 if (js_get_length64(ctx, &len, val)) 45659 goto exception; 45660 string_buffer_putc8(jsc->b, '['); 45661 for(i = 0; i < len; i++) { 45662 if (i > 0) 45663 string_buffer_putc8(jsc->b, ','); 45664 string_buffer_concat_value(jsc->b, sep); 45665 v = JS_GetPropertyInt64(ctx, val, i); 45666 if (JS_IsException(v)) 45667 goto exception; 45668 /* XXX: could do this string conversion only when needed */ 45669 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i)); 45670 if (JS_IsException(prop)) 45671 goto exception; 45672 v = js_json_check(ctx, jsc, val, v, prop); 45673 JS_FreeValue(ctx, prop); 45674 prop = JS_UNDEFINED; 45675 if (JS_IsException(v)) 45676 goto exception; 45677 if (JS_IsUndefined(v)) 45678 v = JS_NULL; 45679 if (js_json_to_str(ctx, jsc, val, v, indent1)) 45680 goto exception; 45681 } 45682 if (len > 0 && !JS_IsEmptyString(jsc->gap)) { 45683 string_buffer_putc8(jsc->b, '\n'); 45684 string_buffer_concat_value(jsc->b, indent); 45685 } 45686 string_buffer_putc8(jsc->b, ']'); 45687 } else { 45688 if (!JS_IsUndefined(jsc->property_list)) 45689 tab = JS_DupValue(ctx, jsc->property_list); 45690 else 45691 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); 45692 if (JS_IsException(tab)) 45693 goto exception; 45694 if (js_get_length64(ctx, &len, tab)) 45695 goto exception; 45696 string_buffer_putc8(jsc->b, '{'); 45697 has_content = FALSE; 45698 for(i = 0; i < len; i++) { 45699 JS_FreeValue(ctx, prop); 45700 prop = JS_GetPropertyInt64(ctx, tab, i); 45701 if (JS_IsException(prop)) 45702 goto exception; 45703 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop)); 45704 if (JS_IsException(v)) 45705 goto exception; 45706 v = js_json_check(ctx, jsc, val, v, prop); 45707 if (JS_IsException(v)) 45708 goto exception; 45709 if (!JS_IsUndefined(v)) { 45710 if (has_content) 45711 string_buffer_putc8(jsc->b, ','); 45712 prop = JS_ToQuotedStringFree(ctx, prop); 45713 if (JS_IsException(prop)) { 45714 JS_FreeValue(ctx, v); 45715 goto exception; 45716 } 45717 string_buffer_concat_value(jsc->b, sep); 45718 string_buffer_concat_value(jsc->b, prop); 45719 string_buffer_putc8(jsc->b, ':'); 45720 string_buffer_concat_value(jsc->b, sep1); 45721 if (js_json_to_str(ctx, jsc, val, v, indent1)) 45722 goto exception; 45723 has_content = TRUE; 45724 } 45725 } 45726 if (has_content && !JS_IsEmptyString(jsc->gap)) { 45727 string_buffer_putc8(jsc->b, '\n'); 45728 string_buffer_concat_value(jsc->b, indent); 45729 } 45730 string_buffer_putc8(jsc->b, '}'); 45731 } 45732 if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0))) 45733 goto exception; 45734 JS_FreeValue(ctx, val); 45735 JS_FreeValue(ctx, tab); 45736 JS_FreeValue(ctx, sep); 45737 JS_FreeValue(ctx, sep1); 45738 JS_FreeValue(ctx, indent1); 45739 JS_FreeValue(ctx, prop); 45740 return 0; 45741 } 45742 concat_primitive: 45743 switch (JS_VALUE_GET_NORM_TAG(val)) { 45744 case JS_TAG_STRING: 45745 val = JS_ToQuotedStringFree(ctx, val); 45746 if (JS_IsException(val)) 45747 goto exception; 45748 goto concat_value; 45749 case JS_TAG_FLOAT64: 45750 if (!isfinite(JS_VALUE_GET_FLOAT64(val))) { 45751 val = JS_NULL; 45752 } 45753 goto concat_value; 45754 case JS_TAG_INT: 45755 case JS_TAG_BOOL: 45756 case JS_TAG_NULL: 45757 concat_value: 45758 return string_buffer_concat_value_free(jsc->b, val); 45759 case JS_TAG_BIG_INT: 45760 #ifdef CONFIG_BIGNUM 45761 case JS_TAG_BIG_FLOAT: 45762 case JS_TAG_BIG_DECIMAL: 45763 #endif 45764 /* reject big numbers: use toJSON method to override */ 45765 JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt"); 45766 goto exception; 45767 default: 45768 JS_FreeValue(ctx, val); 45769 return 0; 45770 } 45771 45772 exception: 45773 JS_FreeValue(ctx, val); 45774 JS_FreeValue(ctx, tab); 45775 JS_FreeValue(ctx, sep); 45776 JS_FreeValue(ctx, sep1); 45777 JS_FreeValue(ctx, indent1); 45778 JS_FreeValue(ctx, prop); 45779 return -1; 45780 } 45781 45782 JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, 45783 JSValueConst replacer, JSValueConst space0) 45784 { 45785 StringBuffer b_s; 45786 JSONStringifyContext jsc_s, *jsc = &jsc_s; 45787 JSValue val, v, space, ret, wrapper; 45788 int res; 45789 int64_t i, j, n; 45790 45791 jsc->replacer_func = JS_UNDEFINED; 45792 jsc->stack = JS_UNDEFINED; 45793 jsc->property_list = JS_UNDEFINED; 45794 jsc->gap = JS_UNDEFINED; 45795 jsc->b = &b_s; 45796 jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string); 45797 ret = JS_UNDEFINED; 45798 wrapper = JS_UNDEFINED; 45799 45800 string_buffer_init(ctx, jsc->b, 0); 45801 jsc->stack = JS_NewArray(ctx); 45802 if (JS_IsException(jsc->stack)) 45803 goto exception; 45804 if (JS_IsFunction(ctx, replacer)) { 45805 jsc->replacer_func = replacer; 45806 } else { 45807 res = JS_IsArray(ctx, replacer); 45808 if (res < 0) 45809 goto exception; 45810 if (res) { 45811 /* XXX: enumeration is not fully correct */ 45812 jsc->property_list = JS_NewArray(ctx); 45813 if (JS_IsException(jsc->property_list)) 45814 goto exception; 45815 if (js_get_length64(ctx, &n, replacer)) 45816 goto exception; 45817 for (i = j = 0; i < n; i++) { 45818 JSValue present; 45819 v = JS_GetPropertyInt64(ctx, replacer, i); 45820 if (JS_IsException(v)) 45821 goto exception; 45822 if (JS_IsObject(v)) { 45823 JSObject *p = JS_VALUE_GET_OBJ(v); 45824 if (p->class_id == JS_CLASS_STRING || 45825 p->class_id == JS_CLASS_NUMBER) { 45826 v = JS_ToStringFree(ctx, v); 45827 if (JS_IsException(v)) 45828 goto exception; 45829 } else { 45830 JS_FreeValue(ctx, v); 45831 continue; 45832 } 45833 } else if (JS_IsNumber(v)) { 45834 v = JS_ToStringFree(ctx, v); 45835 if (JS_IsException(v)) 45836 goto exception; 45837 } else if (!JS_IsString(v)) { 45838 JS_FreeValue(ctx, v); 45839 continue; 45840 } 45841 present = js_array_includes(ctx, jsc->property_list, 45842 1, (JSValueConst *)&v); 45843 if (JS_IsException(present)) { 45844 JS_FreeValue(ctx, v); 45845 goto exception; 45846 } 45847 if (!JS_ToBoolFree(ctx, present)) { 45848 JS_SetPropertyInt64(ctx, jsc->property_list, j++, v); 45849 } else { 45850 JS_FreeValue(ctx, v); 45851 } 45852 } 45853 } 45854 } 45855 space = JS_DupValue(ctx, space0); 45856 if (JS_IsObject(space)) { 45857 JSObject *p = JS_VALUE_GET_OBJ(space); 45858 if (p->class_id == JS_CLASS_NUMBER) { 45859 space = JS_ToNumberFree(ctx, space); 45860 } else if (p->class_id == JS_CLASS_STRING) { 45861 space = JS_ToStringFree(ctx, space); 45862 } 45863 if (JS_IsException(space)) { 45864 JS_FreeValue(ctx, space); 45865 goto exception; 45866 } 45867 } 45868 if (JS_IsNumber(space)) { 45869 int n; 45870 if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0)) 45871 goto exception; 45872 jsc->gap = JS_NewStringLen(ctx, " ", n); 45873 } else if (JS_IsString(space)) { 45874 JSString *p = JS_VALUE_GET_STRING(space); 45875 jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10)); 45876 } else { 45877 jsc->gap = JS_DupValue(ctx, jsc->empty); 45878 } 45879 JS_FreeValue(ctx, space); 45880 if (JS_IsException(jsc->gap)) 45881 goto exception; 45882 wrapper = JS_NewObject(ctx); 45883 if (JS_IsException(wrapper)) 45884 goto exception; 45885 if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string, 45886 JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0) 45887 goto exception; 45888 val = JS_DupValue(ctx, obj); 45889 45890 val = js_json_check(ctx, jsc, wrapper, val, jsc->empty); 45891 if (JS_IsException(val)) 45892 goto exception; 45893 if (JS_IsUndefined(val)) { 45894 ret = JS_UNDEFINED; 45895 goto done1; 45896 } 45897 if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty)) 45898 goto exception; 45899 45900 ret = string_buffer_end(jsc->b); 45901 goto done; 45902 45903 exception: 45904 ret = JS_EXCEPTION; 45905 done1: 45906 string_buffer_free(jsc->b); 45907 done: 45908 JS_FreeValue(ctx, wrapper); 45909 JS_FreeValue(ctx, jsc->empty); 45910 JS_FreeValue(ctx, jsc->gap); 45911 JS_FreeValue(ctx, jsc->property_list); 45912 JS_FreeValue(ctx, jsc->stack); 45913 return ret; 45914 } 45915 45916 static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val, 45917 int argc, JSValueConst *argv) 45918 { 45919 // stringify(val, replacer, space) 45920 return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]); 45921 } 45922 45923 static const JSCFunctionListEntry js_json_funcs[] = { 45924 JS_CFUNC_DEF("parse", 2, js_json_parse ), 45925 JS_CFUNC_DEF("stringify", 3, js_json_stringify ), 45926 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ), 45927 }; 45928 45929 static const JSCFunctionListEntry js_json_obj[] = { 45930 JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 45931 }; 45932 45933 void JS_AddIntrinsicJSON(JSContext *ctx) 45934 { 45935 /* add JSON as autoinit object */ 45936 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj)); 45937 } 45938 45939 /* Reflect */ 45940 45941 static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val, 45942 int argc, JSValueConst *argv) 45943 { 45944 return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2); 45945 } 45946 45947 static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, 45948 int argc, JSValueConst *argv) 45949 { 45950 JSValueConst func, array_arg, new_target; 45951 JSValue *tab, ret; 45952 uint32_t len; 45953 45954 func = argv[0]; 45955 array_arg = argv[1]; 45956 if (argc > 2) { 45957 new_target = argv[2]; 45958 if (!JS_IsConstructor(ctx, new_target)) 45959 return JS_ThrowTypeError(ctx, "not a constructor"); 45960 } else { 45961 new_target = func; 45962 } 45963 tab = build_arg_list(ctx, &len, array_arg); 45964 if (!tab) 45965 return JS_EXCEPTION; 45966 ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); 45967 free_arg_list(ctx, tab, len); 45968 return ret; 45969 } 45970 45971 static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val, 45972 int argc, JSValueConst *argv) 45973 { 45974 JSValueConst obj; 45975 JSAtom atom; 45976 int ret; 45977 45978 obj = argv[0]; 45979 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 45980 return JS_ThrowTypeErrorNotAnObject(ctx); 45981 atom = JS_ValueToAtom(ctx, argv[1]); 45982 if (unlikely(atom == JS_ATOM_NULL)) 45983 return JS_EXCEPTION; 45984 ret = JS_DeleteProperty(ctx, obj, atom, 0); 45985 JS_FreeAtom(ctx, atom); 45986 if (ret < 0) 45987 return JS_EXCEPTION; 45988 else 45989 return JS_NewBool(ctx, ret); 45990 } 45991 45992 static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val, 45993 int argc, JSValueConst *argv) 45994 { 45995 JSValueConst obj, prop, receiver; 45996 JSAtom atom; 45997 JSValue ret; 45998 45999 obj = argv[0]; 46000 prop = argv[1]; 46001 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 46002 return JS_ThrowTypeErrorNotAnObject(ctx); 46003 if (argc > 2) 46004 receiver = argv[2]; 46005 else 46006 receiver = obj; 46007 atom = JS_ValueToAtom(ctx, prop); 46008 if (unlikely(atom == JS_ATOM_NULL)) 46009 return JS_EXCEPTION; 46010 ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE); 46011 JS_FreeAtom(ctx, atom); 46012 return ret; 46013 } 46014 46015 static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val, 46016 int argc, JSValueConst *argv) 46017 { 46018 JSValueConst obj, prop; 46019 JSAtom atom; 46020 int ret; 46021 46022 obj = argv[0]; 46023 prop = argv[1]; 46024 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 46025 return JS_ThrowTypeErrorNotAnObject(ctx); 46026 atom = JS_ValueToAtom(ctx, prop); 46027 if (unlikely(atom == JS_ATOM_NULL)) 46028 return JS_EXCEPTION; 46029 ret = JS_HasProperty(ctx, obj, atom); 46030 JS_FreeAtom(ctx, atom); 46031 if (ret < 0) 46032 return JS_EXCEPTION; 46033 else 46034 return JS_NewBool(ctx, ret); 46035 } 46036 46037 static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, 46038 int argc, JSValueConst *argv) 46039 { 46040 JSValueConst obj, prop, val, receiver; 46041 int ret; 46042 JSAtom atom; 46043 46044 obj = argv[0]; 46045 prop = argv[1]; 46046 val = argv[2]; 46047 if (argc > 3) 46048 receiver = argv[3]; 46049 else 46050 receiver = obj; 46051 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 46052 return JS_ThrowTypeErrorNotAnObject(ctx); 46053 atom = JS_ValueToAtom(ctx, prop); 46054 if (unlikely(atom == JS_ATOM_NULL)) 46055 return JS_EXCEPTION; 46056 ret = JS_SetPropertyInternal(ctx, obj, atom, 46057 JS_DupValue(ctx, val), receiver, 0); 46058 JS_FreeAtom(ctx, atom); 46059 if (ret < 0) 46060 return JS_EXCEPTION; 46061 else 46062 return JS_NewBool(ctx, ret); 46063 } 46064 46065 static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val, 46066 int argc, JSValueConst *argv) 46067 { 46068 int ret; 46069 ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE); 46070 if (ret < 0) 46071 return JS_EXCEPTION; 46072 else 46073 return JS_NewBool(ctx, ret); 46074 } 46075 46076 static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val, 46077 int argc, JSValueConst *argv) 46078 { 46079 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) 46080 return JS_ThrowTypeErrorNotAnObject(ctx); 46081 return JS_GetOwnPropertyNames2(ctx, argv[0], 46082 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK, 46083 JS_ITERATOR_KIND_KEY); 46084 } 46085 46086 static const JSCFunctionListEntry js_reflect_funcs[] = { 46087 JS_CFUNC_DEF("apply", 3, js_reflect_apply ), 46088 JS_CFUNC_DEF("construct", 2, js_reflect_construct ), 46089 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ), 46090 JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ), 46091 JS_CFUNC_DEF("get", 2, js_reflect_get ), 46092 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ), 46093 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ), 46094 JS_CFUNC_DEF("has", 2, js_reflect_has ), 46095 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ), 46096 JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ), 46097 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ), 46098 JS_CFUNC_DEF("set", 3, js_reflect_set ), 46099 JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ), 46100 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ), 46101 }; 46102 46103 static const JSCFunctionListEntry js_reflect_obj[] = { 46104 JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 46105 }; 46106 46107 /* Proxy */ 46108 46109 static void js_proxy_finalizer(JSRuntime *rt, JSValue val) 46110 { 46111 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); 46112 if (s) { 46113 JS_FreeValueRT(rt, s->target); 46114 JS_FreeValueRT(rt, s->handler); 46115 js_free_rt(rt, s); 46116 } 46117 } 46118 46119 static void js_proxy_mark(JSRuntime *rt, JSValueConst val, 46120 JS_MarkFunc *mark_func) 46121 { 46122 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY); 46123 if (s) { 46124 JS_MarkValue(rt, s->target, mark_func); 46125 JS_MarkValue(rt, s->handler, mark_func); 46126 } 46127 } 46128 46129 static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx) 46130 { 46131 return JS_ThrowTypeError(ctx, "revoked proxy"); 46132 } 46133 46134 static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod, 46135 JSValueConst obj, JSAtom name) 46136 { 46137 JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); 46138 JSValue method; 46139 46140 /* safer to test recursion in all proxy methods */ 46141 if (js_check_stack_overflow(ctx->rt, 0)) { 46142 JS_ThrowStackOverflow(ctx); 46143 return NULL; 46144 } 46145 46146 /* 's' should never be NULL */ 46147 if (s->is_revoked) { 46148 JS_ThrowTypeErrorRevokedProxy(ctx); 46149 return NULL; 46150 } 46151 method = JS_GetProperty(ctx, s->handler, name); 46152 if (JS_IsException(method)) 46153 return NULL; 46154 if (JS_IsNull(method)) 46155 method = JS_UNDEFINED; 46156 *pmethod = method; 46157 return s; 46158 } 46159 46160 static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) 46161 { 46162 JSProxyData *s; 46163 JSValue method, ret, proto1; 46164 int res; 46165 46166 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf); 46167 if (!s) 46168 return JS_EXCEPTION; 46169 if (JS_IsUndefined(method)) 46170 return JS_GetPrototype(ctx, s->target); 46171 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 46172 if (JS_IsException(ret)) 46173 return ret; 46174 if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && 46175 JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 46176 goto fail; 46177 } 46178 res = JS_IsExtensible(ctx, s->target); 46179 if (res < 0) { 46180 JS_FreeValue(ctx, ret); 46181 return JS_EXCEPTION; 46182 } 46183 if (!res) { 46184 /* check invariant */ 46185 proto1 = JS_GetPrototype(ctx, s->target); 46186 if (JS_IsException(proto1)) { 46187 JS_FreeValue(ctx, ret); 46188 return JS_EXCEPTION; 46189 } 46190 if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) { 46191 JS_FreeValue(ctx, proto1); 46192 fail: 46193 JS_FreeValue(ctx, ret); 46194 return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); 46195 } 46196 JS_FreeValue(ctx, proto1); 46197 } 46198 return ret; 46199 } 46200 46201 static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, 46202 JSValueConst proto_val, BOOL throw_flag) 46203 { 46204 JSProxyData *s; 46205 JSValue method, ret, proto1; 46206 JSValueConst args[2]; 46207 BOOL res; 46208 int res2; 46209 46210 s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf); 46211 if (!s) 46212 return -1; 46213 if (JS_IsUndefined(method)) 46214 return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag); 46215 args[0] = s->target; 46216 args[1] = proto_val; 46217 ret = JS_CallFree(ctx, method, s->handler, 2, args); 46218 if (JS_IsException(ret)) 46219 return -1; 46220 res = JS_ToBoolFree(ctx, ret); 46221 if (!res) { 46222 if (throw_flag) { 46223 JS_ThrowTypeError(ctx, "proxy: bad prototype"); 46224 return -1; 46225 } else { 46226 return FALSE; 46227 } 46228 } 46229 res2 = JS_IsExtensible(ctx, s->target); 46230 if (res2 < 0) 46231 return -1; 46232 if (!res2) { 46233 proto1 = JS_GetPrototype(ctx, s->target); 46234 if (JS_IsException(proto1)) 46235 return -1; 46236 if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) { 46237 JS_FreeValue(ctx, proto1); 46238 JS_ThrowTypeError(ctx, "proxy: inconsistent prototype"); 46239 return -1; 46240 } 46241 JS_FreeValue(ctx, proto1); 46242 } 46243 return TRUE; 46244 } 46245 46246 static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) 46247 { 46248 JSProxyData *s; 46249 JSValue method, ret; 46250 BOOL res; 46251 int res2; 46252 46253 s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible); 46254 if (!s) 46255 return -1; 46256 if (JS_IsUndefined(method)) 46257 return JS_IsExtensible(ctx, s->target); 46258 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 46259 if (JS_IsException(ret)) 46260 return -1; 46261 res = JS_ToBoolFree(ctx, ret); 46262 res2 = JS_IsExtensible(ctx, s->target); 46263 if (res2 < 0) 46264 return res2; 46265 if (res != res2) { 46266 JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible"); 46267 return -1; 46268 } 46269 return res; 46270 } 46271 46272 static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) 46273 { 46274 JSProxyData *s; 46275 JSValue method, ret; 46276 BOOL res; 46277 int res2; 46278 46279 s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions); 46280 if (!s) 46281 return -1; 46282 if (JS_IsUndefined(method)) 46283 return JS_PreventExtensions(ctx, s->target); 46284 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 46285 if (JS_IsException(ret)) 46286 return -1; 46287 res = JS_ToBoolFree(ctx, ret); 46288 if (res) { 46289 res2 = JS_IsExtensible(ctx, s->target); 46290 if (res2 < 0) 46291 return res2; 46292 if (res2) { 46293 JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions"); 46294 return -1; 46295 } 46296 } 46297 return res; 46298 } 46299 46300 static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom) 46301 { 46302 JSProxyData *s; 46303 JSValue method, ret1, atom_val; 46304 int ret, res; 46305 JSObject *p; 46306 JSValueConst args[2]; 46307 BOOL res2; 46308 46309 s = get_proxy_method(ctx, &method, obj, JS_ATOM_has); 46310 if (!s) 46311 return -1; 46312 if (JS_IsUndefined(method)) 46313 return JS_HasProperty(ctx, s->target, atom); 46314 atom_val = JS_AtomToValue(ctx, atom); 46315 if (JS_IsException(atom_val)) { 46316 JS_FreeValue(ctx, method); 46317 return -1; 46318 } 46319 args[0] = s->target; 46320 args[1] = atom_val; 46321 ret1 = JS_CallFree(ctx, method, s->handler, 2, args); 46322 JS_FreeValue(ctx, atom_val); 46323 if (JS_IsException(ret1)) 46324 return -1; 46325 ret = JS_ToBoolFree(ctx, ret1); 46326 if (!ret) { 46327 JSPropertyDescriptor desc; 46328 p = JS_VALUE_GET_OBJ(s->target); 46329 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); 46330 if (res < 0) 46331 return -1; 46332 if (res) { 46333 res2 = !(desc.flags & JS_PROP_CONFIGURABLE); 46334 js_free_desc(ctx, &desc); 46335 if (res2 || !p->extensible) { 46336 JS_ThrowTypeError(ctx, "proxy: inconsistent has"); 46337 return -1; 46338 } 46339 } 46340 } 46341 return ret; 46342 } 46343 46344 static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom, 46345 JSValueConst receiver) 46346 { 46347 JSProxyData *s; 46348 JSValue method, ret, atom_val; 46349 int res; 46350 JSValueConst args[3]; 46351 JSPropertyDescriptor desc; 46352 46353 s = get_proxy_method(ctx, &method, obj, JS_ATOM_get); 46354 if (!s) 46355 return JS_EXCEPTION; 46356 /* Note: recursion is possible thru the prototype of s->target */ 46357 if (JS_IsUndefined(method)) 46358 return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE); 46359 atom_val = JS_AtomToValue(ctx, atom); 46360 if (JS_IsException(atom_val)) { 46361 JS_FreeValue(ctx, method); 46362 return JS_EXCEPTION; 46363 } 46364 args[0] = s->target; 46365 args[1] = atom_val; 46366 args[2] = receiver; 46367 ret = JS_CallFree(ctx, method, s->handler, 3, args); 46368 JS_FreeValue(ctx, atom_val); 46369 if (JS_IsException(ret)) 46370 return JS_EXCEPTION; 46371 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 46372 if (res < 0) { 46373 JS_FreeValue(ctx, ret); 46374 return JS_EXCEPTION; 46375 } 46376 if (res) { 46377 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { 46378 if (!js_same_value(ctx, desc.value, ret)) { 46379 goto fail; 46380 } 46381 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) { 46382 if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) { 46383 fail: 46384 js_free_desc(ctx, &desc); 46385 JS_FreeValue(ctx, ret); 46386 return JS_ThrowTypeError(ctx, "proxy: inconsistent get"); 46387 } 46388 } 46389 js_free_desc(ctx, &desc); 46390 } 46391 return ret; 46392 } 46393 46394 static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, 46395 JSValueConst value, JSValueConst receiver, int flags) 46396 { 46397 JSProxyData *s; 46398 JSValue method, ret1, atom_val; 46399 int ret, res; 46400 JSValueConst args[4]; 46401 46402 s = get_proxy_method(ctx, &method, obj, JS_ATOM_set); 46403 if (!s) 46404 return -1; 46405 if (JS_IsUndefined(method)) { 46406 return JS_SetPropertyInternal(ctx, s->target, atom, 46407 JS_DupValue(ctx, value), receiver, 46408 flags); 46409 } 46410 atom_val = JS_AtomToValue(ctx, atom); 46411 if (JS_IsException(atom_val)) { 46412 JS_FreeValue(ctx, method); 46413 return -1; 46414 } 46415 args[0] = s->target; 46416 args[1] = atom_val; 46417 args[2] = value; 46418 args[3] = receiver; 46419 ret1 = JS_CallFree(ctx, method, s->handler, 4, args); 46420 JS_FreeValue(ctx, atom_val); 46421 if (JS_IsException(ret1)) 46422 return -1; 46423 ret = JS_ToBoolFree(ctx, ret1); 46424 if (ret) { 46425 JSPropertyDescriptor desc; 46426 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 46427 if (res < 0) 46428 return -1; 46429 if (res) { 46430 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) { 46431 if (!js_same_value(ctx, desc.value, value)) { 46432 goto fail; 46433 } 46434 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) { 46435 fail: 46436 js_free_desc(ctx, &desc); 46437 JS_ThrowTypeError(ctx, "proxy: inconsistent set"); 46438 return -1; 46439 } 46440 js_free_desc(ctx, &desc); 46441 } 46442 } else { 46443 if ((flags & JS_PROP_THROW) || 46444 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { 46445 JS_ThrowTypeError(ctx, "proxy: cannot set property"); 46446 return -1; 46447 } 46448 } 46449 return ret; 46450 } 46451 46452 static JSValue js_create_desc(JSContext *ctx, JSValueConst val, 46453 JSValueConst getter, JSValueConst setter, 46454 int flags) 46455 { 46456 JSValue ret; 46457 ret = JS_NewObject(ctx); 46458 if (JS_IsException(ret)) 46459 return ret; 46460 if (flags & JS_PROP_HAS_GET) { 46461 JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter), 46462 JS_PROP_C_W_E); 46463 } 46464 if (flags & JS_PROP_HAS_SET) { 46465 JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter), 46466 JS_PROP_C_W_E); 46467 } 46468 if (flags & JS_PROP_HAS_VALUE) { 46469 JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val), 46470 JS_PROP_C_W_E); 46471 } 46472 if (flags & JS_PROP_HAS_WRITABLE) { 46473 JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, 46474 JS_NewBool(ctx, flags & JS_PROP_WRITABLE), 46475 JS_PROP_C_W_E); 46476 } 46477 if (flags & JS_PROP_HAS_ENUMERABLE) { 46478 JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, 46479 JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE), 46480 JS_PROP_C_W_E); 46481 } 46482 if (flags & JS_PROP_HAS_CONFIGURABLE) { 46483 JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, 46484 JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE), 46485 JS_PROP_C_W_E); 46486 } 46487 return ret; 46488 } 46489 46490 static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc, 46491 JSValueConst obj, JSAtom prop) 46492 { 46493 JSProxyData *s; 46494 JSValue method, trap_result_obj, prop_val; 46495 int res, target_desc_ret, ret; 46496 JSObject *p; 46497 JSValueConst args[2]; 46498 JSPropertyDescriptor result_desc, target_desc; 46499 46500 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor); 46501 if (!s) 46502 return -1; 46503 p = JS_VALUE_GET_OBJ(s->target); 46504 if (JS_IsUndefined(method)) { 46505 return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop); 46506 } 46507 prop_val = JS_AtomToValue(ctx, prop); 46508 if (JS_IsException(prop_val)) { 46509 JS_FreeValue(ctx, method); 46510 return -1; 46511 } 46512 args[0] = s->target; 46513 args[1] = prop_val; 46514 trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args); 46515 JS_FreeValue(ctx, prop_val); 46516 if (JS_IsException(trap_result_obj)) 46517 return -1; 46518 if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) { 46519 JS_FreeValue(ctx, trap_result_obj); 46520 goto fail; 46521 } 46522 target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop); 46523 if (target_desc_ret < 0) { 46524 JS_FreeValue(ctx, trap_result_obj); 46525 return -1; 46526 } 46527 if (target_desc_ret) 46528 js_free_desc(ctx, &target_desc); 46529 if (JS_IsUndefined(trap_result_obj)) { 46530 if (target_desc_ret) { 46531 if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible) 46532 goto fail; 46533 } 46534 ret = FALSE; 46535 } else { 46536 int flags1, extensible_target; 46537 extensible_target = JS_IsExtensible(ctx, s->target); 46538 if (extensible_target < 0) { 46539 JS_FreeValue(ctx, trap_result_obj); 46540 return -1; 46541 } 46542 res = js_obj_to_desc(ctx, &result_desc, trap_result_obj); 46543 JS_FreeValue(ctx, trap_result_obj); 46544 if (res < 0) 46545 return -1; 46546 46547 if (target_desc_ret) { 46548 /* convert result_desc.flags to defineProperty flags */ 46549 flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE; 46550 if (result_desc.flags & JS_PROP_GETSET) 46551 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET; 46552 else 46553 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE; 46554 /* XXX: not complete check: need to compare value & 46555 getter/setter as in defineproperty */ 46556 if (!check_define_prop_flags(target_desc.flags, flags1)) 46557 goto fail1; 46558 } else { 46559 if (!extensible_target) 46560 goto fail1; 46561 } 46562 if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) { 46563 if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE)) 46564 goto fail1; 46565 if ((result_desc.flags & 46566 (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 && 46567 target_desc_ret && 46568 (target_desc.flags & JS_PROP_WRITABLE) != 0) { 46569 /* proxy-missing-checks */ 46570 fail1: 46571 js_free_desc(ctx, &result_desc); 46572 fail: 46573 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor"); 46574 return -1; 46575 } 46576 } 46577 ret = TRUE; 46578 if (pdesc) { 46579 *pdesc = result_desc; 46580 } else { 46581 js_free_desc(ctx, &result_desc); 46582 } 46583 } 46584 return ret; 46585 } 46586 46587 static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj, 46588 JSAtom prop, JSValueConst val, 46589 JSValueConst getter, JSValueConst setter, 46590 int flags) 46591 { 46592 JSProxyData *s; 46593 JSValue method, ret1, prop_val, desc_val; 46594 int res, ret; 46595 JSObject *p; 46596 JSValueConst args[3]; 46597 JSPropertyDescriptor desc; 46598 BOOL setting_not_configurable; 46599 46600 s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty); 46601 if (!s) 46602 return -1; 46603 if (JS_IsUndefined(method)) { 46604 return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags); 46605 } 46606 prop_val = JS_AtomToValue(ctx, prop); 46607 if (JS_IsException(prop_val)) { 46608 JS_FreeValue(ctx, method); 46609 return -1; 46610 } 46611 desc_val = js_create_desc(ctx, val, getter, setter, flags); 46612 if (JS_IsException(desc_val)) { 46613 JS_FreeValue(ctx, prop_val); 46614 JS_FreeValue(ctx, method); 46615 return -1; 46616 } 46617 args[0] = s->target; 46618 args[1] = prop_val; 46619 args[2] = desc_val; 46620 ret1 = JS_CallFree(ctx, method, s->handler, 3, args); 46621 JS_FreeValue(ctx, prop_val); 46622 JS_FreeValue(ctx, desc_val); 46623 if (JS_IsException(ret1)) 46624 return -1; 46625 ret = JS_ToBoolFree(ctx, ret1); 46626 if (!ret) { 46627 if (flags & JS_PROP_THROW) { 46628 JS_ThrowTypeError(ctx, "proxy: defineProperty exception"); 46629 return -1; 46630 } else { 46631 return 0; 46632 } 46633 } 46634 p = JS_VALUE_GET_OBJ(s->target); 46635 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); 46636 if (res < 0) 46637 return -1; 46638 setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE | 46639 JS_PROP_CONFIGURABLE)) == 46640 JS_PROP_HAS_CONFIGURABLE); 46641 if (!res) { 46642 if (!p->extensible || setting_not_configurable) 46643 goto fail; 46644 } else { 46645 if (!check_define_prop_flags(desc.flags, flags) || 46646 ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) { 46647 goto fail1; 46648 } 46649 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { 46650 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == 46651 JS_PROP_GETSET) { 46652 if ((flags & JS_PROP_HAS_GET) && 46653 !js_same_value(ctx, getter, desc.getter)) { 46654 goto fail1; 46655 } 46656 if ((flags & JS_PROP_HAS_SET) && 46657 !js_same_value(ctx, setter, desc.setter)) { 46658 goto fail1; 46659 } 46660 } 46661 } else if (flags & JS_PROP_HAS_VALUE) { 46662 if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 46663 JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) { 46664 /* missing-proxy-check feature */ 46665 goto fail1; 46666 } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && 46667 !js_same_value(ctx, val, desc.value)) { 46668 goto fail1; 46669 } 46670 } 46671 if (flags & JS_PROP_HAS_WRITABLE) { 46672 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | 46673 JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) { 46674 /* proxy-missing-checks */ 46675 fail1: 46676 js_free_desc(ctx, &desc); 46677 fail: 46678 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty"); 46679 return -1; 46680 } 46681 } 46682 js_free_desc(ctx, &desc); 46683 } 46684 return 1; 46685 } 46686 46687 static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj, 46688 JSAtom atom) 46689 { 46690 JSProxyData *s; 46691 JSValue method, ret, atom_val; 46692 int res, res2, is_extensible; 46693 JSValueConst args[2]; 46694 46695 s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty); 46696 if (!s) 46697 return -1; 46698 if (JS_IsUndefined(method)) { 46699 return JS_DeleteProperty(ctx, s->target, atom, 0); 46700 } 46701 atom_val = JS_AtomToValue(ctx, atom);; 46702 if (JS_IsException(atom_val)) { 46703 JS_FreeValue(ctx, method); 46704 return -1; 46705 } 46706 args[0] = s->target; 46707 args[1] = atom_val; 46708 ret = JS_CallFree(ctx, method, s->handler, 2, args); 46709 JS_FreeValue(ctx, atom_val); 46710 if (JS_IsException(ret)) 46711 return -1; 46712 res = JS_ToBoolFree(ctx, ret); 46713 if (res) { 46714 JSPropertyDescriptor desc; 46715 res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom); 46716 if (res2 < 0) 46717 return -1; 46718 if (res2) { 46719 if (!(desc.flags & JS_PROP_CONFIGURABLE)) 46720 goto fail; 46721 is_extensible = JS_IsExtensible(ctx, s->target); 46722 if (is_extensible < 0) 46723 goto fail1; 46724 if (!is_extensible) { 46725 /* proxy-missing-checks */ 46726 fail: 46727 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty"); 46728 fail1: 46729 js_free_desc(ctx, &desc); 46730 return -1; 46731 } 46732 js_free_desc(ctx, &desc); 46733 } 46734 } 46735 return res; 46736 } 46737 46738 /* return the index of the property or -1 if not found */ 46739 static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom) 46740 { 46741 int i; 46742 for(i = 0; i < n; i++) { 46743 if (tab[i].atom == atom) 46744 return i; 46745 } 46746 return -1; 46747 } 46748 46749 static int js_proxy_get_own_property_names(JSContext *ctx, 46750 JSPropertyEnum **ptab, 46751 uint32_t *plen, 46752 JSValueConst obj) 46753 { 46754 JSProxyData *s; 46755 JSValue method, prop_array, val; 46756 uint32_t len, i, len2; 46757 JSPropertyEnum *tab, *tab2; 46758 JSAtom atom; 46759 JSPropertyDescriptor desc; 46760 int res, is_extensible, idx; 46761 46762 s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys); 46763 if (!s) 46764 return -1; 46765 if (JS_IsUndefined(method)) { 46766 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, 46767 JS_VALUE_GET_OBJ(s->target), 46768 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); 46769 } 46770 prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); 46771 if (JS_IsException(prop_array)) 46772 return -1; 46773 tab = NULL; 46774 len = 0; 46775 tab2 = NULL; 46776 len2 = 0; 46777 if (js_get_length32(ctx, &len, prop_array)) 46778 goto fail; 46779 if (len > 0) { 46780 tab = js_mallocz(ctx, sizeof(tab[0]) * len); 46781 if (!tab) 46782 goto fail; 46783 } 46784 for(i = 0; i < len; i++) { 46785 val = JS_GetPropertyUint32(ctx, prop_array, i); 46786 if (JS_IsException(val)) 46787 goto fail; 46788 if (!JS_IsString(val) && !JS_IsSymbol(val)) { 46789 JS_FreeValue(ctx, val); 46790 JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols"); 46791 goto fail; 46792 } 46793 atom = JS_ValueToAtom(ctx, val); 46794 JS_FreeValue(ctx, val); 46795 if (atom == JS_ATOM_NULL) 46796 goto fail; 46797 tab[i].atom = atom; 46798 tab[i].is_enumerable = FALSE; /* XXX: redundant? */ 46799 } 46800 46801 /* check duplicate properties (XXX: inefficient, could store the 46802 * properties an a temporary object to use the hash) */ 46803 for(i = 1; i < len; i++) { 46804 if (find_prop_key(tab, i, tab[i].atom) >= 0) { 46805 JS_ThrowTypeError(ctx, "proxy: duplicate property"); 46806 goto fail; 46807 } 46808 } 46809 46810 is_extensible = JS_IsExtensible(ctx, s->target); 46811 if (is_extensible < 0) 46812 goto fail; 46813 46814 /* check if there are non configurable properties */ 46815 if (s->is_revoked) { 46816 JS_ThrowTypeErrorRevokedProxy(ctx); 46817 goto fail; 46818 } 46819 if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target), 46820 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) 46821 goto fail; 46822 for(i = 0; i < len2; i++) { 46823 if (s->is_revoked) { 46824 JS_ThrowTypeErrorRevokedProxy(ctx); 46825 goto fail; 46826 } 46827 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), 46828 tab2[i].atom); 46829 if (res < 0) 46830 goto fail; 46831 if (res) { /* safety, property should be found */ 46832 js_free_desc(ctx, &desc); 46833 if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) { 46834 idx = find_prop_key(tab, len, tab2[i].atom); 46835 if (idx < 0) { 46836 JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys"); 46837 goto fail; 46838 } 46839 /* mark the property as found */ 46840 if (!is_extensible) 46841 tab[idx].is_enumerable = TRUE; 46842 } 46843 } 46844 } 46845 if (!is_extensible) { 46846 /* check that all property in 'tab' were checked */ 46847 for(i = 0; i < len; i++) { 46848 if (!tab[i].is_enumerable) { 46849 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy"); 46850 goto fail; 46851 } 46852 } 46853 } 46854 46855 js_free_prop_enum(ctx, tab2, len2); 46856 JS_FreeValue(ctx, prop_array); 46857 *ptab = tab; 46858 *plen = len; 46859 return 0; 46860 fail: 46861 js_free_prop_enum(ctx, tab2, len2); 46862 js_free_prop_enum(ctx, tab, len); 46863 JS_FreeValue(ctx, prop_array); 46864 return -1; 46865 } 46866 46867 static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj, 46868 JSValueConst new_target, 46869 int argc, JSValueConst *argv) 46870 { 46871 JSProxyData *s; 46872 JSValue method, arg_array, ret; 46873 JSValueConst args[3]; 46874 46875 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct); 46876 if (!s) 46877 return JS_EXCEPTION; 46878 if (!JS_IsConstructor(ctx, s->target)) 46879 return JS_ThrowTypeError(ctx, "not a constructor"); 46880 if (JS_IsUndefined(method)) 46881 return JS_CallConstructor2(ctx, s->target, new_target, argc, argv); 46882 arg_array = js_create_array(ctx, argc, argv); 46883 if (JS_IsException(arg_array)) { 46884 ret = JS_EXCEPTION; 46885 goto fail; 46886 } 46887 args[0] = s->target; 46888 args[1] = arg_array; 46889 args[2] = new_target; 46890 ret = JS_Call(ctx, method, s->handler, 3, args); 46891 if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { 46892 JS_FreeValue(ctx, ret); 46893 ret = JS_ThrowTypeErrorNotAnObject(ctx); 46894 } 46895 fail: 46896 JS_FreeValue(ctx, method); 46897 JS_FreeValue(ctx, arg_array); 46898 return ret; 46899 } 46900 46901 static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, 46902 JSValueConst this_obj, 46903 int argc, JSValueConst *argv, int flags) 46904 { 46905 JSProxyData *s; 46906 JSValue method, arg_array, ret; 46907 JSValueConst args[3]; 46908 46909 if (flags & JS_CALL_FLAG_CONSTRUCTOR) 46910 return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv); 46911 46912 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply); 46913 if (!s) 46914 return JS_EXCEPTION; 46915 if (!s->is_func) { 46916 JS_FreeValue(ctx, method); 46917 return JS_ThrowTypeError(ctx, "not a function"); 46918 } 46919 if (JS_IsUndefined(method)) 46920 return JS_Call(ctx, s->target, this_obj, argc, argv); 46921 arg_array = js_create_array(ctx, argc, argv); 46922 if (JS_IsException(arg_array)) { 46923 ret = JS_EXCEPTION; 46924 goto fail; 46925 } 46926 args[0] = s->target; 46927 args[1] = this_obj; 46928 args[2] = arg_array; 46929 ret = JS_Call(ctx, method, s->handler, 3, args); 46930 fail: 46931 JS_FreeValue(ctx, method); 46932 JS_FreeValue(ctx, arg_array); 46933 return ret; 46934 } 46935 46936 /* `js_resolve_proxy`: resolve the proxy chain 46937 `*pval` is updated with to ultimate proxy target 46938 `throw_exception` controls whether exceptions are thown or not 46939 - return -1 in case of error 46940 - otherwise return 0 46941 */ 46942 static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) { 46943 int depth = 0; 46944 JSObject *p; 46945 JSProxyData *s; 46946 46947 while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) { 46948 p = JS_VALUE_GET_OBJ(*pval); 46949 if (p->class_id != JS_CLASS_PROXY) 46950 break; 46951 if (depth++ > 1000) { 46952 if (throw_exception) 46953 JS_ThrowStackOverflow(ctx); 46954 return -1; 46955 } 46956 s = p->u.opaque; 46957 if (s->is_revoked) { 46958 if (throw_exception) 46959 JS_ThrowTypeErrorRevokedProxy(ctx); 46960 return -1; 46961 } 46962 *pval = s->target; 46963 } 46964 return 0; 46965 } 46966 46967 static const JSClassExoticMethods js_proxy_exotic_methods = { 46968 .get_own_property = js_proxy_get_own_property, 46969 .define_own_property = js_proxy_define_own_property, 46970 .delete_property = js_proxy_delete_property, 46971 .get_own_property_names = js_proxy_get_own_property_names, 46972 .has_property = js_proxy_has, 46973 .get_property = js_proxy_get, 46974 .set_property = js_proxy_set, 46975 }; 46976 46977 static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val, 46978 int argc, JSValueConst *argv) 46979 { 46980 JSValueConst target, handler; 46981 JSValue obj; 46982 JSProxyData *s; 46983 46984 target = argv[0]; 46985 handler = argv[1]; 46986 if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT || 46987 JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT) 46988 return JS_ThrowTypeErrorNotAnObject(ctx); 46989 46990 obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY); 46991 if (JS_IsException(obj)) 46992 return obj; 46993 s = js_malloc(ctx, sizeof(JSProxyData)); 46994 if (!s) { 46995 JS_FreeValue(ctx, obj); 46996 return JS_EXCEPTION; 46997 } 46998 s->target = JS_DupValue(ctx, target); 46999 s->handler = JS_DupValue(ctx, handler); 47000 s->is_func = JS_IsFunction(ctx, target); 47001 s->is_revoked = FALSE; 47002 JS_SetOpaque(obj, s); 47003 JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target)); 47004 return obj; 47005 } 47006 47007 static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val, 47008 int argc, JSValueConst *argv, int magic, 47009 JSValue *func_data) 47010 { 47011 JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY); 47012 if (s) { 47013 /* We do not free the handler and target in case they are 47014 referenced as constants in the C call stack */ 47015 s->is_revoked = TRUE; 47016 JS_FreeValue(ctx, func_data[0]); 47017 func_data[0] = JS_NULL; 47018 } 47019 return JS_UNDEFINED; 47020 } 47021 47022 static JSValue js_proxy_revoke_constructor(JSContext *ctx, 47023 JSValueConst proxy_obj) 47024 { 47025 return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj); 47026 } 47027 47028 static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val, 47029 int argc, JSValueConst *argv) 47030 { 47031 JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj; 47032 47033 proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv); 47034 if (JS_IsException(proxy_obj)) 47035 goto fail; 47036 revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj); 47037 if (JS_IsException(revoke_obj)) 47038 goto fail; 47039 obj = JS_NewObject(ctx); 47040 if (JS_IsException(obj)) 47041 goto fail; 47042 // XXX: exceptions? 47043 JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E); 47044 JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E); 47045 return obj; 47046 fail: 47047 JS_FreeValue(ctx, proxy_obj); 47048 JS_FreeValue(ctx, revoke_obj); 47049 return JS_EXCEPTION; 47050 } 47051 47052 static const JSCFunctionListEntry js_proxy_funcs[] = { 47053 JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ), 47054 }; 47055 47056 static const JSClassShortDef js_proxy_class_def[] = { 47057 { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */ 47058 }; 47059 47060 void JS_AddIntrinsicProxy(JSContext *ctx) 47061 { 47062 JSRuntime *rt = ctx->rt; 47063 JSValue obj1; 47064 47065 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) { 47066 init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY, 47067 countof(js_proxy_class_def)); 47068 rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods; 47069 rt->class_array[JS_CLASS_PROXY].call = js_proxy_call; 47070 } 47071 47072 obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2, 47073 JS_CFUNC_constructor, 0); 47074 JS_SetConstructorBit(ctx, obj1, TRUE); 47075 JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs, 47076 countof(js_proxy_funcs)); 47077 JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy", 47078 obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 47079 } 47080 47081 /* Symbol */ 47082 47083 static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target, 47084 int argc, JSValueConst *argv) 47085 { 47086 JSValue str; 47087 JSString *p; 47088 47089 if (!JS_IsUndefined(new_target)) 47090 return JS_ThrowTypeError(ctx, "not a constructor"); 47091 if (argc == 0 || JS_IsUndefined(argv[0])) { 47092 p = NULL; 47093 } else { 47094 str = JS_ToString(ctx, argv[0]); 47095 if (JS_IsException(str)) 47096 return JS_EXCEPTION; 47097 p = JS_VALUE_GET_STRING(str); 47098 } 47099 return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL); 47100 } 47101 47102 static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val) 47103 { 47104 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL) 47105 return JS_DupValue(ctx, this_val); 47106 47107 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 47108 JSObject *p = JS_VALUE_GET_OBJ(this_val); 47109 if (p->class_id == JS_CLASS_SYMBOL) { 47110 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL) 47111 return JS_DupValue(ctx, p->u.object_data); 47112 } 47113 } 47114 return JS_ThrowTypeError(ctx, "not a symbol"); 47115 } 47116 47117 static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, 47118 int argc, JSValueConst *argv) 47119 { 47120 JSValue val, ret; 47121 val = js_thisSymbolValue(ctx, this_val); 47122 if (JS_IsException(val)) 47123 return val; 47124 /* XXX: use JS_ToStringInternal() with a flags */ 47125 ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); 47126 JS_FreeValue(ctx, val); 47127 return ret; 47128 } 47129 47130 static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val, 47131 int argc, JSValueConst *argv) 47132 { 47133 return js_thisSymbolValue(ctx, this_val); 47134 } 47135 47136 static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val) 47137 { 47138 JSValue val, ret; 47139 JSAtomStruct *p; 47140 47141 val = js_thisSymbolValue(ctx, this_val); 47142 if (JS_IsException(val)) 47143 return val; 47144 p = JS_VALUE_GET_PTR(val); 47145 if (p->len == 0 && p->is_wide_char != 0) { 47146 ret = JS_UNDEFINED; 47147 } else { 47148 ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)); 47149 } 47150 JS_FreeValue(ctx, val); 47151 return ret; 47152 } 47153 47154 static const JSCFunctionListEntry js_symbol_proto_funcs[] = { 47155 JS_CFUNC_DEF("toString", 0, js_symbol_toString ), 47156 JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ), 47157 // XXX: should have writable: false 47158 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ), 47159 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ), 47160 JS_CGETSET_DEF("description", js_symbol_get_description, NULL ), 47161 }; 47162 47163 static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val, 47164 int argc, JSValueConst *argv) 47165 { 47166 JSValue str; 47167 47168 str = JS_ToString(ctx, argv[0]); 47169 if (JS_IsException(str)) 47170 return JS_EXCEPTION; 47171 return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL); 47172 } 47173 47174 static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val, 47175 int argc, JSValueConst *argv) 47176 { 47177 JSAtomStruct *p; 47178 47179 if (!JS_IsSymbol(argv[0])) 47180 return JS_ThrowTypeError(ctx, "not a symbol"); 47181 p = JS_VALUE_GET_PTR(argv[0]); 47182 if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL) 47183 return JS_UNDEFINED; 47184 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); 47185 } 47186 47187 static const JSCFunctionListEntry js_symbol_funcs[] = { 47188 JS_CFUNC_DEF("for", 1, js_symbol_for ), 47189 JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ), 47190 }; 47191 47192 /* Set/Map/WeakSet/WeakMap */ 47193 47194 typedef struct JSMapRecord { 47195 int ref_count; /* used during enumeration to avoid freeing the record */ 47196 BOOL empty; /* TRUE if the record is deleted */ 47197 struct JSMapState *map; 47198 struct JSMapRecord *next_weak_ref; 47199 struct list_head link; 47200 struct list_head hash_link; 47201 JSValue key; 47202 JSValue value; 47203 } JSMapRecord; 47204 47205 typedef struct JSMapState { 47206 BOOL is_weak; /* TRUE if WeakSet/WeakMap */ 47207 struct list_head records; /* list of JSMapRecord.link */ 47208 uint32_t record_count; 47209 struct list_head *hash_table; 47210 uint32_t hash_size; /* must be a power of two */ 47211 uint32_t record_count_threshold; /* count at which a hash table 47212 resize is needed */ 47213 } JSMapState; 47214 47215 #define MAGIC_SET (1 << 0) 47216 #define MAGIC_WEAK (1 << 1) 47217 47218 static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, 47219 int argc, JSValueConst *argv, int magic) 47220 { 47221 JSMapState *s; 47222 JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; 47223 JSValueConst arr; 47224 BOOL is_set, is_weak; 47225 47226 is_set = magic & MAGIC_SET; 47227 is_weak = ((magic & MAGIC_WEAK) != 0); 47228 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); 47229 if (JS_IsException(obj)) 47230 return JS_EXCEPTION; 47231 s = js_mallocz(ctx, sizeof(*s)); 47232 if (!s) 47233 goto fail; 47234 init_list_head(&s->records); 47235 s->is_weak = is_weak; 47236 JS_SetOpaque(obj, s); 47237 s->hash_size = 1; 47238 s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size); 47239 if (!s->hash_table) 47240 goto fail; 47241 init_list_head(&s->hash_table[0]); 47242 s->record_count_threshold = 4; 47243 47244 arr = JS_UNDEFINED; 47245 if (argc > 0) 47246 arr = argv[0]; 47247 if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) { 47248 JSValue item, ret; 47249 BOOL done; 47250 47251 adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set); 47252 if (JS_IsException(adder)) 47253 goto fail; 47254 if (!JS_IsFunction(ctx, adder)) { 47255 JS_ThrowTypeError(ctx, "set/add is not a function"); 47256 goto fail; 47257 } 47258 47259 iter = JS_GetIterator(ctx, arr, FALSE); 47260 if (JS_IsException(iter)) 47261 goto fail; 47262 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 47263 if (JS_IsException(next_method)) 47264 goto fail; 47265 47266 for(;;) { 47267 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 47268 if (JS_IsException(item)) 47269 goto fail; 47270 if (done) { 47271 JS_FreeValue(ctx, item); 47272 break; 47273 } 47274 if (is_set) { 47275 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); 47276 if (JS_IsException(ret)) { 47277 JS_FreeValue(ctx, item); 47278 goto fail; 47279 } 47280 } else { 47281 JSValue key, value; 47282 JSValueConst args[2]; 47283 key = JS_UNDEFINED; 47284 value = JS_UNDEFINED; 47285 if (!JS_IsObject(item)) { 47286 JS_ThrowTypeErrorNotAnObject(ctx); 47287 goto fail1; 47288 } 47289 key = JS_GetPropertyUint32(ctx, item, 0); 47290 if (JS_IsException(key)) 47291 goto fail1; 47292 value = JS_GetPropertyUint32(ctx, item, 1); 47293 if (JS_IsException(value)) 47294 goto fail1; 47295 args[0] = key; 47296 args[1] = value; 47297 ret = JS_Call(ctx, adder, obj, 2, args); 47298 if (JS_IsException(ret)) { 47299 fail1: 47300 JS_FreeValue(ctx, item); 47301 JS_FreeValue(ctx, key); 47302 JS_FreeValue(ctx, value); 47303 goto fail; 47304 } 47305 JS_FreeValue(ctx, key); 47306 JS_FreeValue(ctx, value); 47307 } 47308 JS_FreeValue(ctx, ret); 47309 JS_FreeValue(ctx, item); 47310 } 47311 JS_FreeValue(ctx, next_method); 47312 JS_FreeValue(ctx, iter); 47313 JS_FreeValue(ctx, adder); 47314 } 47315 return obj; 47316 fail: 47317 if (JS_IsObject(iter)) { 47318 /* close the iterator object, preserving pending exception */ 47319 JS_IteratorClose(ctx, iter, TRUE); 47320 } 47321 JS_FreeValue(ctx, next_method); 47322 JS_FreeValue(ctx, iter); 47323 JS_FreeValue(ctx, adder); 47324 JS_FreeValue(ctx, obj); 47325 return JS_EXCEPTION; 47326 } 47327 47328 /* XXX: could normalize strings to speed up comparison */ 47329 static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key) 47330 { 47331 uint32_t tag = JS_VALUE_GET_TAG(key); 47332 /* convert -0.0 to +0.0 */ 47333 if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) { 47334 key = JS_NewInt32(ctx, 0); 47335 } 47336 return key; 47337 } 47338 47339 /* XXX: better hash ? */ 47340 static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) 47341 { 47342 uint32_t tag = JS_VALUE_GET_NORM_TAG(key); 47343 uint32_t h; 47344 double d; 47345 JSFloat64Union u; 47346 47347 switch(tag) { 47348 case JS_TAG_BOOL: 47349 h = JS_VALUE_GET_INT(key); 47350 break; 47351 case JS_TAG_STRING: 47352 h = hash_string(JS_VALUE_GET_STRING(key), 0); 47353 break; 47354 case JS_TAG_OBJECT: 47355 case JS_TAG_SYMBOL: 47356 h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; 47357 break; 47358 case JS_TAG_INT: 47359 d = JS_VALUE_GET_INT(key); 47360 goto hash_float64; 47361 case JS_TAG_FLOAT64: 47362 d = JS_VALUE_GET_FLOAT64(key); 47363 /* normalize the NaN */ 47364 if (isnan(d)) 47365 d = JS_FLOAT64_NAN; 47366 hash_float64: 47367 u.d = d; 47368 h = (u.u32[0] ^ u.u32[1]) * 3163; 47369 return h ^= JS_TAG_FLOAT64; 47370 default: 47371 h = 0; /* XXX: bignum support */ 47372 break; 47373 } 47374 h ^= tag; 47375 return h; 47376 } 47377 47378 static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s, 47379 JSValueConst key) 47380 { 47381 struct list_head *el; 47382 JSMapRecord *mr; 47383 uint32_t h; 47384 h = map_hash_key(ctx, key) & (s->hash_size - 1); 47385 list_for_each(el, &s->hash_table[h]) { 47386 mr = list_entry(el, JSMapRecord, hash_link); 47387 if (js_same_value_zero(ctx, mr->key, key)) 47388 return mr; 47389 } 47390 return NULL; 47391 } 47392 47393 static void map_hash_resize(JSContext *ctx, JSMapState *s) 47394 { 47395 uint32_t new_hash_size, i, h; 47396 size_t slack; 47397 struct list_head *new_hash_table, *el; 47398 JSMapRecord *mr; 47399 47400 /* XXX: no reporting of memory allocation failure */ 47401 if (s->hash_size == 1) 47402 new_hash_size = 4; 47403 else 47404 new_hash_size = s->hash_size * 2; 47405 new_hash_table = js_realloc2(ctx, s->hash_table, 47406 sizeof(new_hash_table[0]) * new_hash_size, &slack); 47407 if (!new_hash_table) 47408 return; 47409 new_hash_size += slack / sizeof(*new_hash_table); 47410 47411 for(i = 0; i < new_hash_size; i++) 47412 init_list_head(&new_hash_table[i]); 47413 47414 list_for_each(el, &s->records) { 47415 mr = list_entry(el, JSMapRecord, link); 47416 if (!mr->empty) { 47417 h = map_hash_key(ctx, mr->key) & (new_hash_size - 1); 47418 list_add_tail(&mr->hash_link, &new_hash_table[h]); 47419 } 47420 } 47421 s->hash_table = new_hash_table; 47422 s->hash_size = new_hash_size; 47423 s->record_count_threshold = new_hash_size * 2; 47424 } 47425 47426 static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, 47427 JSValueConst key) 47428 { 47429 uint32_t h; 47430 JSMapRecord *mr; 47431 47432 mr = js_malloc(ctx, sizeof(*mr)); 47433 if (!mr) 47434 return NULL; 47435 mr->ref_count = 1; 47436 mr->map = s; 47437 mr->empty = FALSE; 47438 if (s->is_weak) { 47439 JSObject *p = JS_VALUE_GET_OBJ(key); 47440 /* Add the weak reference */ 47441 mr->next_weak_ref = p->first_weak_ref; 47442 p->first_weak_ref = mr; 47443 } else { 47444 JS_DupValue(ctx, key); 47445 } 47446 mr->key = (JSValue)key; 47447 h = map_hash_key(ctx, key) & (s->hash_size - 1); 47448 list_add_tail(&mr->hash_link, &s->hash_table[h]); 47449 list_add_tail(&mr->link, &s->records); 47450 s->record_count++; 47451 if (s->record_count >= s->record_count_threshold) { 47452 map_hash_resize(ctx, s); 47453 } 47454 return mr; 47455 } 47456 47457 /* Remove the weak reference from the object weak 47458 reference list. we don't use a doubly linked list to 47459 save space, assuming a given object has few weak 47460 references to it */ 47461 static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) 47462 { 47463 JSMapRecord **pmr, *mr1; 47464 JSObject *p; 47465 47466 p = JS_VALUE_GET_OBJ(mr->key); 47467 pmr = &p->first_weak_ref; 47468 for(;;) { 47469 mr1 = *pmr; 47470 assert(mr1 != NULL); 47471 if (mr1 == mr) 47472 break; 47473 pmr = &mr1->next_weak_ref; 47474 } 47475 *pmr = mr1->next_weak_ref; 47476 } 47477 47478 static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) 47479 { 47480 if (mr->empty) 47481 return; 47482 list_del(&mr->hash_link); 47483 if (s->is_weak) { 47484 delete_weak_ref(rt, mr); 47485 } else { 47486 JS_FreeValueRT(rt, mr->key); 47487 } 47488 JS_FreeValueRT(rt, mr->value); 47489 if (--mr->ref_count == 0) { 47490 list_del(&mr->link); 47491 js_free_rt(rt, mr); 47492 } else { 47493 /* keep a zombie record for iterators */ 47494 mr->empty = TRUE; 47495 mr->key = JS_UNDEFINED; 47496 mr->value = JS_UNDEFINED; 47497 } 47498 s->record_count--; 47499 } 47500 47501 static void map_decref_record(JSRuntime *rt, JSMapRecord *mr) 47502 { 47503 if (--mr->ref_count == 0) { 47504 /* the record can be safely removed */ 47505 assert(mr->empty); 47506 list_del(&mr->link); 47507 js_free_rt(rt, mr); 47508 } 47509 } 47510 47511 static void reset_weak_ref(JSRuntime *rt, JSObject *p) 47512 { 47513 JSMapRecord *mr, *mr_next; 47514 JSMapState *s; 47515 47516 /* first pass to remove the records from the WeakMap/WeakSet 47517 lists */ 47518 for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { 47519 s = mr->map; 47520 assert(s->is_weak); 47521 assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ 47522 list_del(&mr->hash_link); 47523 list_del(&mr->link); 47524 } 47525 47526 /* second pass to free the values to avoid modifying the weak 47527 reference list while traversing it. */ 47528 for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) { 47529 mr_next = mr->next_weak_ref; 47530 JS_FreeValueRT(rt, mr->value); 47531 js_free_rt(rt, mr); 47532 } 47533 47534 p->first_weak_ref = NULL; /* fail safe */ 47535 } 47536 47537 static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, 47538 int argc, JSValueConst *argv, int magic) 47539 { 47540 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47541 JSMapRecord *mr; 47542 JSValueConst key, value; 47543 47544 if (!s) 47545 return JS_EXCEPTION; 47546 key = map_normalize_key(ctx, argv[0]); 47547 if (s->is_weak && !JS_IsObject(key)) 47548 return JS_ThrowTypeErrorNotAnObject(ctx); 47549 if (magic & MAGIC_SET) 47550 value = JS_UNDEFINED; 47551 else 47552 value = argv[1]; 47553 mr = map_find_record(ctx, s, key); 47554 if (mr) { 47555 JS_FreeValue(ctx, mr->value); 47556 } else { 47557 mr = map_add_record(ctx, s, key); 47558 if (!mr) 47559 return JS_EXCEPTION; 47560 } 47561 mr->value = JS_DupValue(ctx, value); 47562 return JS_DupValue(ctx, this_val); 47563 } 47564 47565 static JSValue js_map_get(JSContext *ctx, JSValueConst this_val, 47566 int argc, JSValueConst *argv, int magic) 47567 { 47568 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47569 JSMapRecord *mr; 47570 JSValueConst key; 47571 47572 if (!s) 47573 return JS_EXCEPTION; 47574 key = map_normalize_key(ctx, argv[0]); 47575 mr = map_find_record(ctx, s, key); 47576 if (!mr) 47577 return JS_UNDEFINED; 47578 else 47579 return JS_DupValue(ctx, mr->value); 47580 } 47581 47582 static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, 47583 int argc, JSValueConst *argv, int magic) 47584 { 47585 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47586 JSMapRecord *mr; 47587 JSValueConst key; 47588 47589 if (!s) 47590 return JS_EXCEPTION; 47591 key = map_normalize_key(ctx, argv[0]); 47592 mr = map_find_record(ctx, s, key); 47593 return JS_NewBool(ctx, mr != NULL); 47594 } 47595 47596 static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, 47597 int argc, JSValueConst *argv, int magic) 47598 { 47599 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47600 JSMapRecord *mr; 47601 JSValueConst key; 47602 47603 if (!s) 47604 return JS_EXCEPTION; 47605 key = map_normalize_key(ctx, argv[0]); 47606 mr = map_find_record(ctx, s, key); 47607 if (!mr) 47608 return JS_FALSE; 47609 map_delete_record(ctx->rt, s, mr); 47610 return JS_TRUE; 47611 } 47612 47613 static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val, 47614 int argc, JSValueConst *argv, int magic) 47615 { 47616 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47617 struct list_head *el, *el1; 47618 JSMapRecord *mr; 47619 47620 if (!s) 47621 return JS_EXCEPTION; 47622 list_for_each_safe(el, el1, &s->records) { 47623 mr = list_entry(el, JSMapRecord, link); 47624 map_delete_record(ctx->rt, s, mr); 47625 } 47626 return JS_UNDEFINED; 47627 } 47628 47629 static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic) 47630 { 47631 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47632 if (!s) 47633 return JS_EXCEPTION; 47634 return JS_NewUint32(ctx, s->record_count); 47635 } 47636 47637 static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, 47638 int argc, JSValueConst *argv, int magic) 47639 { 47640 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47641 JSValueConst func, this_arg; 47642 JSValue ret, args[3]; 47643 struct list_head *el; 47644 JSMapRecord *mr; 47645 47646 if (!s) 47647 return JS_EXCEPTION; 47648 func = argv[0]; 47649 if (argc > 1) 47650 this_arg = argv[1]; 47651 else 47652 this_arg = JS_UNDEFINED; 47653 if (check_function(ctx, func)) 47654 return JS_EXCEPTION; 47655 /* Note: the list can be modified while traversing it, but the 47656 current element is locked */ 47657 el = s->records.next; 47658 while (el != &s->records) { 47659 mr = list_entry(el, JSMapRecord, link); 47660 if (!mr->empty) { 47661 mr->ref_count++; 47662 /* must duplicate in case the record is deleted */ 47663 args[1] = JS_DupValue(ctx, mr->key); 47664 if (magic) 47665 args[0] = args[1]; 47666 else 47667 args[0] = JS_DupValue(ctx, mr->value); 47668 args[2] = (JSValue)this_val; 47669 ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); 47670 JS_FreeValue(ctx, args[0]); 47671 if (!magic) 47672 JS_FreeValue(ctx, args[1]); 47673 el = el->next; 47674 map_decref_record(ctx->rt, mr); 47675 if (JS_IsException(ret)) 47676 return ret; 47677 JS_FreeValue(ctx, ret); 47678 } else { 47679 el = el->next; 47680 } 47681 } 47682 return JS_UNDEFINED; 47683 } 47684 47685 static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, 47686 int argc, JSValueConst *argv, int is_map) 47687 { 47688 JSValueConst cb, args[2]; 47689 JSValue res, iter, next, groups, key, v, prop; 47690 JSAtom key_atom = JS_ATOM_NULL; 47691 int64_t idx; 47692 BOOL done; 47693 47694 // "is function?" check must be observed before argv[0] is accessed 47695 cb = argv[1]; 47696 if (check_function(ctx, cb)) 47697 return JS_EXCEPTION; 47698 47699 iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); 47700 if (JS_IsException(iter)) 47701 return JS_EXCEPTION; 47702 47703 key = JS_UNDEFINED; 47704 key_atom = JS_ATOM_NULL; 47705 v = JS_UNDEFINED; 47706 prop = JS_UNDEFINED; 47707 groups = JS_UNDEFINED; 47708 47709 next = JS_GetProperty(ctx, iter, JS_ATOM_next); 47710 if (JS_IsException(next)) 47711 goto exception; 47712 47713 if (is_map) { 47714 groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0); 47715 } else { 47716 groups = JS_NewObjectProto(ctx, JS_NULL); 47717 } 47718 if (JS_IsException(groups)) 47719 goto exception; 47720 47721 for (idx = 0; ; idx++) { 47722 if (idx >= MAX_SAFE_INTEGER) { 47723 JS_ThrowTypeError(ctx, "too many elements"); 47724 goto iterator_close_exception; 47725 } 47726 v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); 47727 if (JS_IsException(v)) 47728 goto exception; 47729 if (done) 47730 break; // v is JS_UNDEFINED 47731 47732 args[0] = v; 47733 args[1] = JS_NewInt64(ctx, idx); 47734 key = JS_Call(ctx, cb, ctx->global_obj, 2, args); 47735 if (JS_IsException(key)) 47736 goto iterator_close_exception; 47737 47738 if (is_map) { 47739 prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0); 47740 } else { 47741 key_atom = JS_ValueToAtom(ctx, key); 47742 JS_FreeValue(ctx, key); 47743 key = JS_UNDEFINED; 47744 if (key_atom == JS_ATOM_NULL) 47745 goto iterator_close_exception; 47746 prop = JS_GetProperty(ctx, groups, key_atom); 47747 } 47748 if (JS_IsException(prop)) 47749 goto exception; 47750 47751 if (JS_IsUndefined(prop)) { 47752 prop = JS_NewArray(ctx); 47753 if (JS_IsException(prop)) 47754 goto exception; 47755 if (is_map) { 47756 args[0] = key; 47757 args[1] = prop; 47758 res = js_map_set(ctx, groups, 2, args, 0); 47759 if (JS_IsException(res)) 47760 goto exception; 47761 JS_FreeValue(ctx, res); 47762 } else { 47763 prop = JS_DupValue(ctx, prop); 47764 if (JS_DefinePropertyValue(ctx, groups, key_atom, prop, 47765 JS_PROP_C_W_E) < 0) { 47766 goto exception; 47767 } 47768 } 47769 } 47770 res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0); 47771 if (JS_IsException(res)) 47772 goto exception; 47773 // res is an int64 47774 47775 JS_FreeValue(ctx, prop); 47776 JS_FreeValue(ctx, key); 47777 JS_FreeAtom(ctx, key_atom); 47778 JS_FreeValue(ctx, v); 47779 prop = JS_UNDEFINED; 47780 key = JS_UNDEFINED; 47781 key_atom = JS_ATOM_NULL; 47782 v = JS_UNDEFINED; 47783 } 47784 47785 JS_FreeValue(ctx, iter); 47786 JS_FreeValue(ctx, next); 47787 return groups; 47788 47789 iterator_close_exception: 47790 JS_IteratorClose(ctx, iter, TRUE); 47791 exception: 47792 JS_FreeAtom(ctx, key_atom); 47793 JS_FreeValue(ctx, prop); 47794 JS_FreeValue(ctx, key); 47795 JS_FreeValue(ctx, v); 47796 JS_FreeValue(ctx, groups); 47797 JS_FreeValue(ctx, iter); 47798 JS_FreeValue(ctx, next); 47799 return JS_EXCEPTION; 47800 } 47801 47802 static void js_map_finalizer(JSRuntime *rt, JSValue val) 47803 { 47804 JSObject *p; 47805 JSMapState *s; 47806 struct list_head *el, *el1; 47807 JSMapRecord *mr; 47808 47809 p = JS_VALUE_GET_OBJ(val); 47810 s = p->u.map_state; 47811 if (s) { 47812 /* if the object is deleted we are sure that no iterator is 47813 using it */ 47814 list_for_each_safe(el, el1, &s->records) { 47815 mr = list_entry(el, JSMapRecord, link); 47816 if (!mr->empty) { 47817 if (s->is_weak) 47818 delete_weak_ref(rt, mr); 47819 else 47820 JS_FreeValueRT(rt, mr->key); 47821 JS_FreeValueRT(rt, mr->value); 47822 } 47823 js_free_rt(rt, mr); 47824 } 47825 js_free_rt(rt, s->hash_table); 47826 js_free_rt(rt, s); 47827 } 47828 } 47829 47830 static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) 47831 { 47832 JSObject *p = JS_VALUE_GET_OBJ(val); 47833 JSMapState *s; 47834 struct list_head *el; 47835 JSMapRecord *mr; 47836 47837 s = p->u.map_state; 47838 if (s) { 47839 list_for_each(el, &s->records) { 47840 mr = list_entry(el, JSMapRecord, link); 47841 if (!s->is_weak) 47842 JS_MarkValue(rt, mr->key, mark_func); 47843 JS_MarkValue(rt, mr->value, mark_func); 47844 } 47845 } 47846 } 47847 47848 /* Map Iterator */ 47849 47850 typedef struct JSMapIteratorData { 47851 JSValue obj; 47852 JSIteratorKindEnum kind; 47853 JSMapRecord *cur_record; 47854 } JSMapIteratorData; 47855 47856 static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val) 47857 { 47858 JSObject *p; 47859 JSMapIteratorData *it; 47860 47861 p = JS_VALUE_GET_OBJ(val); 47862 it = p->u.map_iterator_data; 47863 if (it) { 47864 /* During the GC sweep phase the Map finalizer may be 47865 called before the Map iterator finalizer */ 47866 if (JS_IsLiveObject(rt, it->obj) && it->cur_record) { 47867 map_decref_record(rt, it->cur_record); 47868 } 47869 JS_FreeValueRT(rt, it->obj); 47870 js_free_rt(rt, it); 47871 } 47872 } 47873 47874 static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val, 47875 JS_MarkFunc *mark_func) 47876 { 47877 JSObject *p = JS_VALUE_GET_OBJ(val); 47878 JSMapIteratorData *it; 47879 it = p->u.map_iterator_data; 47880 if (it) { 47881 /* the record is already marked by the object */ 47882 JS_MarkValue(rt, it->obj, mark_func); 47883 } 47884 } 47885 47886 static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val, 47887 int argc, JSValueConst *argv, int magic) 47888 { 47889 JSIteratorKindEnum kind; 47890 JSMapState *s; 47891 JSMapIteratorData *it; 47892 JSValue enum_obj; 47893 47894 kind = magic >> 2; 47895 magic &= 3; 47896 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic); 47897 if (!s) 47898 return JS_EXCEPTION; 47899 enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic); 47900 if (JS_IsException(enum_obj)) 47901 goto fail; 47902 it = js_malloc(ctx, sizeof(*it)); 47903 if (!it) { 47904 JS_FreeValue(ctx, enum_obj); 47905 goto fail; 47906 } 47907 it->obj = JS_DupValue(ctx, this_val); 47908 it->kind = kind; 47909 it->cur_record = NULL; 47910 JS_SetOpaque(enum_obj, it); 47911 return enum_obj; 47912 fail: 47913 return JS_EXCEPTION; 47914 } 47915 47916 static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, 47917 int argc, JSValueConst *argv, 47918 BOOL *pdone, int magic) 47919 { 47920 JSMapIteratorData *it; 47921 JSMapState *s; 47922 JSMapRecord *mr; 47923 struct list_head *el; 47924 47925 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic); 47926 if (!it) { 47927 *pdone = FALSE; 47928 return JS_EXCEPTION; 47929 } 47930 if (JS_IsUndefined(it->obj)) 47931 goto done; 47932 s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic); 47933 assert(s != NULL); 47934 if (!it->cur_record) { 47935 el = s->records.next; 47936 } else { 47937 mr = it->cur_record; 47938 el = mr->link.next; 47939 map_decref_record(ctx->rt, mr); /* the record can be freed here */ 47940 } 47941 for(;;) { 47942 if (el == &s->records) { 47943 /* no more record */ 47944 it->cur_record = NULL; 47945 JS_FreeValue(ctx, it->obj); 47946 it->obj = JS_UNDEFINED; 47947 done: 47948 /* end of enumeration */ 47949 *pdone = TRUE; 47950 return JS_UNDEFINED; 47951 } 47952 mr = list_entry(el, JSMapRecord, link); 47953 if (!mr->empty) 47954 break; 47955 /* get the next record */ 47956 el = mr->link.next; 47957 } 47958 47959 /* lock the record so that it won't be freed */ 47960 mr->ref_count++; 47961 it->cur_record = mr; 47962 *pdone = FALSE; 47963 47964 if (it->kind == JS_ITERATOR_KIND_KEY) { 47965 return JS_DupValue(ctx, mr->key); 47966 } else { 47967 JSValueConst args[2]; 47968 args[0] = mr->key; 47969 if (magic) 47970 args[1] = mr->key; 47971 else 47972 args[1] = mr->value; 47973 if (it->kind == JS_ITERATOR_KIND_VALUE) { 47974 return JS_DupValue(ctx, args[1]); 47975 } else { 47976 return js_create_array(ctx, 2, args); 47977 } 47978 } 47979 } 47980 47981 static const JSCFunctionListEntry js_map_funcs[] = { 47982 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), 47983 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 47984 }; 47985 47986 static const JSCFunctionListEntry js_map_proto_funcs[] = { 47987 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ), 47988 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ), 47989 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ), 47990 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ), 47991 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ), 47992 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0), 47993 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ), 47994 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ), 47995 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ), 47996 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ), 47997 JS_ALIAS_DEF("[Symbol.iterator]", "entries" ), 47998 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ), 47999 }; 48000 48001 static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = { 48002 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ), 48003 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ), 48004 }; 48005 48006 static const JSCFunctionListEntry js_set_proto_funcs[] = { 48007 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ), 48008 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ), 48009 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ), 48010 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ), 48011 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ), 48012 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ), 48013 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ), 48014 JS_ALIAS_DEF("keys", "values" ), 48015 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 48016 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ), 48017 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ), 48018 }; 48019 48020 static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = { 48021 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ), 48022 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), 48023 }; 48024 48025 static const JSCFunctionListEntry js_weak_map_proto_funcs[] = { 48026 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ), 48027 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ), 48028 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ), 48029 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ), 48030 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ), 48031 }; 48032 48033 static const JSCFunctionListEntry js_weak_set_proto_funcs[] = { 48034 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ), 48035 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ), 48036 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ), 48037 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ), 48038 }; 48039 48040 static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = { 48041 js_map_proto_funcs, 48042 js_set_proto_funcs, 48043 js_weak_map_proto_funcs, 48044 js_weak_set_proto_funcs, 48045 js_map_iterator_proto_funcs, 48046 js_set_iterator_proto_funcs, 48047 }; 48048 48049 static const uint8_t js_map_proto_funcs_count[6] = { 48050 countof(js_map_proto_funcs), 48051 countof(js_set_proto_funcs), 48052 countof(js_weak_map_proto_funcs), 48053 countof(js_weak_set_proto_funcs), 48054 countof(js_map_iterator_proto_funcs), 48055 countof(js_set_iterator_proto_funcs), 48056 }; 48057 48058 void JS_AddIntrinsicMapSet(JSContext *ctx) 48059 { 48060 int i; 48061 JSValue obj1; 48062 char buf[ATOM_GET_STR_BUF_SIZE]; 48063 48064 for(i = 0; i < 4; i++) { 48065 const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf), 48066 JS_ATOM_Map + i); 48067 ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx); 48068 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i], 48069 js_map_proto_funcs_ptr[i], 48070 js_map_proto_funcs_count[i]); 48071 obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0, 48072 JS_CFUNC_constructor_magic, i); 48073 if (i < 2) { 48074 JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs, 48075 countof(js_map_funcs)); 48076 } 48077 JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]); 48078 } 48079 48080 for(i = 0; i < 2; i++) { 48081 ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] = 48082 JS_NewObjectProto(ctx, ctx->iterator_proto); 48083 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i], 48084 js_map_proto_funcs_ptr[i + 4], 48085 js_map_proto_funcs_count[i + 4]); 48086 } 48087 } 48088 48089 /* Generator */ 48090 static const JSCFunctionListEntry js_generator_function_proto_funcs[] = { 48091 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE), 48092 }; 48093 48094 static const JSCFunctionListEntry js_generator_proto_funcs[] = { 48095 JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ), 48096 JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ), 48097 JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ), 48098 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE), 48099 }; 48100 48101 /* Promise */ 48102 48103 typedef struct JSPromiseData { 48104 JSPromiseStateEnum promise_state; 48105 /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ 48106 struct list_head promise_reactions[2]; 48107 BOOL is_handled; /* Note: only useful to debug */ 48108 JSValue promise_result; 48109 } JSPromiseData; 48110 48111 typedef struct JSPromiseFunctionDataResolved { 48112 int ref_count; 48113 BOOL already_resolved; 48114 } JSPromiseFunctionDataResolved; 48115 48116 typedef struct JSPromiseFunctionData { 48117 JSValue promise; 48118 JSPromiseFunctionDataResolved *presolved; 48119 } JSPromiseFunctionData; 48120 48121 typedef struct JSPromiseReactionData { 48122 struct list_head link; /* not used in promise_reaction_job */ 48123 JSValue resolving_funcs[2]; 48124 JSValue handler; 48125 } JSPromiseReactionData; 48126 48127 JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) 48128 { 48129 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 48130 if (!s) 48131 return -1; 48132 return s->promise_state; 48133 } 48134 48135 JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) 48136 { 48137 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 48138 if (!s) 48139 return JS_UNDEFINED; 48140 return JS_DupValue(ctx, s->promise_result); 48141 } 48142 48143 static int js_create_resolving_functions(JSContext *ctx, JSValue *args, 48144 JSValueConst promise); 48145 48146 static void promise_reaction_data_free(JSRuntime *rt, 48147 JSPromiseReactionData *rd) 48148 { 48149 JS_FreeValueRT(rt, rd->resolving_funcs[0]); 48150 JS_FreeValueRT(rt, rd->resolving_funcs[1]); 48151 JS_FreeValueRT(rt, rd->handler); 48152 js_free_rt(rt, rd); 48153 } 48154 48155 static JSValue promise_reaction_job(JSContext *ctx, int argc, 48156 JSValueConst *argv) 48157 { 48158 JSValueConst handler, arg, func; 48159 JSValue res, res2; 48160 BOOL is_reject; 48161 48162 assert(argc == 5); 48163 handler = argv[2]; 48164 is_reject = JS_ToBool(ctx, argv[3]); 48165 arg = argv[4]; 48166 #ifdef DUMP_PROMISE 48167 printf("promise_reaction_job: is_reject=%d\n", is_reject); 48168 #endif 48169 48170 if (JS_IsUndefined(handler)) { 48171 if (is_reject) { 48172 res = JS_Throw(ctx, JS_DupValue(ctx, arg)); 48173 } else { 48174 res = JS_DupValue(ctx, arg); 48175 } 48176 } else { 48177 res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg); 48178 } 48179 is_reject = JS_IsException(res); 48180 if (is_reject) 48181 res = JS_GetException(ctx); 48182 func = argv[is_reject]; 48183 /* as an extension, we support undefined as value to avoid 48184 creating a dummy promise in the 'await' implementation of async 48185 functions */ 48186 if (!JS_IsUndefined(func)) { 48187 res2 = JS_Call(ctx, func, JS_UNDEFINED, 48188 1, (JSValueConst *)&res); 48189 } else { 48190 res2 = JS_UNDEFINED; 48191 } 48192 JS_FreeValue(ctx, res); 48193 48194 return res2; 48195 } 48196 48197 void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, 48198 JSHostPromiseRejectionTracker *cb, 48199 void *opaque) 48200 { 48201 rt->host_promise_rejection_tracker = cb; 48202 rt->host_promise_rejection_tracker_opaque = opaque; 48203 } 48204 48205 static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise, 48206 JSValueConst value, BOOL is_reject) 48207 { 48208 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 48209 struct list_head *el, *el1; 48210 JSPromiseReactionData *rd; 48211 JSValueConst args[5]; 48212 48213 if (!s || s->promise_state != JS_PROMISE_PENDING) 48214 return; /* should never happen */ 48215 set_value(ctx, &s->promise_result, JS_DupValue(ctx, value)); 48216 s->promise_state = JS_PROMISE_FULFILLED + is_reject; 48217 #ifdef DUMP_PROMISE 48218 printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject); 48219 #endif 48220 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { 48221 JSRuntime *rt = ctx->rt; 48222 if (rt->host_promise_rejection_tracker) { 48223 rt->host_promise_rejection_tracker(ctx, promise, value, FALSE, 48224 rt->host_promise_rejection_tracker_opaque); 48225 } 48226 } 48227 48228 list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) { 48229 rd = list_entry(el, JSPromiseReactionData, link); 48230 args[0] = rd->resolving_funcs[0]; 48231 args[1] = rd->resolving_funcs[1]; 48232 args[2] = rd->handler; 48233 args[3] = JS_NewBool(ctx, is_reject); 48234 args[4] = value; 48235 JS_EnqueueJob(ctx, promise_reaction_job, 5, args); 48236 list_del(&rd->link); 48237 promise_reaction_data_free(ctx->rt, rd); 48238 } 48239 48240 list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) { 48241 rd = list_entry(el, JSPromiseReactionData, link); 48242 list_del(&rd->link); 48243 promise_reaction_data_free(ctx->rt, rd); 48244 } 48245 } 48246 48247 static void reject_promise(JSContext *ctx, JSValueConst promise, 48248 JSValueConst value) 48249 { 48250 fulfill_or_reject_promise(ctx, promise, value, TRUE); 48251 } 48252 48253 static JSValue js_promise_resolve_thenable_job(JSContext *ctx, 48254 int argc, JSValueConst *argv) 48255 { 48256 JSValueConst promise, thenable, then; 48257 JSValue args[2], res; 48258 48259 #ifdef DUMP_PROMISE 48260 printf("js_promise_resolve_thenable_job\n"); 48261 #endif 48262 assert(argc == 3); 48263 promise = argv[0]; 48264 thenable = argv[1]; 48265 then = argv[2]; 48266 if (js_create_resolving_functions(ctx, args, promise) < 0) 48267 return JS_EXCEPTION; 48268 res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); 48269 if (JS_IsException(res)) { 48270 JSValue error = JS_GetException(ctx); 48271 res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); 48272 JS_FreeValue(ctx, error); 48273 } 48274 JS_FreeValue(ctx, args[0]); 48275 JS_FreeValue(ctx, args[1]); 48276 return res; 48277 } 48278 48279 static void js_promise_resolve_function_free_resolved(JSRuntime *rt, 48280 JSPromiseFunctionDataResolved *sr) 48281 { 48282 if (--sr->ref_count == 0) { 48283 js_free_rt(rt, sr); 48284 } 48285 } 48286 48287 static int js_create_resolving_functions(JSContext *ctx, 48288 JSValue *resolving_funcs, 48289 JSValueConst promise) 48290 48291 { 48292 JSValue obj; 48293 JSPromiseFunctionData *s; 48294 JSPromiseFunctionDataResolved *sr; 48295 int i, ret; 48296 48297 sr = js_malloc(ctx, sizeof(*sr)); 48298 if (!sr) 48299 return -1; 48300 sr->ref_count = 1; 48301 sr->already_resolved = FALSE; /* must be shared between the two functions */ 48302 ret = 0; 48303 for(i = 0; i < 2; i++) { 48304 obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, 48305 JS_CLASS_PROMISE_RESOLVE_FUNCTION + i); 48306 if (JS_IsException(obj)) 48307 goto fail; 48308 s = js_malloc(ctx, sizeof(*s)); 48309 if (!s) { 48310 JS_FreeValue(ctx, obj); 48311 fail: 48312 48313 if (i != 0) 48314 JS_FreeValue(ctx, resolving_funcs[0]); 48315 ret = -1; 48316 break; 48317 } 48318 sr->ref_count++; 48319 s->presolved = sr; 48320 s->promise = JS_DupValue(ctx, promise); 48321 JS_SetOpaque(obj, s); 48322 js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1); 48323 resolving_funcs[i] = obj; 48324 } 48325 js_promise_resolve_function_free_resolved(ctx->rt, sr); 48326 return ret; 48327 } 48328 48329 static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val) 48330 { 48331 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; 48332 if (s) { 48333 js_promise_resolve_function_free_resolved(rt, s->presolved); 48334 JS_FreeValueRT(rt, s->promise); 48335 js_free_rt(rt, s); 48336 } 48337 } 48338 48339 static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val, 48340 JS_MarkFunc *mark_func) 48341 { 48342 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data; 48343 if (s) { 48344 JS_MarkValue(rt, s->promise, mark_func); 48345 } 48346 } 48347 48348 static JSValue js_promise_resolve_function_call(JSContext *ctx, 48349 JSValueConst func_obj, 48350 JSValueConst this_val, 48351 int argc, JSValueConst *argv, 48352 int flags) 48353 { 48354 JSObject *p = JS_VALUE_GET_OBJ(func_obj); 48355 JSPromiseFunctionData *s; 48356 JSValueConst resolution, args[3]; 48357 JSValue then; 48358 BOOL is_reject; 48359 48360 s = p->u.promise_function_data; 48361 if (!s || s->presolved->already_resolved) 48362 return JS_UNDEFINED; 48363 s->presolved->already_resolved = TRUE; 48364 is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION; 48365 if (argc > 0) 48366 resolution = argv[0]; 48367 else 48368 resolution = JS_UNDEFINED; 48369 #ifdef DUMP_PROMISE 48370 printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject); 48371 JS_DumpValue(ctx, resolution); 48372 printf("\n"); 48373 #endif 48374 if (is_reject || !JS_IsObject(resolution)) { 48375 goto done; 48376 } else if (js_same_value(ctx, resolution, s->promise)) { 48377 JS_ThrowTypeError(ctx, "promise self resolution"); 48378 goto fail_reject; 48379 } 48380 then = JS_GetProperty(ctx, resolution, JS_ATOM_then); 48381 if (JS_IsException(then)) { 48382 JSValue error; 48383 fail_reject: 48384 error = JS_GetException(ctx); 48385 reject_promise(ctx, s->promise, error); 48386 JS_FreeValue(ctx, error); 48387 } else if (!JS_IsFunction(ctx, then)) { 48388 JS_FreeValue(ctx, then); 48389 done: 48390 fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject); 48391 } else { 48392 args[0] = s->promise; 48393 args[1] = resolution; 48394 args[2] = then; 48395 JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args); 48396 JS_FreeValue(ctx, then); 48397 } 48398 return JS_UNDEFINED; 48399 } 48400 48401 static void js_promise_finalizer(JSRuntime *rt, JSValue val) 48402 { 48403 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); 48404 struct list_head *el, *el1; 48405 int i; 48406 48407 if (!s) 48408 return; 48409 for(i = 0; i < 2; i++) { 48410 list_for_each_safe(el, el1, &s->promise_reactions[i]) { 48411 JSPromiseReactionData *rd = 48412 list_entry(el, JSPromiseReactionData, link); 48413 promise_reaction_data_free(rt, rd); 48414 } 48415 } 48416 JS_FreeValueRT(rt, s->promise_result); 48417 js_free_rt(rt, s); 48418 } 48419 48420 static void js_promise_mark(JSRuntime *rt, JSValueConst val, 48421 JS_MarkFunc *mark_func) 48422 { 48423 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE); 48424 struct list_head *el; 48425 int i; 48426 48427 if (!s) 48428 return; 48429 for(i = 0; i < 2; i++) { 48430 list_for_each(el, &s->promise_reactions[i]) { 48431 JSPromiseReactionData *rd = 48432 list_entry(el, JSPromiseReactionData, link); 48433 JS_MarkValue(rt, rd->resolving_funcs[0], mark_func); 48434 JS_MarkValue(rt, rd->resolving_funcs[1], mark_func); 48435 JS_MarkValue(rt, rd->handler, mark_func); 48436 } 48437 } 48438 JS_MarkValue(rt, s->promise_result, mark_func); 48439 } 48440 48441 static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, 48442 int argc, JSValueConst *argv) 48443 { 48444 JSValueConst executor; 48445 JSValue obj; 48446 JSPromiseData *s; 48447 JSValue args[2], ret; 48448 int i; 48449 48450 executor = argv[0]; 48451 if (check_function(ctx, executor)) 48452 return JS_EXCEPTION; 48453 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE); 48454 if (JS_IsException(obj)) 48455 return JS_EXCEPTION; 48456 s = js_mallocz(ctx, sizeof(*s)); 48457 if (!s) 48458 goto fail; 48459 s->promise_state = JS_PROMISE_PENDING; 48460 s->is_handled = FALSE; 48461 for(i = 0; i < 2; i++) 48462 init_list_head(&s->promise_reactions[i]); 48463 s->promise_result = JS_UNDEFINED; 48464 JS_SetOpaque(obj, s); 48465 if (js_create_resolving_functions(ctx, args, obj)) 48466 goto fail; 48467 ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args); 48468 if (JS_IsException(ret)) { 48469 JSValue ret2, error; 48470 error = JS_GetException(ctx); 48471 ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); 48472 JS_FreeValue(ctx, error); 48473 if (JS_IsException(ret2)) 48474 goto fail1; 48475 JS_FreeValue(ctx, ret2); 48476 } 48477 JS_FreeValue(ctx, ret); 48478 JS_FreeValue(ctx, args[0]); 48479 JS_FreeValue(ctx, args[1]); 48480 return obj; 48481 fail1: 48482 JS_FreeValue(ctx, args[0]); 48483 JS_FreeValue(ctx, args[1]); 48484 fail: 48485 JS_FreeValue(ctx, obj); 48486 return JS_EXCEPTION; 48487 } 48488 48489 static JSValue js_promise_executor(JSContext *ctx, 48490 JSValueConst this_val, 48491 int argc, JSValueConst *argv, 48492 int magic, JSValue *func_data) 48493 { 48494 int i; 48495 48496 for(i = 0; i < 2; i++) { 48497 if (!JS_IsUndefined(func_data[i])) 48498 return JS_ThrowTypeError(ctx, "resolving function already set"); 48499 func_data[i] = JS_DupValue(ctx, argv[i]); 48500 } 48501 return JS_UNDEFINED; 48502 } 48503 48504 static JSValue js_promise_executor_new(JSContext *ctx) 48505 { 48506 JSValueConst func_data[2]; 48507 48508 func_data[0] = JS_UNDEFINED; 48509 func_data[1] = JS_UNDEFINED; 48510 return JS_NewCFunctionData(ctx, js_promise_executor, 2, 48511 0, 2, func_data); 48512 } 48513 48514 static JSValue js_new_promise_capability(JSContext *ctx, 48515 JSValue *resolving_funcs, 48516 JSValueConst ctor) 48517 { 48518 JSValue executor, result_promise; 48519 JSCFunctionDataRecord *s; 48520 int i; 48521 48522 executor = js_promise_executor_new(ctx); 48523 if (JS_IsException(executor)) 48524 return executor; 48525 48526 if (JS_IsUndefined(ctor)) { 48527 result_promise = js_promise_constructor(ctx, ctor, 1, 48528 (JSValueConst *)&executor); 48529 } else { 48530 result_promise = JS_CallConstructor(ctx, ctor, 1, 48531 (JSValueConst *)&executor); 48532 } 48533 if (JS_IsException(result_promise)) 48534 goto fail; 48535 s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA); 48536 for(i = 0; i < 2; i++) { 48537 if (check_function(ctx, s->data[i])) 48538 goto fail; 48539 } 48540 for(i = 0; i < 2; i++) 48541 resolving_funcs[i] = JS_DupValue(ctx, s->data[i]); 48542 JS_FreeValue(ctx, executor); 48543 return result_promise; 48544 fail: 48545 JS_FreeValue(ctx, executor); 48546 JS_FreeValue(ctx, result_promise); 48547 return JS_EXCEPTION; 48548 } 48549 48550 JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs) 48551 { 48552 return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED); 48553 } 48554 48555 static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, 48556 int argc, JSValueConst *argv, int magic) 48557 { 48558 JSValue result_promise, resolving_funcs[2], ret; 48559 BOOL is_reject = magic; 48560 48561 if (!JS_IsObject(this_val)) 48562 return JS_ThrowTypeErrorNotAnObject(ctx); 48563 if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) { 48564 JSValue ctor; 48565 BOOL is_same; 48566 ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor); 48567 if (JS_IsException(ctor)) 48568 return ctor; 48569 is_same = js_same_value(ctx, ctor, this_val); 48570 JS_FreeValue(ctx, ctor); 48571 if (is_same) 48572 return JS_DupValue(ctx, argv[0]); 48573 } 48574 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 48575 if (JS_IsException(result_promise)) 48576 return result_promise; 48577 ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv); 48578 JS_FreeValue(ctx, resolving_funcs[0]); 48579 JS_FreeValue(ctx, resolving_funcs[1]); 48580 if (JS_IsException(ret)) { 48581 JS_FreeValue(ctx, result_promise); 48582 return ret; 48583 } 48584 JS_FreeValue(ctx, ret); 48585 return result_promise; 48586 } 48587 48588 static JSValue js_promise_withResolvers(JSContext *ctx, 48589 JSValueConst this_val, 48590 int argc, JSValueConst *argv) 48591 { 48592 JSValue result_promise, resolving_funcs[2], obj; 48593 if (!JS_IsObject(this_val)) 48594 return JS_ThrowTypeErrorNotAnObject(ctx); 48595 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 48596 if (JS_IsException(result_promise)) 48597 return result_promise; 48598 obj = JS_NewObject(ctx); 48599 if (JS_IsException(obj)) { 48600 JS_FreeValue(ctx, resolving_funcs[0]); 48601 JS_FreeValue(ctx, resolving_funcs[1]); 48602 JS_FreeValue(ctx, result_promise); 48603 return JS_EXCEPTION; 48604 } 48605 JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E); 48606 JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E); 48607 JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); 48608 return obj; 48609 } 48610 48611 static __exception int remainingElementsCount_add(JSContext *ctx, 48612 JSValueConst resolve_element_env, 48613 int addend) 48614 { 48615 JSValue val; 48616 int remainingElementsCount; 48617 48618 val = JS_GetPropertyUint32(ctx, resolve_element_env, 0); 48619 if (JS_IsException(val)) 48620 return -1; 48621 if (JS_ToInt32Free(ctx, &remainingElementsCount, val)) 48622 return -1; 48623 remainingElementsCount += addend; 48624 if (JS_SetPropertyUint32(ctx, resolve_element_env, 0, 48625 JS_NewInt32(ctx, remainingElementsCount)) < 0) 48626 return -1; 48627 return (remainingElementsCount == 0); 48628 } 48629 48630 #define PROMISE_MAGIC_all 0 48631 #define PROMISE_MAGIC_allSettled 1 48632 #define PROMISE_MAGIC_any 2 48633 48634 static JSValue js_promise_all_resolve_element(JSContext *ctx, 48635 JSValueConst this_val, 48636 int argc, JSValueConst *argv, 48637 int magic, 48638 JSValue *func_data) 48639 { 48640 int resolve_type = magic & 3; 48641 int is_reject = magic & 4; 48642 BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]); 48643 JSValueConst values = func_data[2]; 48644 JSValueConst resolve = func_data[3]; 48645 JSValueConst resolve_element_env = func_data[4]; 48646 JSValue ret, obj; 48647 int is_zero, index; 48648 48649 if (JS_ToInt32(ctx, &index, func_data[1])) 48650 return JS_EXCEPTION; 48651 if (alreadyCalled) 48652 return JS_UNDEFINED; 48653 func_data[0] = JS_NewBool(ctx, TRUE); 48654 48655 if (resolve_type == PROMISE_MAGIC_allSettled) { 48656 JSValue str; 48657 48658 obj = JS_NewObject(ctx); 48659 if (JS_IsException(obj)) 48660 return JS_EXCEPTION; 48661 str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled"); 48662 if (JS_IsException(str)) 48663 goto fail1; 48664 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status, 48665 str, 48666 JS_PROP_C_W_E) < 0) 48667 goto fail1; 48668 if (JS_DefinePropertyValue(ctx, obj, 48669 is_reject ? JS_ATOM_reason : JS_ATOM_value, 48670 JS_DupValue(ctx, argv[0]), 48671 JS_PROP_C_W_E) < 0) { 48672 fail1: 48673 JS_FreeValue(ctx, obj); 48674 return JS_EXCEPTION; 48675 } 48676 } else { 48677 obj = JS_DupValue(ctx, argv[0]); 48678 } 48679 if (JS_DefinePropertyValueUint32(ctx, values, index, 48680 obj, JS_PROP_C_W_E) < 0) 48681 return JS_EXCEPTION; 48682 48683 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); 48684 if (is_zero < 0) 48685 return JS_EXCEPTION; 48686 if (is_zero) { 48687 if (resolve_type == PROMISE_MAGIC_any) { 48688 JSValue error; 48689 error = js_aggregate_error_constructor(ctx, values); 48690 if (JS_IsException(error)) 48691 return JS_EXCEPTION; 48692 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); 48693 JS_FreeValue(ctx, error); 48694 } else { 48695 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); 48696 } 48697 if (JS_IsException(ret)) 48698 return ret; 48699 JS_FreeValue(ctx, ret); 48700 } 48701 return JS_UNDEFINED; 48702 } 48703 48704 /* magic = 0: Promise.all 1: Promise.allSettled */ 48705 static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, 48706 int argc, JSValueConst *argv, int magic) 48707 { 48708 JSValue result_promise, resolving_funcs[2], item, next_promise, ret; 48709 JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED; 48710 JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element; 48711 JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED; 48712 JSValueConst then_args[2], resolve_element_data[5]; 48713 BOOL done; 48714 int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any); 48715 48716 if (!JS_IsObject(this_val)) 48717 return JS_ThrowTypeErrorNotAnObject(ctx); 48718 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 48719 if (JS_IsException(result_promise)) 48720 return result_promise; 48721 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); 48722 if (JS_IsException(promise_resolve) || 48723 check_function(ctx, promise_resolve)) 48724 goto fail_reject; 48725 iter = JS_GetIterator(ctx, argv[0], FALSE); 48726 if (JS_IsException(iter)) { 48727 JSValue error; 48728 fail_reject: 48729 error = JS_GetException(ctx); 48730 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, 48731 (JSValueConst *)&error); 48732 JS_FreeValue(ctx, error); 48733 if (JS_IsException(ret)) 48734 goto fail; 48735 JS_FreeValue(ctx, ret); 48736 } else { 48737 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 48738 if (JS_IsException(next_method)) 48739 goto fail_reject; 48740 values = JS_NewArray(ctx); 48741 if (JS_IsException(values)) 48742 goto fail_reject; 48743 resolve_element_env = JS_NewArray(ctx); 48744 if (JS_IsException(resolve_element_env)) 48745 goto fail_reject; 48746 /* remainingElementsCount field */ 48747 if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0, 48748 JS_NewInt32(ctx, 1), 48749 JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0) 48750 goto fail_reject; 48751 48752 index = 0; 48753 for(;;) { 48754 /* XXX: conformance: should close the iterator if error on 'done' 48755 access, but not on 'value' access */ 48756 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 48757 if (JS_IsException(item)) 48758 goto fail_reject; 48759 if (done) 48760 break; 48761 next_promise = JS_Call(ctx, promise_resolve, 48762 this_val, 1, (JSValueConst *)&item); 48763 JS_FreeValue(ctx, item); 48764 if (JS_IsException(next_promise)) { 48765 fail_reject1: 48766 JS_IteratorClose(ctx, iter, TRUE); 48767 goto fail_reject; 48768 } 48769 resolve_element_data[0] = JS_NewBool(ctx, FALSE); 48770 resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); 48771 resolve_element_data[2] = values; 48772 resolve_element_data[3] = resolving_funcs[is_promise_any]; 48773 resolve_element_data[4] = resolve_element_env; 48774 resolve_element = 48775 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, 48776 magic, 5, resolve_element_data); 48777 if (JS_IsException(resolve_element)) { 48778 JS_FreeValue(ctx, next_promise); 48779 goto fail_reject1; 48780 } 48781 48782 if (magic == PROMISE_MAGIC_allSettled) { 48783 reject_element = 48784 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1, 48785 magic | 4, 5, resolve_element_data); 48786 if (JS_IsException(reject_element)) { 48787 JS_FreeValue(ctx, next_promise); 48788 goto fail_reject1; 48789 } 48790 } else if (magic == PROMISE_MAGIC_any) { 48791 if (JS_DefinePropertyValueUint32(ctx, values, index, 48792 JS_UNDEFINED, JS_PROP_C_W_E) < 0) 48793 goto fail_reject1; 48794 reject_element = resolve_element; 48795 resolve_element = JS_DupValue(ctx, resolving_funcs[0]); 48796 } else { 48797 reject_element = JS_DupValue(ctx, resolving_funcs[1]); 48798 } 48799 48800 if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) { 48801 JS_FreeValue(ctx, next_promise); 48802 JS_FreeValue(ctx, resolve_element); 48803 JS_FreeValue(ctx, reject_element); 48804 goto fail_reject1; 48805 } 48806 48807 then_args[0] = resolve_element; 48808 then_args[1] = reject_element; 48809 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args); 48810 JS_FreeValue(ctx, resolve_element); 48811 JS_FreeValue(ctx, reject_element); 48812 if (check_exception_free(ctx, ret)) 48813 goto fail_reject1; 48814 index++; 48815 } 48816 48817 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1); 48818 if (is_zero < 0) 48819 goto fail_reject; 48820 if (is_zero) { 48821 if (magic == PROMISE_MAGIC_any) { 48822 JSValue error; 48823 error = js_aggregate_error_constructor(ctx, values); 48824 if (JS_IsException(error)) 48825 goto fail_reject; 48826 JS_FreeValue(ctx, values); 48827 values = error; 48828 } 48829 ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, 48830 1, (JSValueConst *)&values); 48831 if (check_exception_free(ctx, ret)) 48832 goto fail_reject; 48833 } 48834 } 48835 done: 48836 JS_FreeValue(ctx, promise_resolve); 48837 JS_FreeValue(ctx, resolve_element_env); 48838 JS_FreeValue(ctx, values); 48839 JS_FreeValue(ctx, next_method); 48840 JS_FreeValue(ctx, iter); 48841 JS_FreeValue(ctx, resolving_funcs[0]); 48842 JS_FreeValue(ctx, resolving_funcs[1]); 48843 return result_promise; 48844 fail: 48845 JS_FreeValue(ctx, result_promise); 48846 result_promise = JS_EXCEPTION; 48847 goto done; 48848 } 48849 48850 static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, 48851 int argc, JSValueConst *argv) 48852 { 48853 JSValue result_promise, resolving_funcs[2], item, next_promise, ret; 48854 JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED; 48855 JSValue promise_resolve = JS_UNDEFINED; 48856 BOOL done; 48857 48858 if (!JS_IsObject(this_val)) 48859 return JS_ThrowTypeErrorNotAnObject(ctx); 48860 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); 48861 if (JS_IsException(result_promise)) 48862 return result_promise; 48863 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve); 48864 if (JS_IsException(promise_resolve) || 48865 check_function(ctx, promise_resolve)) 48866 goto fail_reject; 48867 iter = JS_GetIterator(ctx, argv[0], FALSE); 48868 if (JS_IsException(iter)) { 48869 JSValue error; 48870 fail_reject: 48871 error = JS_GetException(ctx); 48872 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, 48873 (JSValueConst *)&error); 48874 JS_FreeValue(ctx, error); 48875 if (JS_IsException(ret)) 48876 goto fail; 48877 JS_FreeValue(ctx, ret); 48878 } else { 48879 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 48880 if (JS_IsException(next_method)) 48881 goto fail_reject; 48882 48883 for(;;) { 48884 /* XXX: conformance: should close the iterator if error on 'done' 48885 access, but not on 'value' access */ 48886 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 48887 if (JS_IsException(item)) 48888 goto fail_reject; 48889 if (done) 48890 break; 48891 next_promise = JS_Call(ctx, promise_resolve, 48892 this_val, 1, (JSValueConst *)&item); 48893 JS_FreeValue(ctx, item); 48894 if (JS_IsException(next_promise)) { 48895 fail_reject1: 48896 JS_IteratorClose(ctx, iter, TRUE); 48897 goto fail_reject; 48898 } 48899 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, 48900 (JSValueConst *)resolving_funcs); 48901 if (check_exception_free(ctx, ret)) 48902 goto fail_reject1; 48903 } 48904 } 48905 done: 48906 JS_FreeValue(ctx, promise_resolve); 48907 JS_FreeValue(ctx, next_method); 48908 JS_FreeValue(ctx, iter); 48909 JS_FreeValue(ctx, resolving_funcs[0]); 48910 JS_FreeValue(ctx, resolving_funcs[1]); 48911 return result_promise; 48912 fail: 48913 //JS_FreeValue(ctx, next_method); // why not??? 48914 JS_FreeValue(ctx, result_promise); 48915 result_promise = JS_EXCEPTION; 48916 goto done; 48917 } 48918 48919 static __exception int perform_promise_then(JSContext *ctx, 48920 JSValueConst promise, 48921 JSValueConst *resolve_reject, 48922 JSValueConst *cap_resolving_funcs) 48923 { 48924 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); 48925 JSPromiseReactionData *rd_array[2], *rd; 48926 int i, j; 48927 48928 rd_array[0] = NULL; 48929 rd_array[1] = NULL; 48930 for(i = 0; i < 2; i++) { 48931 JSValueConst handler; 48932 rd = js_mallocz(ctx, sizeof(*rd)); 48933 if (!rd) { 48934 if (i == 1) 48935 promise_reaction_data_free(ctx->rt, rd_array[0]); 48936 return -1; 48937 } 48938 for(j = 0; j < 2; j++) 48939 rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]); 48940 handler = resolve_reject[i]; 48941 if (!JS_IsFunction(ctx, handler)) 48942 handler = JS_UNDEFINED; 48943 rd->handler = JS_DupValue(ctx, handler); 48944 rd_array[i] = rd; 48945 } 48946 48947 if (s->promise_state == JS_PROMISE_PENDING) { 48948 for(i = 0; i < 2; i++) 48949 list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]); 48950 } else { 48951 JSValueConst args[5]; 48952 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) { 48953 JSRuntime *rt = ctx->rt; 48954 if (rt->host_promise_rejection_tracker) { 48955 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result, 48956 TRUE, rt->host_promise_rejection_tracker_opaque); 48957 } 48958 } 48959 i = s->promise_state - JS_PROMISE_FULFILLED; 48960 rd = rd_array[i]; 48961 args[0] = rd->resolving_funcs[0]; 48962 args[1] = rd->resolving_funcs[1]; 48963 args[2] = rd->handler; 48964 args[3] = JS_NewBool(ctx, i); 48965 args[4] = s->promise_result; 48966 JS_EnqueueJob(ctx, promise_reaction_job, 5, args); 48967 for(i = 0; i < 2; i++) 48968 promise_reaction_data_free(ctx->rt, rd_array[i]); 48969 } 48970 s->is_handled = TRUE; 48971 return 0; 48972 } 48973 48974 static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, 48975 int argc, JSValueConst *argv) 48976 { 48977 JSValue ctor, result_promise, resolving_funcs[2]; 48978 JSPromiseData *s; 48979 int i, ret; 48980 48981 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE); 48982 if (!s) 48983 return JS_EXCEPTION; 48984 48985 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 48986 if (JS_IsException(ctor)) 48987 return ctor; 48988 result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); 48989 JS_FreeValue(ctx, ctor); 48990 if (JS_IsException(result_promise)) 48991 return result_promise; 48992 ret = perform_promise_then(ctx, this_val, argv, 48993 (JSValueConst *)resolving_funcs); 48994 for(i = 0; i < 2; i++) 48995 JS_FreeValue(ctx, resolving_funcs[i]); 48996 if (ret) { 48997 JS_FreeValue(ctx, result_promise); 48998 return JS_EXCEPTION; 48999 } 49000 return result_promise; 49001 } 49002 49003 static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val, 49004 int argc, JSValueConst *argv) 49005 { 49006 JSValueConst args[2]; 49007 args[0] = JS_UNDEFINED; 49008 args[1] = argv[0]; 49009 return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args); 49010 } 49011 49012 static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val, 49013 int argc, JSValueConst *argv, 49014 int magic, JSValue *func_data) 49015 { 49016 return JS_DupValue(ctx, func_data[0]); 49017 } 49018 49019 static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val, 49020 int argc, JSValueConst *argv, 49021 int magic, JSValue *func_data) 49022 { 49023 return JS_Throw(ctx, JS_DupValue(ctx, func_data[0])); 49024 } 49025 49026 static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val, 49027 int argc, JSValueConst *argv, 49028 int magic, JSValue *func_data) 49029 { 49030 JSValueConst ctor = func_data[0]; 49031 JSValueConst onFinally = func_data[1]; 49032 JSValue res, promise, ret, then_func; 49033 49034 res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); 49035 if (JS_IsException(res)) 49036 return res; 49037 promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); 49038 JS_FreeValue(ctx, res); 49039 if (JS_IsException(promise)) 49040 return promise; 49041 if (magic == 0) { 49042 then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0, 49043 0, 1, argv); 49044 } else { 49045 then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0, 49046 0, 1, argv); 49047 } 49048 if (JS_IsException(then_func)) { 49049 JS_FreeValue(ctx, promise); 49050 return then_func; 49051 } 49052 ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); 49053 JS_FreeValue(ctx, then_func); 49054 return ret; 49055 } 49056 49057 static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val, 49058 int argc, JSValueConst *argv) 49059 { 49060 JSValueConst onFinally = argv[0]; 49061 JSValue ctor, ret; 49062 JSValue then_funcs[2]; 49063 JSValueConst func_data[2]; 49064 int i; 49065 49066 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 49067 if (JS_IsException(ctor)) 49068 return ctor; 49069 if (!JS_IsFunction(ctx, onFinally)) { 49070 then_funcs[0] = JS_DupValue(ctx, onFinally); 49071 then_funcs[1] = JS_DupValue(ctx, onFinally); 49072 } else { 49073 func_data[0] = ctor; 49074 func_data[1] = onFinally; 49075 for(i = 0; i < 2; i++) { 49076 then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data); 49077 if (JS_IsException(then_funcs[i])) { 49078 if (i == 1) 49079 JS_FreeValue(ctx, then_funcs[0]); 49080 JS_FreeValue(ctx, ctor); 49081 return JS_EXCEPTION; 49082 } 49083 } 49084 } 49085 JS_FreeValue(ctx, ctor); 49086 ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs); 49087 JS_FreeValue(ctx, then_funcs[0]); 49088 JS_FreeValue(ctx, then_funcs[1]); 49089 return ret; 49090 } 49091 49092 static const JSCFunctionListEntry js_promise_funcs[] = { 49093 JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ), 49094 JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ), 49095 JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ), 49096 JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), 49097 JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), 49098 JS_CFUNC_DEF("race", 1, js_promise_race ), 49099 JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), 49100 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), 49101 }; 49102 49103 static const JSCFunctionListEntry js_promise_proto_funcs[] = { 49104 JS_CFUNC_DEF("then", 2, js_promise_then ), 49105 JS_CFUNC_DEF("catch", 1, js_promise_catch ), 49106 JS_CFUNC_DEF("finally", 1, js_promise_finally ), 49107 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ), 49108 }; 49109 49110 /* AsyncFunction */ 49111 static const JSCFunctionListEntry js_async_function_proto_funcs[] = { 49112 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ), 49113 }; 49114 49115 static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx, 49116 JSValueConst this_val, 49117 int argc, JSValueConst *argv, 49118 int magic, JSValue *func_data) 49119 { 49120 return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), 49121 JS_ToBool(ctx, func_data[0])); 49122 } 49123 49124 static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, 49125 BOOL done) 49126 { 49127 JSValueConst func_data[1]; 49128 49129 func_data[0] = (JSValueConst)JS_NewBool(ctx, done); 49130 return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, 49131 1, 0, 1, func_data); 49132 } 49133 49134 /* AsyncIteratorPrototype */ 49135 49136 static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = { 49137 JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ), 49138 }; 49139 49140 /* AsyncFromSyncIteratorPrototype */ 49141 49142 typedef struct JSAsyncFromSyncIteratorData { 49143 JSValue sync_iter; 49144 JSValue next_method; 49145 } JSAsyncFromSyncIteratorData; 49146 49147 static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val) 49148 { 49149 JSAsyncFromSyncIteratorData *s = 49150 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 49151 if (s) { 49152 JS_FreeValueRT(rt, s->sync_iter); 49153 JS_FreeValueRT(rt, s->next_method); 49154 js_free_rt(rt, s); 49155 } 49156 } 49157 49158 static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val, 49159 JS_MarkFunc *mark_func) 49160 { 49161 JSAsyncFromSyncIteratorData *s = 49162 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 49163 if (s) { 49164 JS_MarkValue(rt, s->sync_iter, mark_func); 49165 JS_MarkValue(rt, s->next_method, mark_func); 49166 } 49167 } 49168 49169 static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, 49170 JSValueConst sync_iter) 49171 { 49172 JSValue async_iter, next_method; 49173 JSAsyncFromSyncIteratorData *s; 49174 49175 next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next); 49176 if (JS_IsException(next_method)) 49177 return JS_EXCEPTION; 49178 async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 49179 if (JS_IsException(async_iter)) { 49180 JS_FreeValue(ctx, next_method); 49181 return async_iter; 49182 } 49183 s = js_mallocz(ctx, sizeof(*s)); 49184 if (!s) { 49185 JS_FreeValue(ctx, async_iter); 49186 JS_FreeValue(ctx, next_method); 49187 return JS_EXCEPTION; 49188 } 49189 s->sync_iter = JS_DupValue(ctx, sync_iter); 49190 s->next_method = next_method; 49191 JS_SetOpaque(async_iter, s); 49192 return async_iter; 49193 } 49194 49195 static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val, 49196 int argc, JSValueConst *argv, 49197 int magic) 49198 { 49199 JSValue promise, resolving_funcs[2], value, err, method; 49200 JSAsyncFromSyncIteratorData *s; 49201 int done; 49202 int is_reject; 49203 49204 promise = JS_NewPromiseCapability(ctx, resolving_funcs); 49205 if (JS_IsException(promise)) 49206 return JS_EXCEPTION; 49207 s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR); 49208 if (!s) { 49209 JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator"); 49210 goto reject; 49211 } 49212 49213 if (magic == GEN_MAGIC_NEXT) { 49214 method = JS_DupValue(ctx, s->next_method); 49215 } else { 49216 method = JS_GetProperty(ctx, s->sync_iter, 49217 magic == GEN_MAGIC_RETURN ? JS_ATOM_return : 49218 JS_ATOM_throw); 49219 if (JS_IsException(method)) 49220 goto reject; 49221 if (JS_IsUndefined(method) || JS_IsNull(method)) { 49222 if (magic == GEN_MAGIC_RETURN) { 49223 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE); 49224 is_reject = 0; 49225 } else { 49226 err = JS_DupValue(ctx, argv[0]); 49227 is_reject = 1; 49228 } 49229 goto done_resolve; 49230 } 49231 } 49232 value = JS_IteratorNext2(ctx, s->sync_iter, method, 49233 argc >= 1 ? 1 : 0, argv, &done); 49234 JS_FreeValue(ctx, method); 49235 if (JS_IsException(value)) 49236 goto reject; 49237 if (done == 2) { 49238 JSValue obj = value; 49239 value = JS_IteratorGetCompleteValue(ctx, obj, &done); 49240 JS_FreeValue(ctx, obj); 49241 if (JS_IsException(value)) 49242 goto reject; 49243 } 49244 49245 if (JS_IsException(value)) { 49246 JSValue res2; 49247 reject: 49248 err = JS_GetException(ctx); 49249 is_reject = 1; 49250 done_resolve: 49251 res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 49252 1, (JSValueConst *)&err); 49253 JS_FreeValue(ctx, err); 49254 JS_FreeValue(ctx, res2); 49255 JS_FreeValue(ctx, resolving_funcs[0]); 49256 JS_FreeValue(ctx, resolving_funcs[1]); 49257 return promise; 49258 } 49259 { 49260 JSValue value_wrapper_promise, resolve_reject[2]; 49261 int res; 49262 49263 value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, 49264 1, (JSValueConst *)&value, 0); 49265 if (JS_IsException(value_wrapper_promise)) { 49266 JS_FreeValue(ctx, value); 49267 goto reject; 49268 } 49269 49270 resolve_reject[0] = 49271 js_async_from_sync_iterator_unwrap_func_create(ctx, done); 49272 if (JS_IsException(resolve_reject[0])) { 49273 JS_FreeValue(ctx, value_wrapper_promise); 49274 goto fail; 49275 } 49276 JS_FreeValue(ctx, value); 49277 resolve_reject[1] = JS_UNDEFINED; 49278 49279 res = perform_promise_then(ctx, value_wrapper_promise, 49280 (JSValueConst *)resolve_reject, 49281 (JSValueConst *)resolving_funcs); 49282 JS_FreeValue(ctx, resolve_reject[0]); 49283 JS_FreeValue(ctx, value_wrapper_promise); 49284 JS_FreeValue(ctx, resolving_funcs[0]); 49285 JS_FreeValue(ctx, resolving_funcs[1]); 49286 if (res) { 49287 JS_FreeValue(ctx, promise); 49288 return JS_EXCEPTION; 49289 } 49290 } 49291 return promise; 49292 fail: 49293 JS_FreeValue(ctx, value); 49294 JS_FreeValue(ctx, resolving_funcs[0]); 49295 JS_FreeValue(ctx, resolving_funcs[1]); 49296 JS_FreeValue(ctx, promise); 49297 return JS_EXCEPTION; 49298 } 49299 49300 static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = { 49301 JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ), 49302 JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ), 49303 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ), 49304 }; 49305 49306 /* AsyncGeneratorFunction */ 49307 49308 static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = { 49309 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ), 49310 }; 49311 49312 /* AsyncGenerator prototype */ 49313 49314 static const JSCFunctionListEntry js_async_generator_proto_funcs[] = { 49315 JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ), 49316 JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ), 49317 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ), 49318 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ), 49319 }; 49320 49321 static JSClassShortDef const js_async_class_def[] = { 49322 { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */ 49323 { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */ 49324 { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */ 49325 { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */ 49326 { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */ 49327 { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */ 49328 { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ 49329 { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */ 49330 { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */ 49331 }; 49332 49333 void JS_AddIntrinsicPromise(JSContext *ctx) 49334 { 49335 JSRuntime *rt = ctx->rt; 49336 JSValue obj1; 49337 49338 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) { 49339 init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE, 49340 countof(js_async_class_def)); 49341 rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call; 49342 rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call; 49343 rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call; 49344 rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call; 49345 rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call; 49346 rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call; 49347 } 49348 49349 /* Promise */ 49350 ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx); 49351 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE], 49352 js_promise_proto_funcs, 49353 countof(js_promise_proto_funcs)); 49354 obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1, 49355 JS_CFUNC_constructor, 0); 49356 ctx->promise_ctor = JS_DupValue(ctx, obj1); 49357 JS_SetPropertyFunctionList(ctx, obj1, 49358 js_promise_funcs, 49359 countof(js_promise_funcs)); 49360 JS_NewGlobalCConstructor2(ctx, obj1, "Promise", 49361 ctx->class_proto[JS_CLASS_PROMISE]); 49362 49363 /* AsyncFunction */ 49364 ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); 49365 obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, 49366 "AsyncFunction", 1, 49367 JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC, 49368 ctx->function_ctor); 49369 JS_SetPropertyFunctionList(ctx, 49370 ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], 49371 js_async_function_proto_funcs, 49372 countof(js_async_function_proto_funcs)); 49373 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION], 49374 0, JS_PROP_CONFIGURABLE); 49375 JS_FreeValue(ctx, obj1); 49376 49377 /* AsyncIteratorPrototype */ 49378 ctx->async_iterator_proto = JS_NewObject(ctx); 49379 JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto, 49380 js_async_iterator_proto_funcs, 49381 countof(js_async_iterator_proto_funcs)); 49382 49383 /* AsyncFromSyncIteratorPrototype */ 49384 ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] = 49385 JS_NewObjectProto(ctx, ctx->async_iterator_proto); 49386 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR], 49387 js_async_from_sync_iterator_proto_funcs, 49388 countof(js_async_from_sync_iterator_proto_funcs)); 49389 49390 /* AsyncGeneratorPrototype */ 49391 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] = 49392 JS_NewObjectProto(ctx, ctx->async_iterator_proto); 49393 JS_SetPropertyFunctionList(ctx, 49394 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], 49395 js_async_generator_proto_funcs, 49396 countof(js_async_generator_proto_funcs)); 49397 49398 /* AsyncGeneratorFunction */ 49399 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] = 49400 JS_NewObjectProto(ctx, ctx->function_proto); 49401 obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor, 49402 "AsyncGeneratorFunction", 1, 49403 JS_CFUNC_constructor_or_func_magic, 49404 JS_FUNC_ASYNC_GENERATOR, 49405 ctx->function_ctor); 49406 JS_SetPropertyFunctionList(ctx, 49407 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], 49408 js_async_generator_function_proto_funcs, 49409 countof(js_async_generator_function_proto_funcs)); 49410 JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], 49411 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR], 49412 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); 49413 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION], 49414 0, JS_PROP_CONFIGURABLE); 49415 JS_FreeValue(ctx, obj1); 49416 } 49417 49418 /* URI handling */ 49419 49420 static int string_get_hex(JSString *p, int k, int n) { 49421 int c = 0, h; 49422 while (n-- > 0) { 49423 if ((h = from_hex(string_get(p, k++))) < 0) 49424 return -1; 49425 c = (c << 4) | h; 49426 } 49427 return c; 49428 } 49429 49430 static int isURIReserved(int c) { 49431 return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; 49432 } 49433 49434 static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) 49435 { 49436 va_list ap; 49437 49438 va_start(ap, fmt); 49439 JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap); 49440 va_end(ap); 49441 return -1; 49442 } 49443 49444 static int hex_decode(JSContext *ctx, JSString *p, int k) { 49445 int c; 49446 49447 if (k >= p->len || string_get(p, k) != '%') 49448 return js_throw_URIError(ctx, "expecting %%"); 49449 if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0) 49450 return js_throw_URIError(ctx, "expecting hex digit"); 49451 49452 return c; 49453 } 49454 49455 static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, 49456 int argc, JSValueConst *argv, int isComponent) 49457 { 49458 JSValue str; 49459 StringBuffer b_s, *b = &b_s; 49460 JSString *p; 49461 int k, c, c1, n, c_min; 49462 49463 str = JS_ToString(ctx, argv[0]); 49464 if (JS_IsException(str)) 49465 return str; 49466 49467 string_buffer_init(ctx, b, 0); 49468 49469 p = JS_VALUE_GET_STRING(str); 49470 for (k = 0; k < p->len;) { 49471 c = string_get(p, k); 49472 if (c == '%') { 49473 c = hex_decode(ctx, p, k); 49474 if (c < 0) 49475 goto fail; 49476 k += 3; 49477 if (c < 0x80) { 49478 if (!isComponent && isURIReserved(c)) { 49479 c = '%'; 49480 k -= 2; 49481 } 49482 } else { 49483 /* Decode URI-encoded UTF-8 sequence */ 49484 if (c >= 0xc0 && c <= 0xdf) { 49485 n = 1; 49486 c_min = 0x80; 49487 c &= 0x1f; 49488 } else if (c >= 0xe0 && c <= 0xef) { 49489 n = 2; 49490 c_min = 0x800; 49491 c &= 0xf; 49492 } else if (c >= 0xf0 && c <= 0xf7) { 49493 n = 3; 49494 c_min = 0x10000; 49495 c &= 0x7; 49496 } else { 49497 n = 0; 49498 c_min = 1; 49499 c = 0; 49500 } 49501 while (n-- > 0) { 49502 c1 = hex_decode(ctx, p, k); 49503 if (c1 < 0) 49504 goto fail; 49505 k += 3; 49506 if ((c1 & 0xc0) != 0x80) { 49507 c = 0; 49508 break; 49509 } 49510 c = (c << 6) | (c1 & 0x3f); 49511 } 49512 if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { 49513 js_throw_URIError(ctx, "malformed UTF-8"); 49514 goto fail; 49515 } 49516 } 49517 } else { 49518 k++; 49519 } 49520 string_buffer_putc(b, c); 49521 } 49522 JS_FreeValue(ctx, str); 49523 return string_buffer_end(b); 49524 49525 fail: 49526 JS_FreeValue(ctx, str); 49527 string_buffer_free(b); 49528 return JS_EXCEPTION; 49529 } 49530 49531 static int isUnescaped(int c) { 49532 static char const unescaped_chars[] = 49533 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 49534 "abcdefghijklmnopqrstuvwxyz" 49535 "0123456789" 49536 "@*_+-./"; 49537 return c < 0x100 && 49538 memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1); 49539 } 49540 49541 static int isURIUnescaped(int c, int isComponent) { 49542 return c < 0x100 && 49543 ((c >= 0x61 && c <= 0x7a) || 49544 (c >= 0x41 && c <= 0x5a) || 49545 (c >= 0x30 && c <= 0x39) || 49546 memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL || 49547 (!isComponent && isURIReserved(c))); 49548 } 49549 49550 static int encodeURI_hex(StringBuffer *b, int c) { 49551 uint8_t buf[6]; 49552 int n = 0; 49553 const char *hex = "0123456789ABCDEF"; 49554 49555 buf[n++] = '%'; 49556 if (c >= 256) { 49557 buf[n++] = 'u'; 49558 buf[n++] = hex[(c >> 12) & 15]; 49559 buf[n++] = hex[(c >> 8) & 15]; 49560 } 49561 buf[n++] = hex[(c >> 4) & 15]; 49562 buf[n++] = hex[(c >> 0) & 15]; 49563 return string_buffer_write8(b, buf, n); 49564 } 49565 49566 static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, 49567 int argc, JSValueConst *argv, 49568 int isComponent) 49569 { 49570 JSValue str; 49571 StringBuffer b_s, *b = &b_s; 49572 JSString *p; 49573 int k, c, c1; 49574 49575 str = JS_ToString(ctx, argv[0]); 49576 if (JS_IsException(str)) 49577 return str; 49578 49579 p = JS_VALUE_GET_STRING(str); 49580 string_buffer_init(ctx, b, p->len); 49581 for (k = 0; k < p->len;) { 49582 c = string_get(p, k); 49583 k++; 49584 if (isURIUnescaped(c, isComponent)) { 49585 string_buffer_putc16(b, c); 49586 } else { 49587 if (is_lo_surrogate(c)) { 49588 js_throw_URIError(ctx, "invalid character"); 49589 goto fail; 49590 } else if (is_hi_surrogate(c)) { 49591 if (k >= p->len) { 49592 js_throw_URIError(ctx, "expecting surrogate pair"); 49593 goto fail; 49594 } 49595 c1 = string_get(p, k); 49596 k++; 49597 if (!is_lo_surrogate(c1)) { 49598 js_throw_URIError(ctx, "expecting surrogate pair"); 49599 goto fail; 49600 } 49601 c = from_surrogate(c, c1); 49602 } 49603 if (c < 0x80) { 49604 encodeURI_hex(b, c); 49605 } else { 49606 /* XXX: use C UTF-8 conversion ? */ 49607 if (c < 0x800) { 49608 encodeURI_hex(b, (c >> 6) | 0xc0); 49609 } else { 49610 if (c < 0x10000) { 49611 encodeURI_hex(b, (c >> 12) | 0xe0); 49612 } else { 49613 encodeURI_hex(b, (c >> 18) | 0xf0); 49614 encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80); 49615 } 49616 encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80); 49617 } 49618 encodeURI_hex(b, (c & 0x3f) | 0x80); 49619 } 49620 } 49621 } 49622 JS_FreeValue(ctx, str); 49623 return string_buffer_end(b); 49624 49625 fail: 49626 JS_FreeValue(ctx, str); 49627 string_buffer_free(b); 49628 return JS_EXCEPTION; 49629 } 49630 49631 static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val, 49632 int argc, JSValueConst *argv) 49633 { 49634 JSValue str; 49635 StringBuffer b_s, *b = &b_s; 49636 JSString *p; 49637 int i, len, c; 49638 49639 str = JS_ToString(ctx, argv[0]); 49640 if (JS_IsException(str)) 49641 return str; 49642 49643 p = JS_VALUE_GET_STRING(str); 49644 string_buffer_init(ctx, b, p->len); 49645 for (i = 0, len = p->len; i < len; i++) { 49646 c = string_get(p, i); 49647 if (isUnescaped(c)) { 49648 string_buffer_putc16(b, c); 49649 } else { 49650 encodeURI_hex(b, c); 49651 } 49652 } 49653 JS_FreeValue(ctx, str); 49654 return string_buffer_end(b); 49655 } 49656 49657 static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val, 49658 int argc, JSValueConst *argv) 49659 { 49660 JSValue str; 49661 StringBuffer b_s, *b = &b_s; 49662 JSString *p; 49663 int i, len, c, n; 49664 49665 str = JS_ToString(ctx, argv[0]); 49666 if (JS_IsException(str)) 49667 return str; 49668 49669 string_buffer_init(ctx, b, 0); 49670 p = JS_VALUE_GET_STRING(str); 49671 for (i = 0, len = p->len; i < len; i++) { 49672 c = string_get(p, i); 49673 if (c == '%') { 49674 if (i + 6 <= len 49675 && string_get(p, i + 1) == 'u' 49676 && (n = string_get_hex(p, i + 2, 4)) >= 0) { 49677 c = n; 49678 i += 6 - 1; 49679 } else 49680 if (i + 3 <= len 49681 && (n = string_get_hex(p, i + 1, 2)) >= 0) { 49682 c = n; 49683 i += 3 - 1; 49684 } 49685 } 49686 string_buffer_putc16(b, c); 49687 } 49688 JS_FreeValue(ctx, str); 49689 return string_buffer_end(b); 49690 } 49691 49692 /* global object */ 49693 49694 static const JSCFunctionListEntry js_global_funcs[] = { 49695 JS_CFUNC_DEF("parseInt", 2, js_parseInt ), 49696 JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ), 49697 JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ), 49698 JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ), 49699 49700 JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ), 49701 JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ), 49702 JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ), 49703 JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ), 49704 JS_CFUNC_DEF("escape", 1, js_global_escape ), 49705 JS_CFUNC_DEF("unescape", 1, js_global_unescape ), 49706 JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), 49707 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), 49708 JS_PROP_UNDEFINED_DEF("undefined", 0 ), 49709 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), 49710 }; 49711 49712 /* Date */ 49713 49714 static int64_t math_mod(int64_t a, int64_t b) { 49715 /* return positive modulo */ 49716 int64_t m = a % b; 49717 return m + (m < 0) * b; 49718 } 49719 49720 static int64_t floor_div(int64_t a, int64_t b) { 49721 /* integer division rounding toward -Infinity */ 49722 int64_t m = a % b; 49723 return (a - (m + (m < 0) * b)) / b; 49724 } 49725 49726 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, 49727 int argc, JSValueConst *argv); 49728 49729 static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) 49730 { 49731 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 49732 JSObject *p = JS_VALUE_GET_OBJ(this_val); 49733 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) 49734 return JS_ToFloat64(ctx, valp, p->u.object_data); 49735 } 49736 JS_ThrowTypeError(ctx, "not a Date object"); 49737 return -1; 49738 } 49739 49740 static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v) 49741 { 49742 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 49743 JSObject *p = JS_VALUE_GET_OBJ(this_val); 49744 if (p->class_id == JS_CLASS_DATE) { 49745 JS_FreeValue(ctx, p->u.object_data); 49746 p->u.object_data = JS_NewFloat64(ctx, v); 49747 return JS_DupValue(ctx, p->u.object_data); 49748 } 49749 } 49750 return JS_ThrowTypeError(ctx, "not a Date object"); 49751 } 49752 49753 static int64_t days_from_year(int64_t y) { 49754 return 365 * (y - 1970) + floor_div(y - 1969, 4) - 49755 floor_div(y - 1901, 100) + floor_div(y - 1601, 400); 49756 } 49757 49758 static int64_t days_in_year(int64_t y) { 49759 return 365 + !(y % 4) - !(y % 100) + !(y % 400); 49760 } 49761 49762 /* return the year, update days */ 49763 static int64_t year_from_days(int64_t *days) { 49764 int64_t y, d1, nd, d = *days; 49765 y = floor_div(d * 10000, 3652425) + 1970; 49766 /* the initial approximation is very good, so only a few 49767 iterations are necessary */ 49768 for(;;) { 49769 d1 = d - days_from_year(y); 49770 if (d1 < 0) { 49771 y--; 49772 d1 += days_in_year(y); 49773 } else { 49774 nd = days_in_year(y); 49775 if (d1 < nd) 49776 break; 49777 d1 -= nd; 49778 y++; 49779 } 49780 } 49781 *days = d1; 49782 return y; 49783 } 49784 49785 static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 49786 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; 49787 static char const day_names[] = "SunMonTueWedThuFriSat"; 49788 49789 static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, 49790 double fields[minimum_length(9)], int is_local, int force) 49791 { 49792 double dval; 49793 int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; 49794 49795 if (JS_ThisTimeValue(ctx, &dval, obj)) 49796 return -1; 49797 49798 if (isnan(dval)) { 49799 if (!force) 49800 return FALSE; /* NaN */ 49801 d = 0; /* initialize all fields to 0 */ 49802 } else { 49803 d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ 49804 if (is_local) { 49805 tz = -getTimezoneOffset(d); 49806 d += tz * 60000; 49807 } 49808 } 49809 49810 /* result is >= 0, we can use % */ 49811 h = math_mod(d, 86400000); 49812 days = (d - h) / 86400000; 49813 ms = h % 1000; 49814 h = (h - ms) / 1000; 49815 s = h % 60; 49816 h = (h - s) / 60; 49817 m = h % 60; 49818 h = (h - m) / 60; 49819 wd = math_mod(days + 4, 7); /* week day */ 49820 y = year_from_days(&days); 49821 49822 for(i = 0; i < 11; i++) { 49823 md = month_days[i]; 49824 if (i == 1) 49825 md += days_in_year(y) - 365; 49826 if (days < md) 49827 break; 49828 days -= md; 49829 } 49830 fields[0] = y; 49831 fields[1] = i; 49832 fields[2] = days + 1; 49833 fields[3] = h; 49834 fields[4] = m; 49835 fields[5] = s; 49836 fields[6] = ms; 49837 fields[7] = wd; 49838 fields[8] = tz; 49839 return TRUE; 49840 } 49841 49842 static double time_clip(double t) { 49843 if (t >= -8.64e15 && t <= 8.64e15) 49844 return trunc(t) + 0.0; /* convert -0 to +0 */ 49845 else 49846 return NAN; 49847 } 49848 49849 /* The spec mandates the use of 'double' and it specifies the order 49850 of the operations */ 49851 static double set_date_fields(double fields[minimum_length(7)], int is_local) { 49852 double y, m, dt, ym, mn, day, h, s, milli, time, tv; 49853 int yi, mi, i; 49854 int64_t days; 49855 volatile double temp; /* enforce evaluation order */ 49856 49857 /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ 49858 y = fields[0]; 49859 m = fields[1]; 49860 dt = fields[2]; 49861 ym = y + floor(m / 12); 49862 mn = fmod(m, 12); 49863 if (mn < 0) 49864 mn += 12; 49865 if (ym < -271821 || ym > 275760) 49866 return NAN; 49867 49868 yi = ym; 49869 mi = mn; 49870 days = days_from_year(yi); 49871 for(i = 0; i < mi; i++) { 49872 days += month_days[i]; 49873 if (i == 1) 49874 days += days_in_year(yi) - 365; 49875 } 49876 day = days + dt - 1; 49877 49878 /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ 49879 h = fields[3]; 49880 m = fields[4]; 49881 s = fields[5]; 49882 milli = fields[6]; 49883 /* Use a volatile intermediary variable to ensure order of evaluation 49884 * as specified in ECMA. This fixes a test262 error on 49885 * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. 49886 * Without the volatile qualifier, the compile can generate code 49887 * that performs the computation in a different order or with instructions 49888 * that produce a different result such as FMA (float multiply and add). 49889 */ 49890 time = h * 3600000; 49891 time += (temp = m * 60000); 49892 time += (temp = s * 1000); 49893 time += milli; 49894 49895 /* emulate 21.4.1.16 MakeDate ( day, time ) */ 49896 tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ 49897 if (!isfinite(tv)) 49898 return NAN; 49899 49900 /* adjust for local time and clip */ 49901 if (is_local) { 49902 int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; 49903 tv += getTimezoneOffset(ti) * 60000; 49904 } 49905 return time_clip(tv); 49906 } 49907 49908 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, 49909 int argc, JSValueConst *argv, int magic) 49910 { 49911 // get_date_field(obj, n, is_local) 49912 double fields[9]; 49913 int res, n, is_local; 49914 49915 is_local = magic & 0x0F; 49916 n = (magic >> 4) & 0x0F; 49917 res = get_date_fields(ctx, this_val, fields, is_local, 0); 49918 if (res < 0) 49919 return JS_EXCEPTION; 49920 if (!res) 49921 return JS_NAN; 49922 49923 if (magic & 0x100) { // getYear 49924 fields[0] -= 1900; 49925 } 49926 return JS_NewFloat64(ctx, fields[n]); 49927 } 49928 49929 static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, 49930 int argc, JSValueConst *argv, int magic) 49931 { 49932 // _field(obj, first_field, end_field, args, is_local) 49933 double fields[9]; 49934 int res, first_field, end_field, is_local, i, n; 49935 double d, a; 49936 49937 d = NAN; 49938 first_field = (magic >> 8) & 0x0F; 49939 end_field = (magic >> 4) & 0x0F; 49940 is_local = magic & 0x0F; 49941 49942 res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); 49943 if (res < 0) 49944 return JS_EXCEPTION; 49945 49946 // Argument coercion is observable and must be done unconditionally. 49947 n = min_int(argc, end_field - first_field); 49948 for(i = 0; i < n; i++) { 49949 if (JS_ToFloat64(ctx, &a, argv[i])) 49950 return JS_EXCEPTION; 49951 if (!isfinite(a)) 49952 res = FALSE; 49953 fields[first_field + i] = trunc(a); 49954 } 49955 if (res && argc > 0) 49956 d = set_date_fields(fields, is_local); 49957 49958 return JS_SetThisTimeValue(ctx, this_val, d); 49959 } 49960 49961 /* fmt: 49962 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT" 49963 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)" 49964 2: toISOString: "2018-01-02T23:02:56.927Z" 49965 3: toLocaleString: "1/2/2018, 11:40:40 PM" 49966 part: 1=date, 2=time 3=all 49967 XXX: should use a variant of strftime(). 49968 */ 49969 static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, 49970 int argc, JSValueConst *argv, int magic) 49971 { 49972 // _string(obj, fmt, part) 49973 char buf[64]; 49974 double fields[9]; 49975 int res, fmt, part, pos; 49976 int y, mon, d, h, m, s, ms, wd, tz; 49977 49978 fmt = (magic >> 4) & 0x0F; 49979 part = magic & 0x0F; 49980 49981 res = get_date_fields(ctx, this_val, fields, fmt & 1, 0); 49982 if (res < 0) 49983 return JS_EXCEPTION; 49984 if (!res) { 49985 if (fmt == 2) 49986 return JS_ThrowRangeError(ctx, "Date value is NaN"); 49987 else 49988 return JS_NewString(ctx, "Invalid Date"); 49989 } 49990 49991 y = fields[0]; 49992 mon = fields[1]; 49993 d = fields[2]; 49994 h = fields[3]; 49995 m = fields[4]; 49996 s = fields[5]; 49997 ms = fields[6]; 49998 wd = fields[7]; 49999 tz = fields[8]; 50000 50001 pos = 0; 50002 50003 if (part & 1) { /* date part */ 50004 switch(fmt) { 50005 case 0: 50006 pos += snprintf(buf + pos, sizeof(buf) - pos, 50007 "%.3s, %02d %.3s %0*d ", 50008 day_names + wd * 3, d, 50009 month_names + mon * 3, 4 + (y < 0), y); 50010 break; 50011 case 1: 50012 pos += snprintf(buf + pos, sizeof(buf) - pos, 50013 "%.3s %.3s %02d %0*d", 50014 day_names + wd * 3, 50015 month_names + mon * 3, d, 4 + (y < 0), y); 50016 if (part == 3) { 50017 buf[pos++] = ' '; 50018 } 50019 break; 50020 case 2: 50021 if (y >= 0 && y <= 9999) { 50022 pos += snprintf(buf + pos, sizeof(buf) - pos, 50023 "%04d", y); 50024 } else { 50025 pos += snprintf(buf + pos, sizeof(buf) - pos, 50026 "%+07d", y); 50027 } 50028 pos += snprintf(buf + pos, sizeof(buf) - pos, 50029 "-%02d-%02dT", mon + 1, d); 50030 break; 50031 case 3: 50032 pos += snprintf(buf + pos, sizeof(buf) - pos, 50033 "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y); 50034 if (part == 3) { 50035 buf[pos++] = ','; 50036 buf[pos++] = ' '; 50037 } 50038 break; 50039 } 50040 } 50041 if (part & 2) { /* time part */ 50042 switch(fmt) { 50043 case 0: 50044 pos += snprintf(buf + pos, sizeof(buf) - pos, 50045 "%02d:%02d:%02d GMT", h, m, s); 50046 break; 50047 case 1: 50048 pos += snprintf(buf + pos, sizeof(buf) - pos, 50049 "%02d:%02d:%02d GMT", h, m, s); 50050 if (tz < 0) { 50051 buf[pos++] = '-'; 50052 tz = -tz; 50053 } else { 50054 buf[pos++] = '+'; 50055 } 50056 /* tz is >= 0, can use % */ 50057 pos += snprintf(buf + pos, sizeof(buf) - pos, 50058 "%02d%02d", tz / 60, tz % 60); 50059 /* XXX: tack the time zone code? */ 50060 break; 50061 case 2: 50062 pos += snprintf(buf + pos, sizeof(buf) - pos, 50063 "%02d:%02d:%02d.%03dZ", h, m, s, ms); 50064 break; 50065 case 3: 50066 pos += snprintf(buf + pos, sizeof(buf) - pos, 50067 "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, 50068 (h < 12) ? 'A' : 'P'); 50069 break; 50070 } 50071 } 50072 return JS_NewStringLen(ctx, buf, pos); 50073 } 50074 50075 /* OS dependent: return the UTC time in ms since 1970. */ 50076 static int64_t date_now(void) { 50077 struct timeval tv; 50078 gettimeofday(&tv, NULL); 50079 return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); 50080 } 50081 50082 static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, 50083 int argc, JSValueConst *argv) 50084 { 50085 // Date(y, mon, d, h, m, s, ms) 50086 JSValue rv; 50087 int i, n; 50088 double a, val; 50089 50090 if (JS_IsUndefined(new_target)) { 50091 /* invoked as function */ 50092 argc = 0; 50093 } 50094 n = argc; 50095 if (n == 0) { 50096 val = date_now(); 50097 } else if (n == 1) { 50098 JSValue v, dv; 50099 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { 50100 JSObject *p = JS_VALUE_GET_OBJ(argv[0]); 50101 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) { 50102 if (JS_ToFloat64(ctx, &val, p->u.object_data)) 50103 return JS_EXCEPTION; 50104 val = time_clip(val); 50105 goto has_val; 50106 } 50107 } 50108 v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); 50109 if (JS_IsString(v)) { 50110 dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); 50111 JS_FreeValue(ctx, v); 50112 if (JS_IsException(dv)) 50113 return JS_EXCEPTION; 50114 if (JS_ToFloat64Free(ctx, &val, dv)) 50115 return JS_EXCEPTION; 50116 } else { 50117 if (JS_ToFloat64Free(ctx, &val, v)) 50118 return JS_EXCEPTION; 50119 } 50120 val = time_clip(val); 50121 } else { 50122 double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; 50123 if (n > 7) 50124 n = 7; 50125 for(i = 0; i < n; i++) { 50126 if (JS_ToFloat64(ctx, &a, argv[i])) 50127 return JS_EXCEPTION; 50128 if (!isfinite(a)) 50129 break; 50130 fields[i] = trunc(a); 50131 if (i == 0 && fields[0] >= 0 && fields[0] < 100) 50132 fields[0] += 1900; 50133 } 50134 val = (i == n) ? set_date_fields(fields, 1) : NAN; 50135 } 50136 has_val: 50137 #if 0 50138 JSValueConst args[3]; 50139 args[0] = new_target; 50140 args[1] = ctx->class_proto[JS_CLASS_DATE]; 50141 args[2] = JS_NewFloat64(ctx, val); 50142 rv = js___date_create(ctx, JS_UNDEFINED, 3, args); 50143 #else 50144 rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE); 50145 if (!JS_IsException(rv)) 50146 JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val)); 50147 #endif 50148 if (!JS_IsException(rv) && JS_IsUndefined(new_target)) { 50149 /* invoked as a function, return (new Date()).toString(); */ 50150 JSValue s; 50151 s = get_date_string(ctx, rv, 0, NULL, 0x13); 50152 JS_FreeValue(ctx, rv); 50153 rv = s; 50154 } 50155 return rv; 50156 } 50157 50158 static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, 50159 int argc, JSValueConst *argv) 50160 { 50161 // UTC(y, mon, d, h, m, s, ms) 50162 double fields[] = { 0, 0, 1, 0, 0, 0, 0 }; 50163 int i, n; 50164 double a; 50165 50166 n = argc; 50167 if (n == 0) 50168 return JS_NAN; 50169 if (n > 7) 50170 n = 7; 50171 for(i = 0; i < n; i++) { 50172 if (JS_ToFloat64(ctx, &a, argv[i])) 50173 return JS_EXCEPTION; 50174 if (!isfinite(a)) 50175 return JS_NAN; 50176 fields[i] = trunc(a); 50177 if (i == 0 && fields[0] >= 0 && fields[0] < 100) 50178 fields[0] += 1900; 50179 } 50180 return JS_NewFloat64(ctx, set_date_fields(fields, 0)); 50181 } 50182 50183 /* Date string parsing */ 50184 50185 static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { 50186 if (sp[*pp] == c) { 50187 *pp += 1; 50188 return TRUE; 50189 } else { 50190 return FALSE; 50191 } 50192 } 50193 50194 /* skip spaces, update offset, return next char */ 50195 static int string_skip_spaces(const uint8_t *sp, int *pp) { 50196 int c; 50197 while ((c = sp[*pp]) == ' ') 50198 *pp += 1; 50199 return c; 50200 } 50201 50202 /* skip dashes dots and commas */ 50203 static int string_skip_separators(const uint8_t *sp, int *pp) { 50204 int c; 50205 while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') 50206 *pp += 1; 50207 return c; 50208 } 50209 50210 /* skip a word, stop on spaces, digits and separators, update offset */ 50211 static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) { 50212 int c; 50213 while (!strchr(stoplist, c = sp[*pp])) 50214 *pp += 1; 50215 return c; 50216 } 50217 50218 /* parse a numeric field (max_digits = 0 -> no maximum) */ 50219 static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, 50220 int min_digits, int max_digits) 50221 { 50222 int v = 0; 50223 int c, p = *pp, p_start; 50224 50225 p_start = p; 50226 while ((c = sp[p]) >= '0' && c <= '9') { 50227 v = v * 10 + c - '0'; 50228 p++; 50229 if (p - p_start == max_digits) 50230 break; 50231 } 50232 if (p - p_start < min_digits) 50233 return FALSE; 50234 *pval = v; 50235 *pp = p; 50236 return TRUE; 50237 } 50238 50239 static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) { 50240 /* parse optional fractional part as milliseconds and truncate. */ 50241 /* spec does not indicate which rounding should be used */ 50242 int mul = 100, ms = 0, c, p_start, p = *pp; 50243 50244 c = sp[p]; 50245 if (c == '.' || c == ',') { 50246 p++; 50247 p_start = p; 50248 while ((c = sp[p]) >= '0' && c <= '9') { 50249 ms += (c - '0') * mul; 50250 mul /= 10; 50251 p++; 50252 if (p - p_start == 9) 50253 break; 50254 } 50255 if (p > p_start) { 50256 /* only consume the separator if digits are present */ 50257 *pval = ms; 50258 *pp = p; 50259 } 50260 } 50261 return TRUE; 50262 } 50263 50264 static uint8_t upper_ascii(uint8_t c) { 50265 return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; 50266 } 50267 50268 static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) { 50269 int tz = 0, sgn, hh, mm, p = *pp; 50270 50271 sgn = sp[p++]; 50272 if (sgn == '+' || sgn == '-') { 50273 int n = p; 50274 if (!string_get_digits(sp, &p, &hh, 1, 9)) 50275 return FALSE; 50276 n = p - n; 50277 if (strict && n != 2 && n != 4) 50278 return FALSE; 50279 while (n > 4) { 50280 n -= 2; 50281 hh /= 100; 50282 } 50283 if (n > 2) { 50284 mm = hh % 100; 50285 hh = hh / 100; 50286 } else { 50287 mm = 0; 50288 if (string_skip_char(sp, &p, ':') /* optional separator */ 50289 && !string_get_digits(sp, &p, &mm, 2, 2)) 50290 return FALSE; 50291 } 50292 if (hh > 23 || mm > 59) 50293 return FALSE; 50294 tz = hh * 60 + mm; 50295 if (sgn != '+') 50296 tz = -tz; 50297 } else 50298 if (sgn != 'Z') { 50299 return FALSE; 50300 } 50301 *pp = p; 50302 *tzp = tz; 50303 return TRUE; 50304 } 50305 50306 static BOOL string_match(const uint8_t *sp, int *pp, const char *s) { 50307 int p = *pp; 50308 while (*s != '\0') { 50309 if (upper_ascii(sp[p]) != upper_ascii(*s++)) 50310 return FALSE; 50311 p++; 50312 } 50313 *pp = p; 50314 return TRUE; 50315 } 50316 50317 static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { 50318 int n, i; 50319 50320 for (n = 0; n < count; n++) { 50321 for (i = 0;; i++) { 50322 if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i])) 50323 break; 50324 if (i == 2) 50325 return n; 50326 } 50327 } 50328 return -1; 50329 } 50330 50331 static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { 50332 int n; 50333 50334 n = find_abbrev(sp, *pp, month_names, 12); 50335 if (n < 0) 50336 return FALSE; 50337 50338 *pval = n + 1; 50339 *pp += 3; 50340 return TRUE; 50341 } 50342 50343 /* parse toISOString format */ 50344 static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { 50345 int sgn, i, p = 0; 50346 50347 /* initialize fields to the beginning of the Epoch */ 50348 for (i = 0; i < 9; i++) { 50349 fields[i] = (i == 2); 50350 } 50351 *is_local = FALSE; 50352 50353 /* year is either yyyy digits or [+-]yyyyyy */ 50354 sgn = sp[p]; 50355 if (sgn == '-' || sgn == '+') { 50356 p++; 50357 if (!string_get_digits(sp, &p, &fields[0], 6, 6)) 50358 return FALSE; 50359 if (sgn == '-') { 50360 if (fields[0] == 0) 50361 return FALSE; // reject -000000 50362 fields[0] = -fields[0]; 50363 } 50364 } else { 50365 if (!string_get_digits(sp, &p, &fields[0], 4, 4)) 50366 return FALSE; 50367 } 50368 if (string_skip_char(sp, &p, '-')) { 50369 if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */ 50370 return FALSE; 50371 if (fields[1] < 1) 50372 return FALSE; 50373 fields[1] -= 1; 50374 if (string_skip_char(sp, &p, '-')) { 50375 if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */ 50376 return FALSE; 50377 if (fields[2] < 1) 50378 return FALSE; 50379 } 50380 } 50381 if (string_skip_char(sp, &p, 'T')) { 50382 *is_local = TRUE; 50383 if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */ 50384 || !string_skip_char(sp, &p, ':') 50385 || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */ 50386 fields[3] = 100; // reject unconditionally 50387 return TRUE; 50388 } 50389 if (string_skip_char(sp, &p, ':')) { 50390 if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */ 50391 return FALSE; 50392 string_get_milliseconds(sp, &p, &fields[6]); 50393 } 50394 } 50395 /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ 50396 if (sp[p]) { 50397 *is_local = FALSE; 50398 if (!string_get_tzoffset(sp, &p, &fields[8], TRUE)) 50399 return FALSE; 50400 } 50401 /* error if extraneous characters */ 50402 return sp[p] == '\0'; 50403 } 50404 50405 static struct { 50406 char name[6]; 50407 int16_t offset; 50408 } const js_tzabbr[] = { 50409 { "GMT", 0 }, // Greenwich Mean Time 50410 { "UTC", 0 }, // Coordinated Universal Time 50411 { "UT", 0 }, // Universal Time 50412 { "Z", 0 }, // Zulu Time 50413 { "EDT", -4 * 60 }, // Eastern Daylight Time 50414 { "EST", -5 * 60 }, // Eastern Standard Time 50415 { "CDT", -5 * 60 }, // Central Daylight Time 50416 { "CST", -6 * 60 }, // Central Standard Time 50417 { "MDT", -6 * 60 }, // Mountain Daylight Time 50418 { "MST", -7 * 60 }, // Mountain Standard Time 50419 { "PDT", -7 * 60 }, // Pacific Daylight Time 50420 { "PST", -8 * 60 }, // Pacific Standard Time 50421 { "WET", +0 * 60 }, // Western European Time 50422 { "WEST", +1 * 60 }, // Western European Summer Time 50423 { "CET", +1 * 60 }, // Central European Time 50424 { "CEST", +2 * 60 }, // Central European Summer Time 50425 { "EET", +2 * 60 }, // Eastern European Time 50426 { "EEST", +3 * 60 }, // Eastern European Summer Time 50427 }; 50428 50429 static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) { 50430 for (size_t i = 0; i < countof(js_tzabbr); i++) { 50431 if (string_match(sp, pp, js_tzabbr[i].name)) { 50432 *offset = js_tzabbr[i].offset; 50433 return TRUE; 50434 } 50435 } 50436 return FALSE; 50437 } 50438 50439 /* parse toString, toUTCString and other formats */ 50440 static BOOL js_date_parse_otherstring(const uint8_t *sp, 50441 int fields[minimum_length(9)], 50442 BOOL *is_local) { 50443 int c, i, val, p = 0, p_start; 50444 int num[3]; 50445 BOOL has_year = FALSE; 50446 BOOL has_mon = FALSE; 50447 BOOL has_time = FALSE; 50448 int num_index = 0; 50449 50450 /* initialize fields to the beginning of 2001-01-01 */ 50451 fields[0] = 2001; 50452 fields[1] = 1; 50453 fields[2] = 1; 50454 for (i = 3; i < 9; i++) { 50455 fields[i] = 0; 50456 } 50457 *is_local = TRUE; 50458 50459 while (string_skip_spaces(sp, &p)) { 50460 p_start = p; 50461 if ((c = sp[p]) == '+' || c == '-') { 50462 if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) { 50463 *is_local = FALSE; 50464 } else { 50465 p++; 50466 if (string_get_digits(sp, &p, &val, 1, 9)) { 50467 if (c == '-') { 50468 if (val == 0) 50469 return FALSE; 50470 val = -val; 50471 } 50472 fields[0] = val; 50473 has_year = TRUE; 50474 } 50475 } 50476 } else 50477 if (string_get_digits(sp, &p, &val, 1, 9)) { 50478 if (string_skip_char(sp, &p, ':')) { 50479 /* time part */ 50480 fields[3] = val; 50481 if (!string_get_digits(sp, &p, &fields[4], 1, 2)) 50482 return FALSE; 50483 if (string_skip_char(sp, &p, ':')) { 50484 if (!string_get_digits(sp, &p, &fields[5], 1, 2)) 50485 return FALSE; 50486 string_get_milliseconds(sp, &p, &fields[6]); 50487 } 50488 has_time = TRUE; 50489 } else { 50490 if (p - p_start > 2) { 50491 fields[0] = val; 50492 has_year = TRUE; 50493 } else 50494 if (val < 1 || val > 31) { 50495 fields[0] = val + (val < 100) * 1900 + (val < 50) * 100; 50496 has_year = TRUE; 50497 } else { 50498 if (num_index == 3) 50499 return FALSE; 50500 num[num_index++] = val; 50501 } 50502 } 50503 } else 50504 if (string_get_month(sp, &p, &fields[1])) { 50505 has_mon = TRUE; 50506 string_skip_until(sp, &p, "0123456789 -/("); 50507 } else 50508 if (has_time && string_match(sp, &p, "PM")) { 50509 if (fields[3] < 12) 50510 fields[3] += 12; 50511 continue; 50512 } else 50513 if (has_time && string_match(sp, &p, "AM")) { 50514 if (fields[3] == 12) 50515 fields[3] -= 12; 50516 continue; 50517 } else 50518 if (string_get_tzabbr(sp, &p, &fields[8])) { 50519 *is_local = FALSE; 50520 continue; 50521 } else 50522 if (c == '(') { /* skip parenthesized phrase */ 50523 int level = 0; 50524 while ((c = sp[p]) != '\0') { 50525 p++; 50526 level += (c == '('); 50527 level -= (c == ')'); 50528 if (!level) 50529 break; 50530 } 50531 if (level > 0) 50532 return FALSE; 50533 } else 50534 if (c == ')') { 50535 return FALSE; 50536 } else { 50537 if (has_year + has_mon + has_time + num_index) 50538 return FALSE; 50539 /* skip a word */ 50540 string_skip_until(sp, &p, " -/("); 50541 } 50542 string_skip_separators(sp, &p); 50543 } 50544 if (num_index + has_year + has_mon > 3) 50545 return FALSE; 50546 50547 switch (num_index) { 50548 case 0: 50549 if (!has_year) 50550 return FALSE; 50551 break; 50552 case 1: 50553 if (has_mon) 50554 fields[2] = num[0]; 50555 else 50556 fields[1] = num[0]; 50557 break; 50558 case 2: 50559 if (has_year) { 50560 fields[1] = num[0]; 50561 fields[2] = num[1]; 50562 } else 50563 if (has_mon) { 50564 fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100; 50565 fields[2] = num[0]; 50566 } else { 50567 fields[1] = num[0]; 50568 fields[2] = num[1]; 50569 } 50570 break; 50571 case 3: 50572 fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; 50573 fields[1] = num[0]; 50574 fields[2] = num[1]; 50575 break; 50576 default: 50577 return FALSE; 50578 } 50579 if (fields[1] < 1 || fields[2] < 1) 50580 return FALSE; 50581 fields[1] -= 1; 50582 return TRUE; 50583 } 50584 50585 static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, 50586 int argc, JSValueConst *argv) 50587 { 50588 JSValue s, rv; 50589 int fields[9]; 50590 double fields1[9]; 50591 double d; 50592 int i, c; 50593 JSString *sp; 50594 uint8_t buf[128]; 50595 BOOL is_local; 50596 50597 rv = JS_NAN; 50598 50599 s = JS_ToString(ctx, argv[0]); 50600 if (JS_IsException(s)) 50601 return JS_EXCEPTION; 50602 50603 sp = JS_VALUE_GET_STRING(s); 50604 /* convert the string as a byte array */ 50605 for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) { 50606 c = string_get(sp, i); 50607 if (c > 255) 50608 c = (c == 0x2212) ? '-' : 'x'; 50609 buf[i] = c; 50610 } 50611 buf[i] = '\0'; 50612 if (js_date_parse_isostring(buf, fields, &is_local) 50613 || js_date_parse_otherstring(buf, fields, &is_local)) { 50614 static int const field_max[6] = { 0, 11, 31, 24, 59, 59 }; 50615 BOOL valid = TRUE; 50616 /* check field maximum values */ 50617 for (i = 1; i < 6; i++) { 50618 if (fields[i] > field_max[i]) 50619 valid = FALSE; 50620 } 50621 /* special case 24:00:00.000 */ 50622 if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) 50623 valid = FALSE; 50624 if (valid) { 50625 for(i = 0; i < 7; i++) 50626 fields1[i] = fields[i]; 50627 d = set_date_fields(fields1, is_local) - fields[8] * 60000; 50628 rv = JS_NewFloat64(ctx, d); 50629 } 50630 } 50631 JS_FreeValue(ctx, s); 50632 return rv; 50633 } 50634 50635 static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val, 50636 int argc, JSValueConst *argv) 50637 { 50638 // now() 50639 return JS_NewInt64(ctx, date_now()); 50640 } 50641 50642 static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, 50643 int argc, JSValueConst *argv) 50644 { 50645 // Symbol_toPrimitive(hint) 50646 JSValueConst obj = this_val; 50647 JSAtom hint = JS_ATOM_NULL; 50648 int hint_num; 50649 50650 if (!JS_IsObject(obj)) 50651 return JS_ThrowTypeErrorNotAnObject(ctx); 50652 50653 if (JS_IsString(argv[0])) { 50654 hint = JS_ValueToAtom(ctx, argv[0]); 50655 if (hint == JS_ATOM_NULL) 50656 return JS_EXCEPTION; 50657 JS_FreeAtom(ctx, hint); 50658 } 50659 switch (hint) { 50660 case JS_ATOM_number: 50661 case JS_ATOM_integer: 50662 hint_num = HINT_NUMBER; 50663 break; 50664 case JS_ATOM_string: 50665 case JS_ATOM_default: 50666 hint_num = HINT_STRING; 50667 break; 50668 default: 50669 return JS_ThrowTypeError(ctx, "invalid hint"); 50670 } 50671 return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY); 50672 } 50673 50674 static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, 50675 int argc, JSValueConst *argv) 50676 { 50677 // getTimezoneOffset() 50678 double v; 50679 50680 if (JS_ThisTimeValue(ctx, &v, this_val)) 50681 return JS_EXCEPTION; 50682 if (isnan(v)) 50683 return JS_NAN; 50684 else 50685 /* assuming -8.64e15 <= v <= -8.64e15 */ 50686 return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); 50687 } 50688 50689 static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val, 50690 int argc, JSValueConst *argv) 50691 { 50692 // getTime() 50693 double v; 50694 50695 if (JS_ThisTimeValue(ctx, &v, this_val)) 50696 return JS_EXCEPTION; 50697 return JS_NewFloat64(ctx, v); 50698 } 50699 50700 static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val, 50701 int argc, JSValueConst *argv) 50702 { 50703 // setTime(v) 50704 double v; 50705 50706 if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0])) 50707 return JS_EXCEPTION; 50708 return JS_SetThisTimeValue(ctx, this_val, time_clip(v)); 50709 } 50710 50711 static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val, 50712 int argc, JSValueConst *argv) 50713 { 50714 // setYear(y) 50715 double y; 50716 JSValueConst args[1]; 50717 50718 if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0])) 50719 return JS_EXCEPTION; 50720 y = +y; 50721 if (isfinite(y)) { 50722 y = trunc(y); 50723 if (y >= 0 && y < 100) 50724 y += 1900; 50725 } 50726 args[0] = JS_NewFloat64(ctx, y); 50727 return set_date_field(ctx, this_val, 1, args, 0x011); 50728 } 50729 50730 static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val, 50731 int argc, JSValueConst *argv) 50732 { 50733 // toJSON(key) 50734 JSValue obj, tv, method, rv; 50735 double d; 50736 50737 rv = JS_EXCEPTION; 50738 tv = JS_UNDEFINED; 50739 50740 obj = JS_ToObject(ctx, this_val); 50741 tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER); 50742 if (JS_IsException(tv)) 50743 goto exception; 50744 if (JS_IsNumber(tv)) { 50745 if (JS_ToFloat64(ctx, &d, tv) < 0) 50746 goto exception; 50747 if (!isfinite(d)) { 50748 rv = JS_NULL; 50749 goto done; 50750 } 50751 } 50752 method = JS_GetPropertyStr(ctx, obj, "toISOString"); 50753 if (JS_IsException(method)) 50754 goto exception; 50755 if (!JS_IsFunction(ctx, method)) { 50756 JS_ThrowTypeError(ctx, "object needs toISOString method"); 50757 JS_FreeValue(ctx, method); 50758 goto exception; 50759 } 50760 rv = JS_CallFree(ctx, method, obj, 0, NULL); 50761 exception: 50762 done: 50763 JS_FreeValue(ctx, obj); 50764 JS_FreeValue(ctx, tv); 50765 return rv; 50766 } 50767 50768 static const JSCFunctionListEntry js_date_funcs[] = { 50769 JS_CFUNC_DEF("now", 0, js_Date_now ), 50770 JS_CFUNC_DEF("parse", 1, js_Date_parse ), 50771 JS_CFUNC_DEF("UTC", 7, js_Date_UTC ), 50772 }; 50773 50774 static const JSCFunctionListEntry js_date_proto_funcs[] = { 50775 JS_CFUNC_DEF("valueOf", 0, js_date_getTime ), 50776 JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ), 50777 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ), 50778 JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ), 50779 JS_ALIAS_DEF("toGMTString", "toUTCString" ), 50780 JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ), 50781 JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ), 50782 JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ), 50783 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ), 50784 JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ), 50785 JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ), 50786 JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ), 50787 JS_CFUNC_DEF("getTime", 0, js_date_getTime ), 50788 JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ), 50789 JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ), 50790 JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ), 50791 JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ), 50792 JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ), 50793 JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ), 50794 JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ), 50795 JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ), 50796 JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ), 50797 JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ), 50798 JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ), 50799 JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ), 50800 JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ), 50801 JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ), 50802 JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ), 50803 JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ), 50804 JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ), 50805 JS_CFUNC_DEF("setTime", 1, js_date_setTime ), 50806 JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ), 50807 JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ), 50808 JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ), 50809 JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ), 50810 JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ), 50811 JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ), 50812 JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ), 50813 JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ), 50814 JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ), 50815 JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ), 50816 JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ), 50817 JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ), 50818 JS_CFUNC_DEF("setYear", 1, js_date_setYear ), 50819 JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ), 50820 JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ), 50821 JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), 50822 }; 50823 50824 JSValue JS_NewDate(JSContext *ctx, double epoch_ms) 50825 { 50826 JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); 50827 if (JS_IsException(obj)) 50828 return JS_EXCEPTION; 50829 JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms))); 50830 return obj; 50831 } 50832 50833 void JS_AddIntrinsicDate(JSContext *ctx) 50834 { 50835 JSValueConst obj; 50836 50837 /* Date */ 50838 ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx); 50839 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs, 50840 countof(js_date_proto_funcs)); 50841 obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7, 50842 ctx->class_proto[JS_CLASS_DATE]); 50843 JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs)); 50844 } 50845 50846 /* eval */ 50847 50848 void JS_AddIntrinsicEval(JSContext *ctx) 50849 { 50850 ctx->eval_internal = __JS_EvalInternal; 50851 } 50852 50853 #ifdef CONFIG_BIGNUM 50854 50855 /* Operators */ 50856 50857 static void js_operator_set_finalizer(JSRuntime *rt, JSValue val) 50858 { 50859 JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); 50860 int i, j; 50861 JSBinaryOperatorDefEntry *ent; 50862 50863 if (opset) { 50864 for(i = 0; i < JS_OVOP_COUNT; i++) { 50865 if (opset->self_ops[i]) 50866 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i])); 50867 } 50868 for(j = 0; j < opset->left.count; j++) { 50869 ent = &opset->left.tab[j]; 50870 for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { 50871 if (ent->ops[i]) 50872 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); 50873 } 50874 } 50875 js_free_rt(rt, opset->left.tab); 50876 for(j = 0; j < opset->right.count; j++) { 50877 ent = &opset->right.tab[j]; 50878 for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { 50879 if (ent->ops[i]) 50880 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i])); 50881 } 50882 } 50883 js_free_rt(rt, opset->right.tab); 50884 js_free_rt(rt, opset); 50885 } 50886 } 50887 50888 static void js_operator_set_mark(JSRuntime *rt, JSValueConst val, 50889 JS_MarkFunc *mark_func) 50890 { 50891 JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET); 50892 int i, j; 50893 JSBinaryOperatorDefEntry *ent; 50894 50895 if (opset) { 50896 for(i = 0; i < JS_OVOP_COUNT; i++) { 50897 if (opset->self_ops[i]) 50898 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]), 50899 mark_func); 50900 } 50901 for(j = 0; j < opset->left.count; j++) { 50902 ent = &opset->left.tab[j]; 50903 for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { 50904 if (ent->ops[i]) 50905 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), 50906 mark_func); 50907 } 50908 } 50909 for(j = 0; j < opset->right.count; j++) { 50910 ent = &opset->right.tab[j]; 50911 for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { 50912 if (ent->ops[i]) 50913 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]), 50914 mark_func); 50915 } 50916 } 50917 } 50918 } 50919 50920 50921 /* create an OperatorSet object */ 50922 static JSValue js_operators_create_internal(JSContext *ctx, 50923 int argc, JSValueConst *argv, 50924 BOOL is_primitive) 50925 { 50926 JSValue opset_obj, prop, obj; 50927 JSOperatorSetData *opset, *opset1; 50928 JSBinaryOperatorDef *def; 50929 JSValueConst arg; 50930 int i, j; 50931 JSBinaryOperatorDefEntry *new_tab; 50932 JSBinaryOperatorDefEntry *ent; 50933 uint32_t op_count; 50934 50935 if (ctx->rt->operator_count == UINT32_MAX) { 50936 return JS_ThrowTypeError(ctx, "too many operators"); 50937 } 50938 opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET); 50939 if (JS_IsException(opset_obj)) 50940 goto fail; 50941 opset = js_mallocz(ctx, sizeof(*opset)); 50942 if (!opset) 50943 goto fail; 50944 JS_SetOpaque(opset_obj, opset); 50945 if (argc >= 1) { 50946 arg = argv[0]; 50947 /* self operators */ 50948 for(i = 0; i < JS_OVOP_COUNT; i++) { 50949 prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]); 50950 if (JS_IsException(prop)) 50951 goto fail; 50952 if (!JS_IsUndefined(prop)) { 50953 if (check_function(ctx, prop)) { 50954 JS_FreeValue(ctx, prop); 50955 goto fail; 50956 } 50957 opset->self_ops[i] = JS_VALUE_GET_OBJ(prop); 50958 } 50959 } 50960 } 50961 /* left & right operators */ 50962 for(j = 1; j < argc; j++) { 50963 arg = argv[j]; 50964 prop = JS_GetPropertyStr(ctx, arg, "left"); 50965 if (JS_IsException(prop)) 50966 goto fail; 50967 def = &opset->right; 50968 if (JS_IsUndefined(prop)) { 50969 prop = JS_GetPropertyStr(ctx, arg, "right"); 50970 if (JS_IsException(prop)) 50971 goto fail; 50972 if (JS_IsUndefined(prop)) { 50973 JS_ThrowTypeError(ctx, "left or right property must be present"); 50974 goto fail; 50975 } 50976 def = &opset->left; 50977 } 50978 /* get the operator set */ 50979 obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype); 50980 JS_FreeValue(ctx, prop); 50981 if (JS_IsException(obj)) 50982 goto fail; 50983 prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet); 50984 JS_FreeValue(ctx, obj); 50985 if (JS_IsException(prop)) 50986 goto fail; 50987 opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET); 50988 if (!opset1) { 50989 JS_FreeValue(ctx, prop); 50990 goto fail; 50991 } 50992 op_count = opset1->operator_counter; 50993 JS_FreeValue(ctx, prop); 50994 50995 /* we assume there are few entries */ 50996 new_tab = js_realloc(ctx, def->tab, 50997 (def->count + 1) * sizeof(def->tab[0])); 50998 if (!new_tab) 50999 goto fail; 51000 def->tab = new_tab; 51001 def->count++; 51002 ent = def->tab + def->count - 1; 51003 memset(ent, 0, sizeof(def->tab[0])); 51004 ent->operator_index = op_count; 51005 51006 for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) { 51007 prop = JS_GetPropertyStr(ctx, arg, 51008 js_overloadable_operator_names[i]); 51009 if (JS_IsException(prop)) 51010 goto fail; 51011 if (!JS_IsUndefined(prop)) { 51012 if (check_function(ctx, prop)) { 51013 JS_FreeValue(ctx, prop); 51014 goto fail; 51015 } 51016 ent->ops[i] = JS_VALUE_GET_OBJ(prop); 51017 } 51018 } 51019 } 51020 opset->is_primitive = is_primitive; 51021 opset->operator_counter = ctx->rt->operator_count++; 51022 return opset_obj; 51023 fail: 51024 JS_FreeValue(ctx, opset_obj); 51025 return JS_EXCEPTION; 51026 } 51027 51028 static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val, 51029 int argc, JSValueConst *argv) 51030 { 51031 return js_operators_create_internal(ctx, argc, argv, FALSE); 51032 } 51033 51034 static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val, 51035 int argc, JSValueConst *argv) 51036 { 51037 JSValue opset_obj, prop; 51038 JSOperatorSetData *opset; 51039 const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW }; 51040 JSOverloadableOperatorEnum op; 51041 int i; 51042 51043 opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT], 51044 JS_ATOM_Symbol_operatorSet); 51045 if (JS_IsException(opset_obj)) 51046 goto fail; 51047 opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET); 51048 if (!opset) 51049 goto fail; 51050 for(i = 0; i < countof(ops); i++) { 51051 op = ops[i]; 51052 prop = JS_GetPropertyStr(ctx, argv[0], 51053 js_overloadable_operator_names[op]); 51054 if (JS_IsException(prop)) 51055 goto fail; 51056 if (!JS_IsUndefined(prop)) { 51057 if (!JS_IsNull(prop) && check_function(ctx, prop)) { 51058 JS_FreeValue(ctx, prop); 51059 goto fail; 51060 } 51061 if (opset->self_ops[op]) 51062 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op])); 51063 if (JS_IsNull(prop)) { 51064 opset->self_ops[op] = NULL; 51065 } else { 51066 opset->self_ops[op] = JS_VALUE_GET_PTR(prop); 51067 } 51068 } 51069 } 51070 JS_FreeValue(ctx, opset_obj); 51071 return JS_UNDEFINED; 51072 fail: 51073 JS_FreeValue(ctx, opset_obj); 51074 return JS_EXCEPTION; 51075 } 51076 51077 static int js_operators_set_default(JSContext *ctx, JSValueConst obj) 51078 { 51079 JSValue opset_obj; 51080 51081 if (!JS_IsObject(obj)) /* in case the prototype is not defined */ 51082 return 0; 51083 opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE); 51084 if (JS_IsException(opset_obj)) 51085 return -1; 51086 /* cannot be modified by the user */ 51087 JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet, 51088 opset_obj, 0); 51089 return 0; 51090 } 51091 51092 static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target, 51093 int argc, JSValueConst *argv) 51094 { 51095 return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); 51096 } 51097 51098 static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val, 51099 int argc, JSValueConst *argv) 51100 { 51101 JSValue func_obj, proto, opset_obj; 51102 51103 func_obj = JS_UNDEFINED; 51104 proto = JS_NewObject(ctx); 51105 if (JS_IsException(proto)) 51106 return JS_EXCEPTION; 51107 opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE); 51108 if (JS_IsException(opset_obj)) 51109 goto fail; 51110 JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet, 51111 opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 51112 func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators", 51113 0, JS_CFUNC_constructor, 0); 51114 if (JS_IsException(func_obj)) 51115 goto fail; 51116 JS_SetConstructor2(ctx, func_obj, proto, 51117 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 51118 JS_FreeValue(ctx, proto); 51119 return func_obj; 51120 fail: 51121 JS_FreeValue(ctx, proto); 51122 JS_FreeValue(ctx, func_obj); 51123 return JS_EXCEPTION; 51124 } 51125 51126 static const JSCFunctionListEntry js_operators_funcs[] = { 51127 JS_CFUNC_DEF("create", 1, js_operators_create ), 51128 JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ), 51129 }; 51130 51131 /* must be called after all overloadable base types are initialized */ 51132 void JS_AddIntrinsicOperators(JSContext *ctx) 51133 { 51134 JSValue obj; 51135 51136 ctx->allow_operator_overloading = TRUE; 51137 obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1); 51138 JS_SetPropertyFunctionList(ctx, obj, 51139 js_operators_funcs, 51140 countof(js_operators_funcs)); 51141 JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators, 51142 obj, 51143 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 51144 /* add default operatorSets */ 51145 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]); 51146 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]); 51147 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]); 51148 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]); 51149 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); 51150 js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); 51151 } 51152 #endif /* CONFIG_BIGNUM */ 51153 51154 /* BigInt */ 51155 51156 static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) 51157 { 51158 uint32_t tag; 51159 51160 redo: 51161 tag = JS_VALUE_GET_NORM_TAG(val); 51162 switch(tag) { 51163 case JS_TAG_INT: 51164 case JS_TAG_BOOL: 51165 val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val)); 51166 break; 51167 case JS_TAG_BIG_INT: 51168 break; 51169 case JS_TAG_FLOAT64: 51170 #ifdef CONFIG_BIGNUM 51171 case JS_TAG_BIG_FLOAT: 51172 #endif 51173 { 51174 bf_t *a, a_s; 51175 51176 a = JS_ToBigFloat(ctx, &a_s, val); 51177 if (!a) { 51178 JS_FreeValue(ctx, val); 51179 return JS_EXCEPTION; 51180 } 51181 if (!bf_is_finite(a)) { 51182 JS_FreeValue(ctx, val); 51183 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); 51184 } else { 51185 JSValue val1 = JS_NewBigInt(ctx); 51186 bf_t *r; 51187 int ret; 51188 if (JS_IsException(val1)) { 51189 JS_FreeValue(ctx, val); 51190 return JS_EXCEPTION; 51191 } 51192 r = JS_GetBigInt(val1); 51193 ret = bf_set(r, a); 51194 ret |= bf_rint(r, BF_RNDZ); 51195 JS_FreeValue(ctx, val); 51196 if (ret & BF_ST_MEM_ERROR) { 51197 JS_FreeValue(ctx, val1); 51198 val = JS_ThrowOutOfMemory(ctx); 51199 } else if (ret & BF_ST_INEXACT) { 51200 JS_FreeValue(ctx, val1); 51201 val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); 51202 } else { 51203 val = JS_CompactBigInt(ctx, val1); 51204 } 51205 } 51206 if (a == &a_s) 51207 bf_delete(a); 51208 } 51209 break; 51210 #ifdef CONFIG_BIGNUM 51211 case JS_TAG_BIG_DECIMAL: 51212 val = JS_ToStringFree(ctx, val); 51213 if (JS_IsException(val)) 51214 break; 51215 goto redo; 51216 #endif 51217 case JS_TAG_STRING: 51218 val = JS_StringToBigIntErr(ctx, val); 51219 break; 51220 case JS_TAG_OBJECT: 51221 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 51222 if (JS_IsException(val)) 51223 break; 51224 goto redo; 51225 case JS_TAG_NULL: 51226 case JS_TAG_UNDEFINED: 51227 default: 51228 JS_FreeValue(ctx, val); 51229 return JS_ThrowTypeError(ctx, "cannot convert to BigInt"); 51230 } 51231 return val; 51232 } 51233 51234 static JSValue js_bigint_constructor(JSContext *ctx, 51235 JSValueConst new_target, 51236 int argc, JSValueConst *argv) 51237 { 51238 if (!JS_IsUndefined(new_target)) 51239 return JS_ThrowTypeError(ctx, "not a constructor"); 51240 return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0])); 51241 } 51242 51243 static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) 51244 { 51245 if (JS_IsBigInt(ctx, this_val)) 51246 return JS_DupValue(ctx, this_val); 51247 51248 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 51249 JSObject *p = JS_VALUE_GET_OBJ(this_val); 51250 if (p->class_id == JS_CLASS_BIG_INT) { 51251 if (JS_IsBigInt(ctx, p->u.object_data)) 51252 return JS_DupValue(ctx, p->u.object_data); 51253 } 51254 } 51255 return JS_ThrowTypeError(ctx, "not a BigInt"); 51256 } 51257 51258 static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, 51259 int argc, JSValueConst *argv) 51260 { 51261 JSValue val; 51262 int base; 51263 JSValue ret; 51264 51265 val = js_thisBigIntValue(ctx, this_val); 51266 if (JS_IsException(val)) 51267 return val; 51268 if (argc == 0 || JS_IsUndefined(argv[0])) { 51269 base = 10; 51270 } else { 51271 base = js_get_radix(ctx, argv[0]); 51272 if (base < 0) 51273 goto fail; 51274 } 51275 ret = js_bigint_to_string1(ctx, val, base); 51276 JS_FreeValue(ctx, val); 51277 return ret; 51278 fail: 51279 JS_FreeValue(ctx, val); 51280 return JS_EXCEPTION; 51281 } 51282 51283 static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, 51284 int argc, JSValueConst *argv) 51285 { 51286 return js_thisBigIntValue(ctx, this_val); 51287 } 51288 51289 #ifdef CONFIG_BIGNUM 51290 static JSValue js_bigint_div(JSContext *ctx, 51291 JSValueConst this_val, 51292 int argc, JSValueConst *argv, int magic) 51293 { 51294 bf_t a_s, b_s, *a, *b, *r, *q; 51295 int status; 51296 JSValue q_val, r_val; 51297 51298 q_val = JS_NewBigInt(ctx); 51299 if (JS_IsException(q_val)) 51300 return JS_EXCEPTION; 51301 r_val = JS_NewBigInt(ctx); 51302 if (JS_IsException(r_val)) 51303 goto fail; 51304 b = NULL; 51305 a = JS_ToBigInt(ctx, &a_s, argv[0]); 51306 if (!a) 51307 goto fail; 51308 b = JS_ToBigInt(ctx, &b_s, argv[1]); 51309 if (!b) { 51310 JS_FreeBigInt(ctx, a, &a_s); 51311 goto fail; 51312 } 51313 q = JS_GetBigInt(q_val); 51314 r = JS_GetBigInt(r_val); 51315 status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf); 51316 JS_FreeBigInt(ctx, a, &a_s); 51317 JS_FreeBigInt(ctx, b, &b_s); 51318 if (unlikely(status)) { 51319 throw_bf_exception(ctx, status); 51320 goto fail; 51321 } 51322 q_val = JS_CompactBigInt(ctx, q_val); 51323 if (magic & 0x10) { 51324 JSValue ret; 51325 ret = JS_NewArray(ctx); 51326 if (JS_IsException(ret)) 51327 goto fail; 51328 JS_SetPropertyUint32(ctx, ret, 0, q_val); 51329 JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val)); 51330 return ret; 51331 } else { 51332 JS_FreeValue(ctx, r_val); 51333 return q_val; 51334 } 51335 fail: 51336 JS_FreeValue(ctx, q_val); 51337 JS_FreeValue(ctx, r_val); 51338 return JS_EXCEPTION; 51339 } 51340 51341 static JSValue js_bigint_sqrt(JSContext *ctx, 51342 JSValueConst this_val, 51343 int argc, JSValueConst *argv, int magic) 51344 { 51345 bf_t a_s, *a, *r, *rem; 51346 int status; 51347 JSValue r_val, rem_val; 51348 51349 r_val = JS_NewBigInt(ctx); 51350 if (JS_IsException(r_val)) 51351 return JS_EXCEPTION; 51352 rem_val = JS_NewBigInt(ctx); 51353 if (JS_IsException(rem_val)) 51354 return JS_EXCEPTION; 51355 r = JS_GetBigInt(r_val); 51356 rem = JS_GetBigInt(rem_val); 51357 51358 a = JS_ToBigInt(ctx, &a_s, argv[0]); 51359 if (!a) 51360 goto fail; 51361 status = bf_sqrtrem(r, rem, a); 51362 JS_FreeBigInt(ctx, a, &a_s); 51363 if (unlikely(status & ~BF_ST_INEXACT)) { 51364 throw_bf_exception(ctx, status); 51365 goto fail; 51366 } 51367 r_val = JS_CompactBigInt(ctx, r_val); 51368 if (magic) { 51369 JSValue ret; 51370 ret = JS_NewArray(ctx); 51371 if (JS_IsException(ret)) 51372 goto fail; 51373 JS_SetPropertyUint32(ctx, ret, 0, r_val); 51374 JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val)); 51375 return ret; 51376 } else { 51377 JS_FreeValue(ctx, rem_val); 51378 return r_val; 51379 } 51380 fail: 51381 JS_FreeValue(ctx, r_val); 51382 JS_FreeValue(ctx, rem_val); 51383 return JS_EXCEPTION; 51384 } 51385 51386 static JSValue js_bigint_op1(JSContext *ctx, 51387 JSValueConst this_val, 51388 int argc, JSValueConst *argv, 51389 int magic) 51390 { 51391 bf_t a_s, *a; 51392 int64_t res; 51393 51394 a = JS_ToBigInt(ctx, &a_s, argv[0]); 51395 if (!a) 51396 return JS_EXCEPTION; 51397 switch(magic) { 51398 case 0: /* floorLog2 */ 51399 if (a->sign || a->expn <= 0) { 51400 res = -1; 51401 } else { 51402 res = a->expn - 1; 51403 } 51404 break; 51405 case 1: /* ctz */ 51406 if (bf_is_zero(a)) { 51407 res = -1; 51408 } else { 51409 res = bf_get_exp_min(a); 51410 } 51411 break; 51412 default: 51413 abort(); 51414 } 51415 JS_FreeBigInt(ctx, a, &a_s); 51416 return JS_NewBigInt64(ctx, res); 51417 } 51418 #endif 51419 51420 static JSValue js_bigint_asUintN(JSContext *ctx, 51421 JSValueConst this_val, 51422 int argc, JSValueConst *argv, int asIntN) 51423 { 51424 uint64_t bits; 51425 bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s; 51426 JSValue res; 51427 51428 if (JS_ToIndex(ctx, &bits, argv[0])) 51429 return JS_EXCEPTION; 51430 res = JS_NewBigInt(ctx); 51431 if (JS_IsException(res)) 51432 return JS_EXCEPTION; 51433 r = JS_GetBigInt(res); 51434 a = JS_ToBigInt(ctx, &a_s, argv[1]); 51435 if (!a) { 51436 JS_FreeValue(ctx, res); 51437 return JS_EXCEPTION; 51438 } 51439 /* XXX: optimize */ 51440 r = JS_GetBigInt(res); 51441 bf_init(ctx->bf_ctx, mask); 51442 bf_set_ui(mask, 1); 51443 bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); 51444 bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ); 51445 bf_logic_and(r, a, mask); 51446 if (asIntN && bits != 0) { 51447 bf_set_ui(mask, 1); 51448 bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ); 51449 if (bf_cmpu(r, mask) >= 0) { 51450 bf_set_ui(mask, 1); 51451 bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ); 51452 bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ); 51453 } 51454 } 51455 bf_delete(mask); 51456 JS_FreeBigInt(ctx, a, &a_s); 51457 return JS_CompactBigInt(ctx, res); 51458 } 51459 51460 static const JSCFunctionListEntry js_bigint_funcs[] = { 51461 JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), 51462 JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), 51463 #ifdef CONFIG_BIGNUM 51464 /* QuickJS extensions */ 51465 JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ), 51466 JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ), 51467 JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ), 51468 JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ), 51469 JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ), 51470 JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ), 51471 JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ), 51472 JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ), 51473 JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ), 51474 JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ), 51475 JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ), 51476 JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ), 51477 #endif 51478 }; 51479 51480 static const JSCFunctionListEntry js_bigint_proto_funcs[] = { 51481 JS_CFUNC_DEF("toString", 0, js_bigint_toString ), 51482 JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ), 51483 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ), 51484 }; 51485 51486 void JS_AddIntrinsicBigInt(JSContext *ctx) 51487 { 51488 JSRuntime *rt = ctx->rt; 51489 JSValueConst obj1; 51490 51491 rt->bigint_ops.to_string = js_bigint_to_string; 51492 rt->bigint_ops.from_string = js_string_to_bigint; 51493 rt->bigint_ops.unary_arith = js_unary_arith_bigint; 51494 rt->bigint_ops.binary_arith = js_binary_arith_bigint; 51495 rt->bigint_ops.compare = js_compare_bigfloat; 51496 51497 ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx); 51498 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT], 51499 js_bigint_proto_funcs, 51500 countof(js_bigint_proto_funcs)); 51501 obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1, 51502 ctx->class_proto[JS_CLASS_BIG_INT]); 51503 JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs, 51504 countof(js_bigint_funcs)); 51505 } 51506 51507 #ifdef CONFIG_BIGNUM 51508 51509 /* BigFloat */ 51510 51511 static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) 51512 { 51513 if (JS_IsBigFloat(this_val)) 51514 return JS_DupValue(ctx, this_val); 51515 51516 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 51517 JSObject *p = JS_VALUE_GET_OBJ(this_val); 51518 if (p->class_id == JS_CLASS_BIG_FLOAT) { 51519 if (JS_IsBigFloat(p->u.object_data)) 51520 return JS_DupValue(ctx, p->u.object_data); 51521 } 51522 } 51523 return JS_ThrowTypeError(ctx, "not a bigfloat"); 51524 } 51525 51526 static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val, 51527 int argc, JSValueConst *argv) 51528 { 51529 JSValue val; 51530 int base; 51531 JSValue ret; 51532 51533 val = js_thisBigFloatValue(ctx, this_val); 51534 if (JS_IsException(val)) 51535 return val; 51536 if (argc == 0 || JS_IsUndefined(argv[0])) { 51537 base = 10; 51538 } else { 51539 base = js_get_radix(ctx, argv[0]); 51540 if (base < 0) 51541 goto fail; 51542 } 51543 ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN); 51544 JS_FreeValue(ctx, val); 51545 return ret; 51546 fail: 51547 JS_FreeValue(ctx, val); 51548 return JS_EXCEPTION; 51549 } 51550 51551 static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val, 51552 int argc, JSValueConst *argv) 51553 { 51554 return js_thisBigFloatValue(ctx, this_val); 51555 } 51556 51557 static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val) 51558 { 51559 int rnd_mode; 51560 if (JS_ToInt32Sat(ctx, &rnd_mode, val)) 51561 return -1; 51562 if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) { 51563 JS_ThrowRangeError(ctx, "invalid rounding mode"); 51564 return -1; 51565 } 51566 return rnd_mode; 51567 } 51568 51569 static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val, 51570 int argc, JSValueConst *argv) 51571 { 51572 JSValue val, ret; 51573 int64_t f; 51574 int rnd_mode, radix; 51575 51576 val = js_thisBigFloatValue(ctx, this_val); 51577 if (JS_IsException(val)) 51578 return val; 51579 if (JS_ToInt64Sat(ctx, &f, argv[0])) 51580 goto fail; 51581 if (f < 0 || f > BF_PREC_MAX) { 51582 JS_ThrowRangeError(ctx, "invalid number of digits"); 51583 goto fail; 51584 } 51585 rnd_mode = BF_RNDNA; 51586 radix = 10; 51587 /* XXX: swap parameter order for rounding mode and radix */ 51588 if (argc > 1) { 51589 rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); 51590 if (rnd_mode < 0) 51591 goto fail; 51592 } 51593 if (argc > 2) { 51594 radix = js_get_radix(ctx, argv[2]); 51595 if (radix < 0) 51596 goto fail; 51597 } 51598 ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC); 51599 JS_FreeValue(ctx, val); 51600 return ret; 51601 fail: 51602 JS_FreeValue(ctx, val); 51603 return JS_EXCEPTION; 51604 } 51605 51606 static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val) 51607 { 51608 BOOL res; 51609 uint32_t tag; 51610 51611 tag = JS_VALUE_GET_NORM_TAG(val); 51612 switch(tag) { 51613 case JS_TAG_BIG_FLOAT: 51614 { 51615 JSBigFloat *p = JS_VALUE_GET_PTR(val); 51616 res = bf_is_finite(&p->num); 51617 } 51618 break; 51619 default: 51620 res = FALSE; 51621 break; 51622 } 51623 return res; 51624 } 51625 51626 static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val, 51627 int argc, JSValueConst *argv) 51628 { 51629 JSValue val, ret; 51630 int64_t f; 51631 int rnd_mode, radix; 51632 51633 val = js_thisBigFloatValue(ctx, this_val); 51634 if (JS_IsException(val)) 51635 return val; 51636 if (JS_ToInt64Sat(ctx, &f, argv[0])) 51637 goto fail; 51638 if (!js_bigfloat_is_finite(ctx, val)) { 51639 ret = JS_ToString(ctx, val); 51640 } else if (JS_IsUndefined(argv[0])) { 51641 ret = js_ftoa(ctx, val, 10, 0, 51642 BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); 51643 } else { 51644 if (f < 0 || f > BF_PREC_MAX) { 51645 JS_ThrowRangeError(ctx, "invalid number of digits"); 51646 goto fail; 51647 } 51648 rnd_mode = BF_RNDNA; 51649 radix = 10; 51650 if (argc > 1) { 51651 rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); 51652 if (rnd_mode < 0) 51653 goto fail; 51654 } 51655 if (argc > 2) { 51656 radix = js_get_radix(ctx, argv[2]); 51657 if (radix < 0) 51658 goto fail; 51659 } 51660 ret = js_ftoa(ctx, val, radix, f + 1, 51661 rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); 51662 } 51663 JS_FreeValue(ctx, val); 51664 return ret; 51665 fail: 51666 JS_FreeValue(ctx, val); 51667 return JS_EXCEPTION; 51668 } 51669 51670 static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val, 51671 int argc, JSValueConst *argv) 51672 { 51673 JSValue val, ret; 51674 int64_t p; 51675 int rnd_mode, radix; 51676 51677 val = js_thisBigFloatValue(ctx, this_val); 51678 if (JS_IsException(val)) 51679 return val; 51680 if (JS_IsUndefined(argv[0])) 51681 goto to_string; 51682 if (JS_ToInt64Sat(ctx, &p, argv[0])) 51683 goto fail; 51684 if (!js_bigfloat_is_finite(ctx, val)) { 51685 to_string: 51686 ret = JS_ToString(ctx, this_val); 51687 } else { 51688 if (p < 1 || p > BF_PREC_MAX) { 51689 JS_ThrowRangeError(ctx, "invalid number of digits"); 51690 goto fail; 51691 } 51692 rnd_mode = BF_RNDNA; 51693 radix = 10; 51694 if (argc > 1) { 51695 rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]); 51696 if (rnd_mode < 0) 51697 goto fail; 51698 } 51699 if (argc > 2) { 51700 radix = js_get_radix(ctx, argv[2]); 51701 if (radix < 0) 51702 goto fail; 51703 } 51704 ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED); 51705 } 51706 JS_FreeValue(ctx, val); 51707 return ret; 51708 fail: 51709 JS_FreeValue(ctx, val); 51710 return JS_EXCEPTION; 51711 } 51712 51713 static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = { 51714 JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ), 51715 JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ), 51716 JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ), 51717 JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ), 51718 JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ), 51719 }; 51720 51721 static JSValue js_bigfloat_constructor(JSContext *ctx, 51722 JSValueConst new_target, 51723 int argc, JSValueConst *argv) 51724 { 51725 JSValue val; 51726 if (!JS_IsUndefined(new_target)) 51727 return JS_ThrowTypeError(ctx, "not a constructor"); 51728 if (argc == 0) { 51729 bf_t *r; 51730 val = JS_NewBigFloat(ctx); 51731 if (JS_IsException(val)) 51732 return val; 51733 r = JS_GetBigFloat(val); 51734 bf_set_zero(r, 0); 51735 } else { 51736 val = JS_DupValue(ctx, argv[0]); 51737 redo: 51738 switch(JS_VALUE_GET_NORM_TAG(val)) { 51739 case JS_TAG_BIG_FLOAT: 51740 break; 51741 case JS_TAG_FLOAT64: 51742 { 51743 bf_t *r; 51744 double d = JS_VALUE_GET_FLOAT64(val); 51745 val = JS_NewBigFloat(ctx); 51746 if (JS_IsException(val)) 51747 break; 51748 r = JS_GetBigFloat(val); 51749 if (bf_set_float64(r, d)) 51750 goto fail; 51751 } 51752 break; 51753 case JS_TAG_INT: 51754 { 51755 bf_t *r; 51756 int32_t v = JS_VALUE_GET_INT(val); 51757 val = JS_NewBigFloat(ctx); 51758 if (JS_IsException(val)) 51759 break; 51760 r = JS_GetBigFloat(val); 51761 if (bf_set_si(r, v)) 51762 goto fail; 51763 } 51764 break; 51765 case JS_TAG_BIG_INT: 51766 /* We keep the full precision of the integer */ 51767 { 51768 JSBigFloat *p = JS_VALUE_GET_PTR(val); 51769 val = JS_MKPTR(JS_TAG_BIG_FLOAT, p); 51770 } 51771 break; 51772 case JS_TAG_BIG_DECIMAL: 51773 val = JS_ToStringFree(ctx, val); 51774 if (JS_IsException(val)) 51775 break; 51776 goto redo; 51777 case JS_TAG_STRING: 51778 { 51779 const char *str, *p; 51780 size_t len; 51781 int err; 51782 51783 str = JS_ToCStringLen(ctx, &len, val); 51784 JS_FreeValue(ctx, val); 51785 if (!str) 51786 return JS_EXCEPTION; 51787 p = str; 51788 p += skip_spaces(p); 51789 if ((p - str) == len) { 51790 bf_t *r; 51791 val = JS_NewBigFloat(ctx); 51792 if (JS_IsException(val)) 51793 break; 51794 r = JS_GetBigFloat(val); 51795 bf_set_zero(r, 0); 51796 err = 0; 51797 } else { 51798 val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT | 51799 ATOD_TYPE_BIG_FLOAT | 51800 ATOD_ACCEPT_PREFIX_AFTER_SIGN); 51801 if (JS_IsException(val)) { 51802 JS_FreeCString(ctx, str); 51803 return JS_EXCEPTION; 51804 } 51805 p += skip_spaces(p); 51806 err = ((p - str) != len); 51807 } 51808 JS_FreeCString(ctx, str); 51809 if (err) { 51810 JS_FreeValue(ctx, val); 51811 return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal"); 51812 } 51813 } 51814 break; 51815 case JS_TAG_OBJECT: 51816 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 51817 if (JS_IsException(val)) 51818 break; 51819 goto redo; 51820 case JS_TAG_NULL: 51821 case JS_TAG_UNDEFINED: 51822 default: 51823 JS_FreeValue(ctx, val); 51824 return JS_ThrowTypeError(ctx, "cannot convert to bigfloat"); 51825 } 51826 } 51827 return val; 51828 fail: 51829 JS_FreeValue(ctx, val); 51830 return JS_EXCEPTION; 51831 } 51832 51833 static JSValue js_bigfloat_get_const(JSContext *ctx, 51834 JSValueConst this_val, int magic) 51835 { 51836 bf_t *r; 51837 JSValue val; 51838 val = JS_NewBigFloat(ctx); 51839 if (JS_IsException(val)) 51840 return val; 51841 r = JS_GetBigFloat(val); 51842 switch(magic) { 51843 case 0: /* PI */ 51844 bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags); 51845 break; 51846 case 1: /* LN2 */ 51847 bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags); 51848 break; 51849 case 2: /* MIN_VALUE */ 51850 case 3: /* MAX_VALUE */ 51851 { 51852 slimb_t e_range, e; 51853 e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1); 51854 bf_set_ui(r, 1); 51855 if (magic == 2) { 51856 e = -e_range + 2; 51857 if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL) 51858 e -= ctx->fp_env.prec - 1; 51859 bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags); 51860 } else { 51861 bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec, 51862 ctx->fp_env.flags); 51863 bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags); 51864 bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec, 51865 ctx->fp_env.flags); 51866 } 51867 } 51868 break; 51869 case 4: /* EPSILON */ 51870 bf_set_ui(r, 1); 51871 bf_mul_2exp(r, 1 - ctx->fp_env.prec, 51872 ctx->fp_env.prec, ctx->fp_env.flags); 51873 break; 51874 default: 51875 abort(); 51876 } 51877 return val; 51878 } 51879 51880 static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val, 51881 int argc, JSValueConst *argv) 51882 { 51883 bf_t *a; 51884 const char *str; 51885 JSValue ret; 51886 int radix; 51887 JSFloatEnv *fe; 51888 51889 str = JS_ToCString(ctx, argv[0]); 51890 if (!str) 51891 return JS_EXCEPTION; 51892 if (JS_ToInt32(ctx, &radix, argv[1])) { 51893 fail: 51894 JS_FreeCString(ctx, str); 51895 return JS_EXCEPTION; 51896 } 51897 if (radix != 0 && (radix < 2 || radix > 36)) { 51898 JS_ThrowRangeError(ctx, "radix must be between 2 and 36"); 51899 goto fail; 51900 } 51901 fe = &ctx->fp_env; 51902 if (argc > 2) { 51903 fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); 51904 if (!fe) 51905 goto fail; 51906 } 51907 ret = JS_NewBigFloat(ctx); 51908 if (JS_IsException(ret)) 51909 goto done; 51910 a = JS_GetBigFloat(ret); 51911 /* XXX: use js_atof() */ 51912 bf_atof(a, str, NULL, radix, fe->prec, fe->flags); 51913 done: 51914 JS_FreeCString(ctx, str); 51915 return ret; 51916 } 51917 51918 static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val, 51919 int argc, JSValueConst *argv) 51920 { 51921 JSValueConst val = argv[0]; 51922 JSBigFloat *p; 51923 51924 if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) 51925 return JS_FALSE; 51926 p = JS_VALUE_GET_PTR(val); 51927 return JS_NewBool(ctx, bf_is_finite(&p->num)); 51928 } 51929 51930 static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val, 51931 int argc, JSValueConst *argv) 51932 { 51933 JSValueConst val = argv[0]; 51934 JSBigFloat *p; 51935 51936 if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT) 51937 return JS_FALSE; 51938 p = JS_VALUE_GET_PTR(val); 51939 return JS_NewBool(ctx, bf_is_nan(&p->num)); 51940 } 51941 51942 enum { 51943 MATH_OP_ABS, 51944 MATH_OP_FLOOR, 51945 MATH_OP_CEIL, 51946 MATH_OP_ROUND, 51947 MATH_OP_TRUNC, 51948 MATH_OP_SQRT, 51949 MATH_OP_FPROUND, 51950 MATH_OP_ACOS, 51951 MATH_OP_ASIN, 51952 MATH_OP_ATAN, 51953 MATH_OP_ATAN2, 51954 MATH_OP_COS, 51955 MATH_OP_EXP, 51956 MATH_OP_LOG, 51957 MATH_OP_POW, 51958 MATH_OP_SIN, 51959 MATH_OP_TAN, 51960 MATH_OP_FMOD, 51961 MATH_OP_REM, 51962 MATH_OP_SIGN, 51963 51964 MATH_OP_ADD, 51965 MATH_OP_SUB, 51966 MATH_OP_MUL, 51967 MATH_OP_DIV, 51968 }; 51969 51970 static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, 51971 int argc, JSValueConst *argv, int magic) 51972 { 51973 bf_t a_s, *a, *r; 51974 JSFloatEnv *fe; 51975 int rnd_mode; 51976 JSValue op1, res; 51977 51978 op1 = JS_ToNumeric(ctx, argv[0]); 51979 if (JS_IsException(op1)) 51980 return op1; 51981 a = JS_ToBigFloat(ctx, &a_s, op1); 51982 if (!a) { 51983 JS_FreeValue(ctx, op1); 51984 return JS_EXCEPTION; 51985 } 51986 fe = &ctx->fp_env; 51987 if (argc > 1) { 51988 fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); 51989 if (!fe) 51990 goto fail; 51991 } 51992 res = JS_NewBigFloat(ctx); 51993 if (JS_IsException(res)) { 51994 fail: 51995 if (a == &a_s) 51996 bf_delete(a); 51997 JS_FreeValue(ctx, op1); 51998 return JS_EXCEPTION; 51999 } 52000 r = JS_GetBigFloat(res); 52001 switch (magic) { 52002 case MATH_OP_ABS: 52003 bf_set(r, a); 52004 r->sign = 0; 52005 break; 52006 case MATH_OP_FLOOR: 52007 rnd_mode = BF_RNDD; 52008 goto rint; 52009 case MATH_OP_CEIL: 52010 rnd_mode = BF_RNDU; 52011 goto rint; 52012 case MATH_OP_ROUND: 52013 rnd_mode = BF_RNDNA; 52014 goto rint; 52015 case MATH_OP_TRUNC: 52016 rnd_mode = BF_RNDZ; 52017 rint: 52018 bf_set(r, a); 52019 fe->status |= bf_rint(r, rnd_mode); 52020 break; 52021 case MATH_OP_SQRT: 52022 fe->status |= bf_sqrt(r, a, fe->prec, fe->flags); 52023 break; 52024 case MATH_OP_FPROUND: 52025 bf_set(r, a); 52026 fe->status |= bf_round(r, fe->prec, fe->flags); 52027 break; 52028 case MATH_OP_ACOS: 52029 fe->status |= bf_acos(r, a, fe->prec, fe->flags); 52030 break; 52031 case MATH_OP_ASIN: 52032 fe->status |= bf_asin(r, a, fe->prec, fe->flags); 52033 break; 52034 case MATH_OP_ATAN: 52035 fe->status |= bf_atan(r, a, fe->prec, fe->flags); 52036 break; 52037 case MATH_OP_COS: 52038 fe->status |= bf_cos(r, a, fe->prec, fe->flags); 52039 break; 52040 case MATH_OP_EXP: 52041 fe->status |= bf_exp(r, a, fe->prec, fe->flags); 52042 break; 52043 case MATH_OP_LOG: 52044 fe->status |= bf_log(r, a, fe->prec, fe->flags); 52045 break; 52046 case MATH_OP_SIN: 52047 fe->status |= bf_sin(r, a, fe->prec, fe->flags); 52048 break; 52049 case MATH_OP_TAN: 52050 fe->status |= bf_tan(r, a, fe->prec, fe->flags); 52051 break; 52052 case MATH_OP_SIGN: 52053 if (bf_is_nan(a) || bf_is_zero(a)) { 52054 bf_set(r, a); 52055 } else { 52056 bf_set_si(r, 1 - 2 * a->sign); 52057 } 52058 break; 52059 default: 52060 abort(); 52061 } 52062 if (a == &a_s) 52063 bf_delete(a); 52064 JS_FreeValue(ctx, op1); 52065 return res; 52066 } 52067 52068 static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, 52069 int argc, JSValueConst *argv, int magic) 52070 { 52071 bf_t a_s, *a, b_s, *b, r_s, *r = &r_s; 52072 JSFloatEnv *fe; 52073 JSValue op1, op2, res; 52074 52075 op1 = JS_ToNumeric(ctx, argv[0]); 52076 if (JS_IsException(op1)) 52077 return op1; 52078 op2 = JS_ToNumeric(ctx, argv[1]); 52079 if (JS_IsException(op2)) { 52080 JS_FreeValue(ctx, op1); 52081 return op2; 52082 } 52083 a = JS_ToBigFloat(ctx, &a_s, op1); 52084 if (!a) 52085 goto fail1; 52086 b = JS_ToBigFloat(ctx, &b_s, op2); 52087 if (!b) 52088 goto fail2; 52089 fe = &ctx->fp_env; 52090 if (argc > 2) { 52091 fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); 52092 if (!fe) 52093 goto fail; 52094 } 52095 res = JS_NewBigFloat(ctx); 52096 if (JS_IsException(res)) { 52097 fail: 52098 if (b == &b_s) 52099 bf_delete(b); 52100 fail2: 52101 if (a == &a_s) 52102 bf_delete(a); 52103 fail1: 52104 JS_FreeValue(ctx, op1); 52105 JS_FreeValue(ctx, op2); 52106 return JS_EXCEPTION; 52107 } 52108 r = JS_GetBigFloat(res); 52109 switch (magic) { 52110 case MATH_OP_ATAN2: 52111 fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags); 52112 break; 52113 case MATH_OP_POW: 52114 fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS); 52115 break; 52116 case MATH_OP_FMOD: 52117 fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); 52118 break; 52119 case MATH_OP_REM: 52120 fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN); 52121 break; 52122 case MATH_OP_ADD: 52123 fe->status |= bf_add(r, a, b, fe->prec, fe->flags); 52124 break; 52125 case MATH_OP_SUB: 52126 fe->status |= bf_sub(r, a, b, fe->prec, fe->flags); 52127 break; 52128 case MATH_OP_MUL: 52129 fe->status |= bf_mul(r, a, b, fe->prec, fe->flags); 52130 break; 52131 case MATH_OP_DIV: 52132 fe->status |= bf_div(r, a, b, fe->prec, fe->flags); 52133 break; 52134 default: 52135 abort(); 52136 } 52137 if (a == &a_s) 52138 bf_delete(a); 52139 if (b == &b_s) 52140 bf_delete(b); 52141 JS_FreeValue(ctx, op1); 52142 JS_FreeValue(ctx, op2); 52143 return res; 52144 } 52145 52146 static const JSCFunctionListEntry js_bigfloat_funcs[] = { 52147 JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ), 52148 JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ), 52149 JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ), 52150 JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ), 52151 JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ), 52152 JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ), 52153 JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ), 52154 JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ), 52155 JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ), 52156 JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ), 52157 JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ), 52158 JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ), 52159 JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ), 52160 JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ), 52161 JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ), 52162 JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ), 52163 JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ), 52164 JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ), 52165 JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ), 52166 JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ), 52167 JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ), 52168 JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ), 52169 JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ), 52170 JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ), 52171 JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ), 52172 JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ), 52173 JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ), 52174 JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ), 52175 JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ), 52176 JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ), 52177 JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ), 52178 JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ), 52179 }; 52180 52181 /* FloatEnv */ 52182 52183 static JSValue js_float_env_constructor(JSContext *ctx, 52184 JSValueConst new_target, 52185 int argc, JSValueConst *argv) 52186 { 52187 JSValue obj; 52188 JSFloatEnv *fe; 52189 int64_t prec; 52190 int flags, rndmode; 52191 52192 prec = ctx->fp_env.prec; 52193 flags = ctx->fp_env.flags; 52194 if (!JS_IsUndefined(argv[0])) { 52195 if (JS_ToInt64Sat(ctx, &prec, argv[0])) 52196 return JS_EXCEPTION; 52197 if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) 52198 return JS_ThrowRangeError(ctx, "invalid precision"); 52199 flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */ 52200 if (argc > 1 && !JS_IsUndefined(argv[1])) { 52201 if (JS_ToInt32Sat(ctx, &rndmode, argv[1])) 52202 return JS_EXCEPTION; 52203 if (rndmode < BF_RNDN || rndmode > BF_RNDF) 52204 return JS_ThrowRangeError(ctx, "invalid rounding mode"); 52205 flags = rndmode; 52206 } 52207 } 52208 52209 obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV); 52210 if (JS_IsException(obj)) 52211 return JS_EXCEPTION; 52212 fe = js_malloc(ctx, sizeof(*fe)); 52213 if (!fe) 52214 return JS_EXCEPTION; 52215 fe->prec = prec; 52216 fe->flags = flags; 52217 fe->status = 0; 52218 JS_SetOpaque(obj, fe); 52219 return obj; 52220 } 52221 52222 static void js_float_env_finalizer(JSRuntime *rt, JSValue val) 52223 { 52224 JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV); 52225 js_free_rt(rt, fe); 52226 } 52227 52228 static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val) 52229 { 52230 return JS_NewInt64(ctx, ctx->fp_env.prec); 52231 } 52232 52233 static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val) 52234 { 52235 return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags)); 52236 } 52237 52238 static JSValue js_float_env_setPrec(JSContext *ctx, 52239 JSValueConst this_val, 52240 int argc, JSValueConst *argv) 52241 { 52242 JSValueConst func; 52243 int exp_bits, flags, saved_flags; 52244 JSValue ret; 52245 limb_t saved_prec; 52246 int64_t prec; 52247 52248 func = argv[0]; 52249 if (JS_ToInt64Sat(ctx, &prec, argv[1])) 52250 return JS_EXCEPTION; 52251 if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) 52252 return JS_ThrowRangeError(ctx, "invalid precision"); 52253 exp_bits = BF_EXP_BITS_MAX; 52254 52255 if (argc > 2 && !JS_IsUndefined(argv[2])) { 52256 if (JS_ToInt32Sat(ctx, &exp_bits, argv[2])) 52257 return JS_EXCEPTION; 52258 if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX) 52259 return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); 52260 } 52261 52262 flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits); 52263 52264 saved_prec = ctx->fp_env.prec; 52265 saved_flags = ctx->fp_env.flags; 52266 52267 ctx->fp_env.prec = prec; 52268 ctx->fp_env.flags = flags; 52269 52270 ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); 52271 /* always restore the floating point precision */ 52272 ctx->fp_env.prec = saved_prec; 52273 ctx->fp_env.flags = saved_flags; 52274 return ret; 52275 } 52276 52277 #define FE_PREC (-1) 52278 #define FE_EXP (-2) 52279 #define FE_RNDMODE (-3) 52280 #define FE_SUBNORMAL (-4) 52281 52282 static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic) 52283 { 52284 JSFloatEnv *fe; 52285 fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); 52286 if (!fe) 52287 return JS_EXCEPTION; 52288 switch(magic) { 52289 case FE_PREC: 52290 return JS_NewInt64(ctx, fe->prec); 52291 case FE_EXP: 52292 return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags)); 52293 case FE_RNDMODE: 52294 return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); 52295 case FE_SUBNORMAL: 52296 return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL); 52297 default: 52298 return JS_NewBool(ctx, fe->status & magic); 52299 } 52300 } 52301 52302 static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic) 52303 { 52304 JSFloatEnv *fe; 52305 int b; 52306 int64_t prec; 52307 52308 fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); 52309 if (!fe) 52310 return JS_EXCEPTION; 52311 switch(magic) { 52312 case FE_PREC: 52313 if (JS_ToInt64Sat(ctx, &prec, val)) 52314 return JS_EXCEPTION; 52315 if (prec < BF_PREC_MIN || prec > BF_PREC_MAX) 52316 return JS_ThrowRangeError(ctx, "invalid precision"); 52317 fe->prec = prec; 52318 break; 52319 case FE_EXP: 52320 if (JS_ToInt32Sat(ctx, &b, val)) 52321 return JS_EXCEPTION; 52322 if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX) 52323 return JS_ThrowRangeError(ctx, "invalid number of exponent bits"); 52324 fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) | 52325 bf_set_exp_bits(b); 52326 break; 52327 case FE_RNDMODE: 52328 b = bigfloat_get_rnd_mode(ctx, val); 52329 if (b < 0) 52330 return JS_EXCEPTION; 52331 fe->flags = (fe->flags & ~BF_RND_MASK) | b; 52332 break; 52333 case FE_SUBNORMAL: 52334 b = JS_ToBool(ctx, val); 52335 fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0); 52336 break; 52337 default: 52338 b = JS_ToBool(ctx, val); 52339 fe->status = (fe->status & ~magic) & ((-b) & magic); 52340 break; 52341 } 52342 return JS_UNDEFINED; 52343 } 52344 52345 static JSValue js_float_env_clearStatus(JSContext *ctx, 52346 JSValueConst this_val, 52347 int argc, JSValueConst *argv) 52348 { 52349 JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV); 52350 if (!fe) 52351 return JS_EXCEPTION; 52352 fe->status = 0; 52353 return JS_UNDEFINED; 52354 } 52355 52356 static const JSCFunctionListEntry js_float_env_funcs[] = { 52357 JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ), 52358 JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ), 52359 JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ), 52360 JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ), 52361 JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ), 52362 JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ), 52363 JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ), 52364 JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ), 52365 JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ), 52366 JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ), 52367 JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ), 52368 JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ), 52369 JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ), 52370 JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ), 52371 }; 52372 52373 static const JSCFunctionListEntry js_float_env_proto_funcs[] = { 52374 JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status, 52375 js_float_env_proto_set_status, FE_PREC ), 52376 JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status, 52377 js_float_env_proto_set_status, FE_EXP ), 52378 JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status, 52379 js_float_env_proto_set_status, FE_RNDMODE ), 52380 JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status, 52381 js_float_env_proto_set_status, FE_SUBNORMAL ), 52382 JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status, 52383 js_float_env_proto_set_status, BF_ST_INVALID_OP ), 52384 JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status, 52385 js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ), 52386 JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status, 52387 js_float_env_proto_set_status, BF_ST_OVERFLOW ), 52388 JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status, 52389 js_float_env_proto_set_status, BF_ST_UNDERFLOW ), 52390 JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status, 52391 js_float_env_proto_set_status, BF_ST_INEXACT ), 52392 JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ), 52393 }; 52394 52395 void JS_AddIntrinsicBigFloat(JSContext *ctx) 52396 { 52397 JSRuntime *rt = ctx->rt; 52398 JSValueConst obj1; 52399 52400 rt->bigfloat_ops.to_string = js_bigfloat_to_string; 52401 rt->bigfloat_ops.from_string = js_string_to_bigfloat; 52402 rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat; 52403 rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat; 52404 rt->bigfloat_ops.compare = js_compare_bigfloat; 52405 rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64; 52406 rt->bigfloat_ops.mul_pow10 = js_mul_pow10; 52407 52408 ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx); 52409 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT], 52410 js_bigfloat_proto_funcs, 52411 countof(js_bigfloat_proto_funcs)); 52412 obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1, 52413 ctx->class_proto[JS_CLASS_BIG_FLOAT]); 52414 JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs, 52415 countof(js_bigfloat_funcs)); 52416 52417 ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx); 52418 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV], 52419 js_float_env_proto_funcs, 52420 countof(js_float_env_proto_funcs)); 52421 obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv", 52422 js_float_env_constructor, 1, 52423 ctx->class_proto[JS_CLASS_FLOAT_ENV]); 52424 JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs, 52425 countof(js_float_env_funcs)); 52426 } 52427 52428 /* BigDecimal */ 52429 52430 static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, 52431 BOOL allow_null_or_undefined) 52432 { 52433 redo: 52434 switch(JS_VALUE_GET_NORM_TAG(val)) { 52435 case JS_TAG_BIG_DECIMAL: 52436 break; 52437 case JS_TAG_NULL: 52438 if (!allow_null_or_undefined) 52439 goto fail; 52440 /* fall thru */ 52441 case JS_TAG_BOOL: 52442 case JS_TAG_INT: 52443 { 52444 bfdec_t *r; 52445 int32_t v = JS_VALUE_GET_INT(val); 52446 52447 val = JS_NewBigDecimal(ctx); 52448 if (JS_IsException(val)) 52449 break; 52450 r = JS_GetBigDecimal(val); 52451 if (bfdec_set_si(r, v)) { 52452 JS_FreeValue(ctx, val); 52453 val = JS_EXCEPTION; 52454 break; 52455 } 52456 } 52457 break; 52458 case JS_TAG_FLOAT64: 52459 case JS_TAG_BIG_INT: 52460 case JS_TAG_BIG_FLOAT: 52461 val = JS_ToStringFree(ctx, val); 52462 if (JS_IsException(val)) 52463 break; 52464 goto redo; 52465 case JS_TAG_STRING: 52466 { 52467 const char *str, *p; 52468 size_t len; 52469 int err; 52470 52471 str = JS_ToCStringLen(ctx, &len, val); 52472 JS_FreeValue(ctx, val); 52473 if (!str) 52474 return JS_EXCEPTION; 52475 p = str; 52476 p += skip_spaces(p); 52477 if ((p - str) == len) { 52478 bfdec_t *r; 52479 val = JS_NewBigDecimal(ctx); 52480 if (JS_IsException(val)) 52481 break; 52482 r = JS_GetBigDecimal(val); 52483 bfdec_set_zero(r, 0); 52484 err = 0; 52485 } else { 52486 val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL); 52487 if (JS_IsException(val)) { 52488 JS_FreeCString(ctx, str); 52489 return JS_EXCEPTION; 52490 } 52491 p += skip_spaces(p); 52492 err = ((p - str) != len); 52493 } 52494 JS_FreeCString(ctx, str); 52495 if (err) { 52496 JS_FreeValue(ctx, val); 52497 return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal"); 52498 } 52499 } 52500 break; 52501 case JS_TAG_OBJECT: 52502 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); 52503 if (JS_IsException(val)) 52504 break; 52505 goto redo; 52506 case JS_TAG_UNDEFINED: 52507 { 52508 bfdec_t *r; 52509 if (!allow_null_or_undefined) 52510 goto fail; 52511 val = JS_NewBigDecimal(ctx); 52512 if (JS_IsException(val)) 52513 break; 52514 r = JS_GetBigDecimal(val); 52515 bfdec_set_nan(r); 52516 } 52517 break; 52518 default: 52519 fail: 52520 JS_FreeValue(ctx, val); 52521 return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal"); 52522 } 52523 return val; 52524 } 52525 52526 static JSValue js_bigdecimal_constructor(JSContext *ctx, 52527 JSValueConst new_target, 52528 int argc, JSValueConst *argv) 52529 { 52530 JSValue val; 52531 if (!JS_IsUndefined(new_target)) 52532 return JS_ThrowTypeError(ctx, "not a constructor"); 52533 if (argc == 0) { 52534 bfdec_t *r; 52535 val = JS_NewBigDecimal(ctx); 52536 if (JS_IsException(val)) 52537 return val; 52538 r = JS_GetBigDecimal(val); 52539 bfdec_set_zero(r, 0); 52540 } else { 52541 val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE); 52542 } 52543 return val; 52544 } 52545 52546 static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val) 52547 { 52548 if (JS_IsBigDecimal(this_val)) 52549 return JS_DupValue(ctx, this_val); 52550 52551 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { 52552 JSObject *p = JS_VALUE_GET_OBJ(this_val); 52553 if (p->class_id == JS_CLASS_BIG_DECIMAL) { 52554 if (JS_IsBigDecimal(p->u.object_data)) 52555 return JS_DupValue(ctx, p->u.object_data); 52556 } 52557 } 52558 return JS_ThrowTypeError(ctx, "not a bigdecimal"); 52559 } 52560 52561 static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val, 52562 int argc, JSValueConst *argv) 52563 { 52564 JSValue val; 52565 52566 val = js_thisBigDecimalValue(ctx, this_val); 52567 if (JS_IsException(val)) 52568 return val; 52569 return JS_ToStringFree(ctx, val); 52570 } 52571 52572 static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val, 52573 int argc, JSValueConst *argv) 52574 { 52575 return js_thisBigDecimalValue(ctx, this_val); 52576 } 52577 52578 static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj) 52579 { 52580 const char *str; 52581 size_t size; 52582 int rnd_mode; 52583 52584 str = JS_ToCStringLen(ctx, &size, obj); 52585 if (!str) 52586 return -1; 52587 if (strlen(str) != size) 52588 goto invalid_rounding_mode; 52589 if (!strcmp(str, "floor")) { 52590 rnd_mode = BF_RNDD; 52591 } else if (!strcmp(str, "ceiling")) { 52592 rnd_mode = BF_RNDU; 52593 } else if (!strcmp(str, "down")) { 52594 rnd_mode = BF_RNDZ; 52595 } else if (!strcmp(str, "up")) { 52596 rnd_mode = BF_RNDA; 52597 } else if (!strcmp(str, "half-even")) { 52598 rnd_mode = BF_RNDN; 52599 } else if (!strcmp(str, "half-up")) { 52600 rnd_mode = BF_RNDNA; 52601 } else { 52602 invalid_rounding_mode: 52603 JS_FreeCString(ctx, str); 52604 JS_ThrowTypeError(ctx, "invalid rounding mode"); 52605 return -1; 52606 } 52607 JS_FreeCString(ctx, str); 52608 return rnd_mode; 52609 } 52610 52611 typedef struct { 52612 int64_t prec; 52613 bf_flags_t flags; 52614 } BigDecimalEnv; 52615 52616 static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe, 52617 JSValueConst obj) 52618 { 52619 JSValue prop; 52620 int64_t val; 52621 BOOL has_prec; 52622 int rnd_mode; 52623 52624 if (!JS_IsObject(obj)) { 52625 JS_ThrowTypeErrorNotAnObject(ctx); 52626 return -1; 52627 } 52628 prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode); 52629 if (JS_IsException(prop)) 52630 return -1; 52631 rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop); 52632 JS_FreeValue(ctx, prop); 52633 if (rnd_mode < 0) 52634 return -1; 52635 fe->flags = rnd_mode; 52636 52637 prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits); 52638 if (JS_IsException(prop)) 52639 return -1; 52640 has_prec = FALSE; 52641 if (!JS_IsUndefined(prop)) { 52642 if (JS_ToInt64SatFree(ctx, &val, prop)) 52643 return -1; 52644 if (val < 1 || val > BF_PREC_MAX) 52645 goto invalid_precision; 52646 fe->prec = val; 52647 has_prec = TRUE; 52648 } 52649 52650 prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits); 52651 if (JS_IsException(prop)) 52652 return -1; 52653 if (!JS_IsUndefined(prop)) { 52654 if (has_prec) { 52655 JS_FreeValue(ctx, prop); 52656 JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits"); 52657 return -1; 52658 } 52659 if (JS_ToInt64SatFree(ctx, &val, prop)) 52660 return -1; 52661 if (val < 0 || val > BF_PREC_MAX) { 52662 invalid_precision: 52663 JS_ThrowTypeError(ctx, "invalid precision"); 52664 return -1; 52665 } 52666 fe->prec = val; 52667 fe->flags |= BF_FLAG_RADPNT_PREC; 52668 has_prec = TRUE; 52669 } 52670 if (!has_prec) { 52671 JS_ThrowTypeError(ctx, "precision must be present"); 52672 return -1; 52673 } 52674 return 0; 52675 } 52676 52677 52678 static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val, 52679 int argc, JSValueConst *argv, int magic) 52680 { 52681 bfdec_t *a, *b, r_s, *r = &r_s; 52682 JSValue op1, op2, res; 52683 BigDecimalEnv fe_s, *fe = &fe_s; 52684 int op_count, ret; 52685 52686 if (magic == MATH_OP_SQRT || 52687 magic == MATH_OP_ROUND) 52688 op_count = 1; 52689 else 52690 op_count = 2; 52691 52692 op1 = JS_ToNumeric(ctx, argv[0]); 52693 if (JS_IsException(op1)) 52694 return op1; 52695 a = JS_ToBigDecimal(ctx, op1); 52696 if (!a) { 52697 JS_FreeValue(ctx, op1); 52698 return JS_EXCEPTION; 52699 } 52700 if (op_count >= 2) { 52701 op2 = JS_ToNumeric(ctx, argv[1]); 52702 if (JS_IsException(op2)) { 52703 JS_FreeValue(ctx, op1); 52704 return op2; 52705 } 52706 b = JS_ToBigDecimal(ctx, op2); 52707 if (!b) 52708 goto fail; 52709 } else { 52710 op2 = JS_UNDEFINED; 52711 b = NULL; 52712 } 52713 fe->flags = BF_RNDZ; 52714 fe->prec = BF_PREC_INF; 52715 if (op_count < argc) { 52716 if (js_bigdecimal_get_env(ctx, fe, argv[op_count])) 52717 goto fail; 52718 } 52719 52720 res = JS_NewBigDecimal(ctx); 52721 if (JS_IsException(res)) { 52722 fail: 52723 JS_FreeValue(ctx, op1); 52724 JS_FreeValue(ctx, op2); 52725 return JS_EXCEPTION; 52726 } 52727 r = JS_GetBigDecimal(res); 52728 switch (magic) { 52729 case MATH_OP_ADD: 52730 ret = bfdec_add(r, a, b, fe->prec, fe->flags); 52731 break; 52732 case MATH_OP_SUB: 52733 ret = bfdec_sub(r, a, b, fe->prec, fe->flags); 52734 break; 52735 case MATH_OP_MUL: 52736 ret = bfdec_mul(r, a, b, fe->prec, fe->flags); 52737 break; 52738 case MATH_OP_DIV: 52739 ret = bfdec_div(r, a, b, fe->prec, fe->flags); 52740 break; 52741 case MATH_OP_FMOD: 52742 ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ); 52743 break; 52744 case MATH_OP_SQRT: 52745 ret = bfdec_sqrt(r, a, fe->prec, fe->flags); 52746 break; 52747 case MATH_OP_ROUND: 52748 ret = bfdec_set(r, a); 52749 if (!(ret & BF_ST_MEM_ERROR)) 52750 ret = bfdec_round(r, fe->prec, fe->flags); 52751 break; 52752 default: 52753 abort(); 52754 } 52755 JS_FreeValue(ctx, op1); 52756 JS_FreeValue(ctx, op2); 52757 ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP | 52758 BF_ST_OVERFLOW; 52759 if (ret != 0) { 52760 JS_FreeValue(ctx, res); 52761 return throw_bf_exception(ctx, ret); 52762 } else { 52763 return res; 52764 } 52765 } 52766 52767 static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val, 52768 int argc, JSValueConst *argv) 52769 { 52770 JSValue val, ret; 52771 int64_t f; 52772 int rnd_mode; 52773 52774 val = js_thisBigDecimalValue(ctx, this_val); 52775 if (JS_IsException(val)) 52776 return val; 52777 if (JS_ToInt64Sat(ctx, &f, argv[0])) 52778 goto fail; 52779 if (f < 0 || f > BF_PREC_MAX) { 52780 JS_ThrowRangeError(ctx, "invalid number of digits"); 52781 goto fail; 52782 } 52783 rnd_mode = BF_RNDNA; 52784 if (argc > 1) { 52785 rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); 52786 if (rnd_mode < 0) 52787 goto fail; 52788 } 52789 ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC); 52790 JS_FreeValue(ctx, val); 52791 return ret; 52792 fail: 52793 JS_FreeValue(ctx, val); 52794 return JS_EXCEPTION; 52795 } 52796 52797 static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val, 52798 int argc, JSValueConst *argv) 52799 { 52800 JSValue val, ret; 52801 int64_t f; 52802 int rnd_mode; 52803 52804 val = js_thisBigDecimalValue(ctx, this_val); 52805 if (JS_IsException(val)) 52806 return val; 52807 if (JS_ToInt64Sat(ctx, &f, argv[0])) 52808 goto fail; 52809 if (JS_IsUndefined(argv[0])) { 52810 ret = js_bigdecimal_to_string1(ctx, val, 0, 52811 BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP); 52812 } else { 52813 if (f < 0 || f > BF_PREC_MAX) { 52814 JS_ThrowRangeError(ctx, "invalid number of digits"); 52815 goto fail; 52816 } 52817 rnd_mode = BF_RNDNA; 52818 if (argc > 1) { 52819 rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); 52820 if (rnd_mode < 0) 52821 goto fail; 52822 } 52823 ret = js_bigdecimal_to_string1(ctx, val, f + 1, 52824 rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP); 52825 } 52826 JS_FreeValue(ctx, val); 52827 return ret; 52828 fail: 52829 JS_FreeValue(ctx, val); 52830 return JS_EXCEPTION; 52831 } 52832 52833 static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val, 52834 int argc, JSValueConst *argv) 52835 { 52836 JSValue val, ret; 52837 int64_t p; 52838 int rnd_mode; 52839 52840 val = js_thisBigDecimalValue(ctx, this_val); 52841 if (JS_IsException(val)) 52842 return val; 52843 if (JS_IsUndefined(argv[0])) { 52844 return JS_ToStringFree(ctx, val); 52845 } 52846 if (JS_ToInt64Sat(ctx, &p, argv[0])) 52847 goto fail; 52848 if (p < 1 || p > BF_PREC_MAX) { 52849 JS_ThrowRangeError(ctx, "invalid number of digits"); 52850 goto fail; 52851 } 52852 rnd_mode = BF_RNDNA; 52853 if (argc > 1) { 52854 rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]); 52855 if (rnd_mode < 0) 52856 goto fail; 52857 } 52858 ret = js_bigdecimal_to_string1(ctx, val, p, 52859 rnd_mode | BF_FTOA_FORMAT_FIXED); 52860 JS_FreeValue(ctx, val); 52861 return ret; 52862 fail: 52863 JS_FreeValue(ctx, val); 52864 return JS_EXCEPTION; 52865 } 52866 52867 static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = { 52868 JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ), 52869 JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ), 52870 JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ), 52871 JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ), 52872 JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ), 52873 }; 52874 52875 static const JSCFunctionListEntry js_bigdecimal_funcs[] = { 52876 JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ), 52877 JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ), 52878 JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ), 52879 JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ), 52880 JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ), 52881 JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ), 52882 JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ), 52883 }; 52884 52885 void JS_AddIntrinsicBigDecimal(JSContext *ctx) 52886 { 52887 JSRuntime *rt = ctx->rt; 52888 JSValueConst obj1; 52889 52890 rt->bigdecimal_ops.to_string = js_bigdecimal_to_string; 52891 rt->bigdecimal_ops.from_string = js_string_to_bigdecimal; 52892 rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal; 52893 rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal; 52894 rt->bigdecimal_ops.compare = js_compare_bigdecimal; 52895 52896 ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx); 52897 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL], 52898 js_bigdecimal_proto_funcs, 52899 countof(js_bigdecimal_proto_funcs)); 52900 obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal", 52901 js_bigdecimal_constructor, 1, 52902 ctx->class_proto[JS_CLASS_BIG_DECIMAL]); 52903 JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs, 52904 countof(js_bigdecimal_funcs)); 52905 } 52906 52907 void JS_EnableBignumExt(JSContext *ctx, BOOL enable) 52908 { 52909 ctx->bignum_ext = enable; 52910 } 52911 52912 #endif /* CONFIG_BIGNUM */ 52913 52914 static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = { 52915 "EvalError", "RangeError", "ReferenceError", 52916 "SyntaxError", "TypeError", "URIError", 52917 "InternalError", "AggregateError", 52918 }; 52919 52920 /* Minimum amount of objects to be able to compile code and display 52921 error messages. No JSAtom should be allocated by this function. */ 52922 static void JS_AddIntrinsicBasicObjects(JSContext *ctx) 52923 { 52924 JSValue proto; 52925 int i; 52926 52927 ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); 52928 ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, 52929 JS_CFUNC_generic, 0, 52930 ctx->class_proto[JS_CLASS_OBJECT]); 52931 ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto); 52932 ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx); 52933 #if 0 52934 /* these are auto-initialized from js_error_proto_funcs, 52935 but delaying might be a problem */ 52936 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name, 52937 JS_AtomToString(ctx, JS_ATOM_Error), 52938 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 52939 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message, 52940 JS_AtomToString(ctx, JS_ATOM_empty_string), 52941 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 52942 #endif 52943 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR], 52944 js_error_proto_funcs, 52945 countof(js_error_proto_funcs)); 52946 52947 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 52948 proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]); 52949 JS_DefinePropertyValue(ctx, proto, JS_ATOM_name, 52950 JS_NewAtomString(ctx, native_error_name[i]), 52951 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 52952 JS_DefinePropertyValue(ctx, proto, JS_ATOM_message, 52953 JS_AtomToString(ctx, JS_ATOM_empty_string), 52954 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 52955 ctx->native_error_proto[i] = proto; 52956 } 52957 52958 /* the array prototype is an array */ 52959 ctx->class_proto[JS_CLASS_ARRAY] = 52960 JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 52961 JS_CLASS_ARRAY); 52962 52963 ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]), 52964 JS_PROP_INITIAL_HASH_SIZE, 1); 52965 add_shape_property(ctx, &ctx->array_shape, NULL, 52966 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH); 52967 52968 /* XXX: could test it on first context creation to ensure that no 52969 new atoms are created in JS_AddIntrinsicBasicObjects(). It is 52970 necessary to avoid useless renumbering of atoms after 52971 JS_EvalBinary() if it is done just after 52972 JS_AddIntrinsicBasicObjects(). */ 52973 // assert(ctx->rt->atom_count == JS_ATOM_END); 52974 } 52975 52976 void JS_AddIntrinsicBaseObjects(JSContext *ctx) 52977 { 52978 int i; 52979 JSValueConst obj, number_obj; 52980 JSValue obj1; 52981 52982 ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0); 52983 52984 /* add caller and arguments properties to throw a TypeError */ 52985 obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0); 52986 JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED, 52987 obj1, ctx->throw_type_error, 52988 JS_PROP_HAS_GET | JS_PROP_HAS_SET | 52989 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); 52990 JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED, 52991 obj1, ctx->throw_type_error, 52992 JS_PROP_HAS_GET | JS_PROP_HAS_SET | 52993 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); 52994 JS_FreeValue(ctx, obj1); 52995 JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); 52996 52997 ctx->global_obj = JS_NewObject(ctx); 52998 ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); 52999 53000 /* Object */ 53001 obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1, 53002 ctx->class_proto[JS_CLASS_OBJECT]); 53003 JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs)); 53004 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT], 53005 js_object_proto_funcs, countof(js_object_proto_funcs)); 53006 53007 /* Function */ 53008 JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs)); 53009 ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor, 53010 "Function", 1, JS_CFUNC_constructor_or_func_magic, 53011 JS_FUNC_NORMAL); 53012 JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function", 53013 ctx->function_proto); 53014 53015 /* Error */ 53016 obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor, 53017 "Error", 1, JS_CFUNC_constructor_or_func_magic, -1); 53018 JS_NewGlobalCConstructor2(ctx, obj1, 53019 "Error", ctx->class_proto[JS_CLASS_ERROR]); 53020 53021 /* Used to squelch a -Wcast-function-type warning. */ 53022 JSCFunctionType ft = { .generic_magic = js_error_constructor }; 53023 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { 53024 JSValue func_obj; 53025 int n_args; 53026 n_args = 1 + (i == JS_AGGREGATE_ERROR); 53027 func_obj = JS_NewCFunction3(ctx, ft.generic, 53028 native_error_name[i], n_args, 53029 JS_CFUNC_constructor_or_func_magic, i, obj1); 53030 JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i], 53031 ctx->native_error_proto[i]); 53032 } 53033 53034 /* Iterator prototype */ 53035 ctx->iterator_proto = JS_NewObject(ctx); 53036 JS_SetPropertyFunctionList(ctx, ctx->iterator_proto, 53037 js_iterator_proto_funcs, 53038 countof(js_iterator_proto_funcs)); 53039 53040 /* Array */ 53041 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY], 53042 js_array_proto_funcs, 53043 countof(js_array_proto_funcs)); 53044 53045 obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1, 53046 ctx->class_proto[JS_CLASS_ARRAY]); 53047 ctx->array_ctor = JS_DupValue(ctx, obj); 53048 JS_SetPropertyFunctionList(ctx, obj, js_array_funcs, 53049 countof(js_array_funcs)); 53050 53051 /* XXX: create auto_initializer */ 53052 { 53053 /* initialize Array.prototype[Symbol.unscopables] */ 53054 static const char unscopables[] = 53055 "copyWithin" "\0" 53056 "entries" "\0" 53057 "fill" "\0" 53058 "find" "\0" 53059 "findIndex" "\0" 53060 "findLast" "\0" 53061 "findLastIndex" "\0" 53062 "flat" "\0" 53063 "flatMap" "\0" 53064 "includes" "\0" 53065 "keys" "\0" 53066 "toReversed" "\0" 53067 "toSorted" "\0" 53068 "toSpliced" "\0" 53069 "values" "\0"; 53070 const char *p = unscopables; 53071 obj1 = JS_NewObjectProto(ctx, JS_NULL); 53072 for(p = unscopables; *p; p += strlen(p) + 1) { 53073 JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E); 53074 } 53075 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY], 53076 JS_ATOM_Symbol_unscopables, obj1, 53077 JS_PROP_CONFIGURABLE); 53078 } 53079 53080 /* needed to initialize arguments[Symbol.iterator] */ 53081 ctx->array_proto_values = 53082 JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values); 53083 53084 ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); 53085 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR], 53086 js_array_iterator_proto_funcs, 53087 countof(js_array_iterator_proto_funcs)); 53088 53089 /* parseFloat and parseInteger must be defined before Number 53090 because of the Number.parseFloat and Number.parseInteger 53091 aliases */ 53092 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs, 53093 countof(js_global_funcs)); 53094 53095 /* Number */ 53096 ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 53097 JS_CLASS_NUMBER); 53098 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0)); 53099 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER], 53100 js_number_proto_funcs, 53101 countof(js_number_proto_funcs)); 53102 number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1, 53103 ctx->class_proto[JS_CLASS_NUMBER]); 53104 JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs)); 53105 53106 /* Boolean */ 53107 ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 53108 JS_CLASS_BOOLEAN); 53109 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE)); 53110 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs, 53111 countof(js_boolean_proto_funcs)); 53112 JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1, 53113 ctx->class_proto[JS_CLASS_BOOLEAN]); 53114 53115 /* String */ 53116 ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], 53117 JS_CLASS_STRING); 53118 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string)); 53119 obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1, 53120 ctx->class_proto[JS_CLASS_STRING]); 53121 JS_SetPropertyFunctionList(ctx, obj, js_string_funcs, 53122 countof(js_string_funcs)); 53123 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs, 53124 countof(js_string_proto_funcs)); 53125 53126 ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); 53127 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR], 53128 js_string_iterator_proto_funcs, 53129 countof(js_string_iterator_proto_funcs)); 53130 53131 /* Math: create as autoinit object */ 53132 js_random_init(ctx); 53133 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj)); 53134 53135 /* ES6 Reflect: create as autoinit object */ 53136 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj)); 53137 53138 /* ES6 Symbol */ 53139 ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx); 53140 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs, 53141 countof(js_symbol_proto_funcs)); 53142 obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0, 53143 ctx->class_proto[JS_CLASS_SYMBOL]); 53144 JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs, 53145 countof(js_symbol_funcs)); 53146 for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) { 53147 char buf[ATOM_GET_STR_BUF_SIZE]; 53148 const char *str, *p; 53149 str = JS_AtomGetStr(ctx, buf, sizeof(buf), i); 53150 /* skip "Symbol." */ 53151 p = strchr(str, '.'); 53152 if (p) 53153 str = p + 1; 53154 JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0); 53155 } 53156 53157 /* ES6 Generator */ 53158 ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto); 53159 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR], 53160 js_generator_proto_funcs, 53161 countof(js_generator_proto_funcs)); 53162 53163 ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto); 53164 obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor, 53165 "GeneratorFunction", 1, 53166 JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR); 53167 JS_SetPropertyFunctionList(ctx, 53168 ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], 53169 js_generator_function_proto_funcs, 53170 countof(js_generator_function_proto_funcs)); 53171 JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], 53172 ctx->class_proto[JS_CLASS_GENERATOR], 53173 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE); 53174 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION], 53175 0, JS_PROP_CONFIGURABLE); 53176 JS_FreeValue(ctx, obj1); 53177 53178 /* global properties */ 53179 ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1); 53180 JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval, 53181 JS_DupValue(ctx, ctx->eval_obj), 53182 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 53183 53184 JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis, 53185 JS_DupValue(ctx, ctx->global_obj), 53186 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); 53187 } 53188 53189 /* Typed Arrays */ 53190 53191 static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { 53192 0, 0, 0, 1, 1, 2, 2, 53193 3, 3, /* BigInt64Array, BigUint64Array */ 53194 2, 3 53195 }; 53196 53197 static JSValue js_array_buffer_constructor3(JSContext *ctx, 53198 JSValueConst new_target, 53199 uint64_t len, JSClassID class_id, 53200 uint8_t *buf, 53201 JSFreeArrayBufferDataFunc *free_func, 53202 void *opaque, BOOL alloc_flag) 53203 { 53204 JSRuntime *rt = ctx->rt; 53205 JSValue obj; 53206 JSArrayBuffer *abuf = NULL; 53207 53208 obj = js_create_from_ctor(ctx, new_target, class_id); 53209 if (JS_IsException(obj)) 53210 return obj; 53211 /* XXX: we are currently limited to 2 GB */ 53212 if (len > INT32_MAX) { 53213 JS_ThrowRangeError(ctx, "invalid array buffer length"); 53214 goto fail; 53215 } 53216 abuf = js_malloc(ctx, sizeof(*abuf)); 53217 if (!abuf) 53218 goto fail; 53219 abuf->byte_length = len; 53220 if (alloc_flag) { 53221 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && 53222 rt->sab_funcs.sab_alloc) { 53223 abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque, 53224 max_int(len, 1)); 53225 if (!abuf->data) 53226 goto fail; 53227 memset(abuf->data, 0, len); 53228 } else { 53229 /* the allocation must be done after the object creation */ 53230 abuf->data = js_mallocz(ctx, max_int(len, 1)); 53231 if (!abuf->data) 53232 goto fail; 53233 } 53234 } else { 53235 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER && 53236 rt->sab_funcs.sab_dup) { 53237 rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf); 53238 } 53239 abuf->data = buf; 53240 } 53241 init_list_head(&abuf->array_list); 53242 abuf->detached = FALSE; 53243 abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER); 53244 abuf->opaque = opaque; 53245 abuf->free_func = free_func; 53246 if (alloc_flag && buf) 53247 memcpy(abuf->data, buf, len); 53248 JS_SetOpaque(obj, abuf); 53249 return obj; 53250 fail: 53251 JS_FreeValue(ctx, obj); 53252 js_free(ctx, abuf); 53253 return JS_EXCEPTION; 53254 } 53255 53256 static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr) 53257 { 53258 js_free_rt(rt, ptr); 53259 } 53260 53261 static JSValue js_array_buffer_constructor2(JSContext *ctx, 53262 JSValueConst new_target, 53263 uint64_t len, JSClassID class_id) 53264 { 53265 return js_array_buffer_constructor3(ctx, new_target, len, class_id, 53266 NULL, js_array_buffer_free, NULL, 53267 TRUE); 53268 } 53269 53270 static JSValue js_array_buffer_constructor1(JSContext *ctx, 53271 JSValueConst new_target, 53272 uint64_t len) 53273 { 53274 return js_array_buffer_constructor2(ctx, new_target, len, 53275 JS_CLASS_ARRAY_BUFFER); 53276 } 53277 53278 JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, 53279 JSFreeArrayBufferDataFunc *free_func, void *opaque, 53280 BOOL is_shared) 53281 { 53282 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, 53283 is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER, 53284 buf, free_func, opaque, FALSE); 53285 } 53286 53287 /* create a new ArrayBuffer of length 'len' and copy 'buf' to it */ 53288 JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len) 53289 { 53290 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len, 53291 JS_CLASS_ARRAY_BUFFER, 53292 (uint8_t *)buf, 53293 js_array_buffer_free, NULL, 53294 TRUE); 53295 } 53296 53297 static JSValue js_array_buffer_constructor(JSContext *ctx, 53298 JSValueConst new_target, 53299 int argc, JSValueConst *argv) 53300 { 53301 uint64_t len; 53302 if (JS_ToIndex(ctx, &len, argv[0])) 53303 return JS_EXCEPTION; 53304 return js_array_buffer_constructor1(ctx, new_target, len); 53305 } 53306 53307 static JSValue js_shared_array_buffer_constructor(JSContext *ctx, 53308 JSValueConst new_target, 53309 int argc, JSValueConst *argv) 53310 { 53311 uint64_t len; 53312 if (JS_ToIndex(ctx, &len, argv[0])) 53313 return JS_EXCEPTION; 53314 return js_array_buffer_constructor2(ctx, new_target, len, 53315 JS_CLASS_SHARED_ARRAY_BUFFER); 53316 } 53317 53318 /* also used for SharedArrayBuffer */ 53319 static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) 53320 { 53321 JSObject *p = JS_VALUE_GET_OBJ(val); 53322 JSArrayBuffer *abuf = p->u.array_buffer; 53323 struct list_head *el, *el1; 53324 53325 if (abuf) { 53326 /* The ArrayBuffer finalizer may be called before the typed 53327 array finalizers using it, so abuf->array_list is not 53328 necessarily empty. */ 53329 list_for_each_safe(el, el1, &abuf->array_list) { 53330 JSTypedArray *ta; 53331 JSObject *p1; 53332 53333 ta = list_entry(el, JSTypedArray, link); 53334 ta->link.prev = NULL; 53335 ta->link.next = NULL; 53336 p1 = ta->obj; 53337 /* Note: the typed array length and offset fields are not modified */ 53338 if (p1->class_id != JS_CLASS_DATAVIEW) { 53339 p1->u.array.count = 0; 53340 p1->u.array.u.ptr = NULL; 53341 } 53342 } 53343 if (abuf->shared && rt->sab_funcs.sab_free) { 53344 rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); 53345 } else { 53346 if (abuf->free_func) 53347 abuf->free_func(rt, abuf->opaque, abuf->data); 53348 } 53349 js_free_rt(rt, abuf); 53350 } 53351 } 53352 53353 static JSValue js_array_buffer_isView(JSContext *ctx, 53354 JSValueConst this_val, 53355 int argc, JSValueConst *argv) 53356 { 53357 JSObject *p; 53358 BOOL res; 53359 res = FALSE; 53360 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) { 53361 p = JS_VALUE_GET_OBJ(argv[0]); 53362 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 53363 p->class_id <= JS_CLASS_DATAVIEW) { 53364 res = TRUE; 53365 } 53366 } 53367 return JS_NewBool(ctx, res); 53368 } 53369 53370 static const JSCFunctionListEntry js_array_buffer_funcs[] = { 53371 JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ), 53372 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 53373 }; 53374 53375 static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx) 53376 { 53377 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached"); 53378 } 53379 53380 static JSValue js_array_buffer_get_byteLength(JSContext *ctx, 53381 JSValueConst this_val, 53382 int class_id) 53383 { 53384 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id); 53385 if (!abuf) 53386 return JS_EXCEPTION; 53387 /* return 0 if detached */ 53388 return JS_NewUint32(ctx, abuf->byte_length); 53389 } 53390 53391 void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj) 53392 { 53393 JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER); 53394 struct list_head *el; 53395 53396 if (!abuf || abuf->detached) 53397 return; 53398 if (abuf->free_func) 53399 abuf->free_func(ctx->rt, abuf->opaque, abuf->data); 53400 abuf->data = NULL; 53401 abuf->byte_length = 0; 53402 abuf->detached = TRUE; 53403 53404 list_for_each(el, &abuf->array_list) { 53405 JSTypedArray *ta; 53406 JSObject *p; 53407 53408 ta = list_entry(el, JSTypedArray, link); 53409 p = ta->obj; 53410 /* Note: the typed array length and offset fields are not modified */ 53411 if (p->class_id != JS_CLASS_DATAVIEW) { 53412 p->u.array.count = 0; 53413 p->u.array.u.ptr = NULL; 53414 } 53415 } 53416 } 53417 53418 /* check if obj is ArrayBuffer or SharedArrayBuffer */ 53419 static BOOL js_is_array_buffer(JSContext *ctx, JSValueConst obj) 53420 { 53421 JSObject *p; 53422 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 53423 return FALSE; 53424 p = JS_VALUE_GET_OBJ(obj); 53425 if (p->class_id != JS_CLASS_ARRAY_BUFFER && 53426 p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { 53427 return FALSE; 53428 } 53429 return TRUE; 53430 } 53431 53432 /* get an ArrayBuffer or SharedArrayBuffer */ 53433 static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj) 53434 { 53435 JSObject *p; 53436 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 53437 goto fail; 53438 p = JS_VALUE_GET_OBJ(obj); 53439 if (p->class_id != JS_CLASS_ARRAY_BUFFER && 53440 p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) { 53441 fail: 53442 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER); 53443 return NULL; 53444 } 53445 return p->u.array_buffer; 53446 } 53447 53448 static JSValue js_array_buffer_slice(JSContext *ctx, 53449 JSValueConst this_val, 53450 int argc, JSValueConst *argv, int class_id) 53451 { 53452 JSArrayBuffer *abuf, *new_abuf; 53453 int64_t len, start, end, new_len; 53454 JSValue ctor, new_obj; 53455 53456 abuf = JS_GetOpaque2(ctx, this_val, class_id); 53457 if (!abuf) 53458 return JS_EXCEPTION; 53459 if (abuf->detached) 53460 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53461 len = abuf->byte_length; 53462 53463 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) 53464 return JS_EXCEPTION; 53465 53466 end = len; 53467 if (!JS_IsUndefined(argv[1])) { 53468 if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len)) 53469 return JS_EXCEPTION; 53470 } 53471 new_len = max_int64(end - start, 0); 53472 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED); 53473 if (JS_IsException(ctor)) 53474 return ctor; 53475 if (JS_IsUndefined(ctor)) { 53476 new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len, 53477 class_id); 53478 } else { 53479 JSValue args[1]; 53480 args[0] = JS_NewInt64(ctx, new_len); 53481 new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args); 53482 JS_FreeValue(ctx, ctor); 53483 JS_FreeValue(ctx, args[0]); 53484 } 53485 if (JS_IsException(new_obj)) 53486 return new_obj; 53487 new_abuf = JS_GetOpaque2(ctx, new_obj, class_id); 53488 if (!new_abuf) 53489 goto fail; 53490 if (js_same_value(ctx, new_obj, this_val)) { 53491 JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer"); 53492 goto fail; 53493 } 53494 if (new_abuf->detached) { 53495 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53496 goto fail; 53497 } 53498 if (new_abuf->byte_length < new_len) { 53499 JS_ThrowTypeError(ctx, "new ArrayBuffer is too small"); 53500 goto fail; 53501 } 53502 /* must test again because of side effects */ 53503 if (abuf->detached) { 53504 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53505 goto fail; 53506 } 53507 memcpy(new_abuf->data, abuf->data + start, new_len); 53508 return new_obj; 53509 fail: 53510 JS_FreeValue(ctx, new_obj); 53511 return JS_EXCEPTION; 53512 } 53513 53514 static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = { 53515 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ), 53516 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ), 53517 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ), 53518 }; 53519 53520 /* SharedArrayBuffer */ 53521 53522 static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = { 53523 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 53524 }; 53525 53526 static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = { 53527 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ), 53528 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ), 53529 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ), 53530 }; 53531 53532 static JSObject *get_typed_array(JSContext *ctx, 53533 JSValueConst this_val, 53534 int is_dataview) 53535 { 53536 JSObject *p; 53537 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 53538 goto fail; 53539 p = JS_VALUE_GET_OBJ(this_val); 53540 if (is_dataview) { 53541 if (p->class_id != JS_CLASS_DATAVIEW) 53542 goto fail; 53543 } else { 53544 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && 53545 p->class_id <= JS_CLASS_FLOAT64_ARRAY)) { 53546 fail: 53547 JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray"); 53548 return NULL; 53549 } 53550 } 53551 return p; 53552 } 53553 53554 /* WARNING: 'p' must be a typed array */ 53555 static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p) 53556 { 53557 JSTypedArray *ta = p->u.typed_array; 53558 JSArrayBuffer *abuf = ta->buffer->u.array_buffer; 53559 /* XXX: could simplify test by ensuring that 53560 p->u.array.u.ptr is NULL iff it is detached */ 53561 return abuf->detached; 53562 } 53563 53564 /* WARNING: 'p' must be a typed array. Works even if the array buffer 53565 is detached */ 53566 static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p) 53567 { 53568 JSTypedArray *ta = p->u.typed_array; 53569 int size_log2 = typed_array_size_log2(p->class_id); 53570 return ta->length >> size_log2; 53571 } 53572 53573 static int validate_typed_array(JSContext *ctx, JSValueConst this_val) 53574 { 53575 JSObject *p; 53576 p = get_typed_array(ctx, this_val, 0); 53577 if (!p) 53578 return -1; 53579 if (typed_array_is_detached(ctx, p)) { 53580 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53581 return -1; 53582 } 53583 return 0; 53584 } 53585 53586 static JSValue js_typed_array_get_length(JSContext *ctx, 53587 JSValueConst this_val) 53588 { 53589 JSObject *p; 53590 p = get_typed_array(ctx, this_val, 0); 53591 if (!p) 53592 return JS_EXCEPTION; 53593 return JS_NewInt32(ctx, p->u.array.count); 53594 } 53595 53596 static JSValue js_typed_array_get_buffer(JSContext *ctx, 53597 JSValueConst this_val, int is_dataview) 53598 { 53599 JSObject *p; 53600 JSTypedArray *ta; 53601 p = get_typed_array(ctx, this_val, is_dataview); 53602 if (!p) 53603 return JS_EXCEPTION; 53604 ta = p->u.typed_array; 53605 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 53606 } 53607 53608 static JSValue js_typed_array_get_byteLength(JSContext *ctx, 53609 JSValueConst this_val, 53610 int is_dataview) 53611 { 53612 JSObject *p; 53613 JSTypedArray *ta; 53614 p = get_typed_array(ctx, this_val, is_dataview); 53615 if (!p) 53616 return JS_EXCEPTION; 53617 if (typed_array_is_detached(ctx, p)) { 53618 if (is_dataview) { 53619 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53620 } else { 53621 return JS_NewInt32(ctx, 0); 53622 } 53623 } 53624 ta = p->u.typed_array; 53625 return JS_NewInt32(ctx, ta->length); 53626 } 53627 53628 static JSValue js_typed_array_get_byteOffset(JSContext *ctx, 53629 JSValueConst this_val, 53630 int is_dataview) 53631 { 53632 JSObject *p; 53633 JSTypedArray *ta; 53634 p = get_typed_array(ctx, this_val, is_dataview); 53635 if (!p) 53636 return JS_EXCEPTION; 53637 if (typed_array_is_detached(ctx, p)) { 53638 if (is_dataview) { 53639 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53640 } else { 53641 return JS_NewInt32(ctx, 0); 53642 } 53643 } 53644 ta = p->u.typed_array; 53645 return JS_NewInt32(ctx, ta->offset); 53646 } 53647 53648 JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, 53649 JSTypedArrayEnum type) 53650 { 53651 if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64) 53652 return JS_ThrowRangeError(ctx, "invalid typed array type"); 53653 53654 return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv, 53655 JS_CLASS_UINT8C_ARRAY + type); 53656 } 53657 53658 /* Return the buffer associated to the typed array or an exception if 53659 it is not a typed array or if the buffer is detached. pbyte_offset, 53660 pbyte_length or pbytes_per_element can be NULL. */ 53661 JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, 53662 size_t *pbyte_offset, 53663 size_t *pbyte_length, 53664 size_t *pbytes_per_element) 53665 { 53666 JSObject *p; 53667 JSTypedArray *ta; 53668 p = get_typed_array(ctx, obj, FALSE); 53669 if (!p) 53670 return JS_EXCEPTION; 53671 if (typed_array_is_detached(ctx, p)) 53672 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53673 ta = p->u.typed_array; 53674 if (pbyte_offset) 53675 *pbyte_offset = ta->offset; 53676 if (pbyte_length) 53677 *pbyte_length = ta->length; 53678 if (pbytes_per_element) { 53679 *pbytes_per_element = 1 << typed_array_size_log2(p->class_id); 53680 } 53681 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 53682 } 53683 53684 BOOL JS_IsArrayBuffer(JSValueConst obj) 53685 { 53686 JSObject *p; 53687 53688 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 53689 return FALSE; 53690 p = JS_VALUE_GET_OBJ(obj); 53691 if (p->class_id == JS_CLASS_ARRAY_BUFFER || 53692 p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { 53693 return TRUE; 53694 } 53695 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 53696 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 53697 return TRUE; 53698 } 53699 return FALSE; 53700 } 53701 53702 /* return NULL if exception. WARNING: any JS call can detach the 53703 buffer and render the returned pointer invalid */ 53704 uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj) 53705 { 53706 if (js_is_array_buffer(ctx, obj)) { 53707 JSArrayBuffer* abuf = js_get_array_buffer(ctx, obj); 53708 if (!abuf) 53709 goto fail; 53710 if (abuf->detached) { 53711 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53712 goto fail; 53713 } 53714 *psize = abuf->byte_length; 53715 return abuf->data; 53716 } else { 53717 JSObject* p; 53718 JSTypedArray* ta; 53719 JSArrayBuffer* abuf; 53720 p = get_typed_array(ctx, obj, FALSE); 53721 if (!p) { 53722 goto fail; 53723 } 53724 if (typed_array_is_detached(ctx, p)) { 53725 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53726 goto fail; 53727 } 53728 ta = p->u.typed_array; 53729 abuf = ta->buffer->u.array_buffer; 53730 if (!abuf) 53731 goto fail; 53732 if (abuf->detached) { 53733 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53734 goto fail; 53735 } 53736 *psize = ta->length; 53737 return abuf->data + ta->offset; 53738 } 53739 JS_ThrowTypeError(ctx, "expected ArrayBuffer or ArrayBufferView"); 53740 fail: 53741 *psize = 0; 53742 return NULL; 53743 } 53744 53745 53746 static JSValue js_typed_array_get_toStringTag(JSContext *ctx, 53747 JSValueConst this_val) 53748 { 53749 JSObject *p; 53750 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) 53751 return JS_UNDEFINED; 53752 p = JS_VALUE_GET_OBJ(this_val); 53753 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY && 53754 p->class_id <= JS_CLASS_FLOAT64_ARRAY)) 53755 return JS_UNDEFINED; 53756 return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name); 53757 } 53758 53759 static JSValue js_typed_array_set_internal(JSContext *ctx, 53760 JSValueConst dst, 53761 JSValueConst src, 53762 JSValueConst off) 53763 { 53764 JSObject *p; 53765 JSObject *src_p; 53766 uint32_t i; 53767 int64_t src_len, offset; 53768 JSValue val, src_obj = JS_UNDEFINED; 53769 53770 p = get_typed_array(ctx, dst, 0); 53771 if (!p) 53772 goto fail; 53773 if (JS_ToInt64Sat(ctx, &offset, off)) 53774 goto fail; 53775 if (offset < 0) 53776 goto range_error; 53777 if (typed_array_is_detached(ctx, p)) { 53778 detached: 53779 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53780 goto fail; 53781 } 53782 src_obj = JS_ToObject(ctx, src); 53783 if (JS_IsException(src_obj)) 53784 goto fail; 53785 src_p = JS_VALUE_GET_OBJ(src_obj); 53786 if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY && 53787 src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 53788 JSTypedArray *dest_ta = p->u.typed_array; 53789 JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer; 53790 JSTypedArray *src_ta = src_p->u.typed_array; 53791 JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer; 53792 int shift = typed_array_size_log2(p->class_id); 53793 53794 if (src_abuf->detached) 53795 goto detached; 53796 53797 src_len = src_p->u.array.count; 53798 if (offset > (int64_t)(p->u.array.count - src_len)) 53799 goto range_error; 53800 53801 /* copying between typed objects */ 53802 if (src_p->class_id == p->class_id) { 53803 /* same type, use memmove */ 53804 memmove(dest_abuf->data + dest_ta->offset + (offset << shift), 53805 src_abuf->data + src_ta->offset, src_len << shift); 53806 goto done; 53807 } 53808 if (dest_abuf->data == src_abuf->data) { 53809 /* copying between the same buffer using different types of mappings 53810 would require a temporary buffer */ 53811 } 53812 /* otherwise, default behavior is slow but correct */ 53813 } else { 53814 if (js_get_length64(ctx, &src_len, src_obj)) 53815 goto fail; 53816 if (offset > (int64_t)(p->u.array.count - src_len)) { 53817 range_error: 53818 JS_ThrowRangeError(ctx, "invalid array length"); 53819 goto fail; 53820 } 53821 } 53822 for(i = 0; i < src_len; i++) { 53823 val = JS_GetPropertyUint32(ctx, src_obj, i); 53824 if (JS_IsException(val)) 53825 goto fail; 53826 if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0) 53827 goto fail; 53828 } 53829 done: 53830 JS_FreeValue(ctx, src_obj); 53831 return JS_UNDEFINED; 53832 fail: 53833 JS_FreeValue(ctx, src_obj); 53834 return JS_EXCEPTION; 53835 } 53836 53837 static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, 53838 int argc, JSValueConst *argv) 53839 { 53840 JSObject *p; 53841 int64_t idx, len; 53842 53843 p = get_typed_array(ctx, this_val, 0); 53844 if (!p) 53845 return JS_EXCEPTION; 53846 53847 if (typed_array_is_detached(ctx, p)) { 53848 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53849 return JS_EXCEPTION; 53850 } 53851 53852 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 53853 return JS_EXCEPTION; 53854 53855 len = p->u.array.count; 53856 if (idx < 0) 53857 idx = len + idx; 53858 if (idx < 0 || idx >= len) 53859 return JS_UNDEFINED; 53860 return JS_GetPropertyInt64(ctx, this_val, idx); 53861 } 53862 53863 static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val, 53864 int argc, JSValueConst *argv) 53865 { 53866 JSValue arr, val; 53867 JSObject *p; 53868 int64_t idx, len; 53869 53870 p = get_typed_array(ctx, this_val, /*is_dataview*/0); 53871 if (!p) 53872 return JS_EXCEPTION; 53873 53874 if (JS_ToInt64Sat(ctx, &idx, argv[0])) 53875 return JS_EXCEPTION; 53876 53877 len = p->u.array.count; 53878 if (idx < 0) 53879 idx = len + idx; 53880 if (idx < 0 || idx >= len) 53881 return JS_ThrowRangeError(ctx, "invalid array index"); 53882 53883 val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); 53884 if (JS_IsException(val)) 53885 return JS_EXCEPTION; 53886 53887 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 53888 p->class_id); 53889 if (JS_IsException(arr)) { 53890 JS_FreeValue(ctx, val); 53891 return JS_EXCEPTION; 53892 } 53893 if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { 53894 JS_FreeValue(ctx, arr); 53895 return JS_EXCEPTION; 53896 } 53897 return arr; 53898 } 53899 53900 static JSValue js_typed_array_set(JSContext *ctx, 53901 JSValueConst this_val, 53902 int argc, JSValueConst *argv) 53903 { 53904 JSValueConst offset = JS_UNDEFINED; 53905 if (argc > 1) { 53906 offset = argv[1]; 53907 } 53908 return js_typed_array_set_internal(ctx, this_val, argv[0], offset); 53909 } 53910 53911 static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val, 53912 int argc, JSValueConst *argv, int magic) 53913 { 53914 if (validate_typed_array(ctx, this_val)) 53915 return JS_EXCEPTION; 53916 return js_create_array_iterator(ctx, this_val, argc, argv, magic); 53917 } 53918 53919 /* return < 0 if exception */ 53920 static int js_typed_array_get_length_internal(JSContext *ctx, 53921 JSValueConst obj) 53922 { 53923 JSObject *p; 53924 p = get_typed_array(ctx, obj, 0); 53925 if (!p) 53926 return -1; 53927 if (typed_array_is_detached(ctx, p)) { 53928 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 53929 return -1; 53930 } 53931 return p->u.array.count; 53932 } 53933 53934 #if 0 53935 /* validate a typed array and return its length */ 53936 static JSValue js_typed_array___getLength(JSContext *ctx, 53937 JSValueConst this_val, 53938 int argc, JSValueConst *argv) 53939 { 53940 BOOL ignore_detached = JS_ToBool(ctx, argv[1]); 53941 53942 if (ignore_detached) { 53943 return js_typed_array_get_length(ctx, argv[0]); 53944 } else { 53945 int len; 53946 len = js_typed_array_get_length_internal(ctx, argv[0]); 53947 if (len < 0) 53948 return JS_EXCEPTION; 53949 return JS_NewInt32(ctx, len); 53950 } 53951 } 53952 #endif 53953 53954 static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor, 53955 int argc, JSValueConst *argv) 53956 { 53957 JSValue ret; 53958 int new_len; 53959 int64_t len; 53960 53961 ret = JS_CallConstructor(ctx, ctor, argc, argv); 53962 if (JS_IsException(ret)) 53963 return ret; 53964 /* validate the typed array */ 53965 new_len = js_typed_array_get_length_internal(ctx, ret); 53966 if (new_len < 0) 53967 goto fail; 53968 if (argc == 1) { 53969 /* ensure that it is large enough */ 53970 if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]))) 53971 goto fail; 53972 if (new_len < len) { 53973 JS_ThrowTypeError(ctx, "TypedArray length is too small"); 53974 fail: 53975 JS_FreeValue(ctx, ret); 53976 return JS_EXCEPTION; 53977 } 53978 } 53979 return ret; 53980 } 53981 53982 #if 0 53983 static JSValue js_typed_array___create(JSContext *ctx, 53984 JSValueConst this_val, 53985 int argc, JSValueConst *argv) 53986 { 53987 return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1); 53988 } 53989 #endif 53990 53991 static JSValue js_typed_array___speciesCreate(JSContext *ctx, 53992 JSValueConst this_val, 53993 int argc, JSValueConst *argv) 53994 { 53995 JSValueConst obj; 53996 JSObject *p; 53997 JSValue ctor, ret; 53998 int argc1; 53999 54000 obj = argv[0]; 54001 p = get_typed_array(ctx, obj, 0); 54002 if (!p) 54003 return JS_EXCEPTION; 54004 ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED); 54005 if (JS_IsException(ctor)) 54006 return ctor; 54007 argc1 = max_int(argc - 1, 0); 54008 if (JS_IsUndefined(ctor)) { 54009 ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1, 54010 p->class_id); 54011 } else { 54012 ret = js_typed_array_create(ctx, ctor, argc1, argv + 1); 54013 JS_FreeValue(ctx, ctor); 54014 } 54015 return ret; 54016 } 54017 54018 static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val, 54019 int argc, JSValueConst *argv) 54020 { 54021 // from(items, mapfn = void 0, this_arg = void 0) 54022 JSValueConst items = argv[0], mapfn, this_arg; 54023 JSValueConst args[2]; 54024 JSValue stack[2]; 54025 JSValue iter, arr, r, v, v2; 54026 int64_t k, len; 54027 int done, mapping; 54028 54029 mapping = FALSE; 54030 mapfn = JS_UNDEFINED; 54031 this_arg = JS_UNDEFINED; 54032 r = JS_UNDEFINED; 54033 arr = JS_UNDEFINED; 54034 stack[0] = JS_UNDEFINED; 54035 stack[1] = JS_UNDEFINED; 54036 54037 if (argc > 1) { 54038 mapfn = argv[1]; 54039 if (!JS_IsUndefined(mapfn)) { 54040 if (check_function(ctx, mapfn)) 54041 goto exception; 54042 mapping = 1; 54043 if (argc > 2) 54044 this_arg = argv[2]; 54045 } 54046 } 54047 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator); 54048 if (JS_IsException(iter)) 54049 goto exception; 54050 if (!JS_IsUndefined(iter)) { 54051 JS_FreeValue(ctx, iter); 54052 arr = JS_NewArray(ctx); 54053 if (JS_IsException(arr)) 54054 goto exception; 54055 stack[0] = JS_DupValue(ctx, items); 54056 if (js_for_of_start(ctx, &stack[1], FALSE)) 54057 goto exception; 54058 for (k = 0;; k++) { 54059 v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done); 54060 if (JS_IsException(v)) 54061 goto exception_close; 54062 if (done) 54063 break; 54064 if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0) 54065 goto exception_close; 54066 } 54067 } else { 54068 arr = JS_ToObject(ctx, items); 54069 if (JS_IsException(arr)) 54070 goto exception; 54071 } 54072 if (js_get_length64(ctx, &len, arr) < 0) 54073 goto exception; 54074 v = JS_NewInt64(ctx, len); 54075 args[0] = v; 54076 r = js_typed_array_create(ctx, this_val, 1, args); 54077 JS_FreeValue(ctx, v); 54078 if (JS_IsException(r)) 54079 goto exception; 54080 for(k = 0; k < len; k++) { 54081 v = JS_GetPropertyInt64(ctx, arr, k); 54082 if (JS_IsException(v)) 54083 goto exception; 54084 if (mapping) { 54085 args[0] = v; 54086 args[1] = JS_NewInt32(ctx, k); 54087 v2 = JS_Call(ctx, mapfn, this_arg, 2, args); 54088 JS_FreeValue(ctx, v); 54089 v = v2; 54090 if (JS_IsException(v)) 54091 goto exception; 54092 } 54093 if (JS_SetPropertyInt64(ctx, r, k, v) < 0) 54094 goto exception; 54095 } 54096 goto done; 54097 54098 exception_close: 54099 if (!JS_IsUndefined(stack[0])) 54100 JS_IteratorClose(ctx, stack[0], TRUE); 54101 exception: 54102 JS_FreeValue(ctx, r); 54103 r = JS_EXCEPTION; 54104 done: 54105 JS_FreeValue(ctx, arr); 54106 JS_FreeValue(ctx, stack[0]); 54107 JS_FreeValue(ctx, stack[1]); 54108 return r; 54109 } 54110 54111 static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val, 54112 int argc, JSValueConst *argv) 54113 { 54114 JSValue obj; 54115 JSValueConst args[1]; 54116 int i; 54117 54118 args[0] = JS_NewInt32(ctx, argc); 54119 obj = js_typed_array_create(ctx, this_val, 1, args); 54120 if (JS_IsException(obj)) 54121 return obj; 54122 54123 for(i = 0; i < argc; i++) { 54124 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) { 54125 JS_FreeValue(ctx, obj); 54126 return JS_EXCEPTION; 54127 } 54128 } 54129 return obj; 54130 } 54131 54132 static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val, 54133 int argc, JSValueConst *argv) 54134 { 54135 JSObject *p; 54136 int len, to, from, final, count, shift; 54137 54138 len = js_typed_array_get_length_internal(ctx, this_val); 54139 if (len < 0) 54140 return JS_EXCEPTION; 54141 54142 if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len)) 54143 return JS_EXCEPTION; 54144 54145 if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len)) 54146 return JS_EXCEPTION; 54147 54148 final = len; 54149 if (argc > 2 && !JS_IsUndefined(argv[2])) { 54150 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) 54151 return JS_EXCEPTION; 54152 } 54153 54154 count = min_int(final - from, len - to); 54155 if (count > 0) { 54156 p = JS_VALUE_GET_OBJ(this_val); 54157 if (typed_array_is_detached(ctx, p)) 54158 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 54159 shift = typed_array_size_log2(p->class_id); 54160 memmove(p->u.array.u.uint8_ptr + (to << shift), 54161 p->u.array.u.uint8_ptr + (from << shift), 54162 count << shift); 54163 } 54164 return JS_DupValue(ctx, this_val); 54165 } 54166 54167 static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, 54168 int argc, JSValueConst *argv) 54169 { 54170 JSObject *p; 54171 int len, k, final, shift; 54172 uint64_t v64; 54173 54174 len = js_typed_array_get_length_internal(ctx, this_val); 54175 if (len < 0) 54176 return JS_EXCEPTION; 54177 p = JS_VALUE_GET_OBJ(this_val); 54178 54179 if (p->class_id == JS_CLASS_UINT8C_ARRAY) { 54180 int32_t v; 54181 if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0]))) 54182 return JS_EXCEPTION; 54183 v64 = v; 54184 } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) { 54185 uint32_t v; 54186 if (JS_ToUint32(ctx, &v, argv[0])) 54187 return JS_EXCEPTION; 54188 v64 = v; 54189 } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { 54190 if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) 54191 return JS_EXCEPTION; 54192 } else { 54193 double d; 54194 if (JS_ToFloat64(ctx, &d, argv[0])) 54195 return JS_EXCEPTION; 54196 if (p->class_id == JS_CLASS_FLOAT32_ARRAY) { 54197 union { 54198 float f; 54199 uint32_t u32; 54200 } u; 54201 u.f = d; 54202 v64 = u.u32; 54203 } else { 54204 JSFloat64Union u; 54205 u.d = d; 54206 v64 = u.u64; 54207 } 54208 } 54209 54210 k = 0; 54211 if (argc > 1) { 54212 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) 54213 return JS_EXCEPTION; 54214 } 54215 54216 final = len; 54217 if (argc > 2 && !JS_IsUndefined(argv[2])) { 54218 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len)) 54219 return JS_EXCEPTION; 54220 } 54221 54222 if (typed_array_is_detached(ctx, p)) 54223 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 54224 54225 shift = typed_array_size_log2(p->class_id); 54226 switch(shift) { 54227 case 0: 54228 if (k < final) { 54229 memset(p->u.array.u.uint8_ptr + k, v64, final - k); 54230 } 54231 break; 54232 case 1: 54233 for(; k < final; k++) { 54234 p->u.array.u.uint16_ptr[k] = v64; 54235 } 54236 break; 54237 case 2: 54238 for(; k < final; k++) { 54239 p->u.array.u.uint32_ptr[k] = v64; 54240 } 54241 break; 54242 case 3: 54243 for(; k < final; k++) { 54244 p->u.array.u.uint64_ptr[k] = v64; 54245 } 54246 break; 54247 default: 54248 abort(); 54249 } 54250 return JS_DupValue(ctx, this_val); 54251 } 54252 54253 static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, 54254 int argc, JSValueConst *argv, int mode) 54255 { 54256 JSValueConst func, this_arg; 54257 JSValueConst args[3]; 54258 JSValue val, index_val, res; 54259 int len, k, end; 54260 int dir; 54261 54262 val = JS_UNDEFINED; 54263 len = js_typed_array_get_length_internal(ctx, this_val); 54264 if (len < 0) 54265 goto exception; 54266 54267 func = argv[0]; 54268 if (check_function(ctx, func)) 54269 goto exception; 54270 54271 this_arg = JS_UNDEFINED; 54272 if (argc > 1) 54273 this_arg = argv[1]; 54274 54275 k = 0; 54276 dir = 1; 54277 end = len; 54278 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { 54279 k = len - 1; 54280 dir = -1; 54281 end = -1; 54282 } 54283 54284 for(; k != end; k += dir) { 54285 index_val = JS_NewInt32(ctx, k); 54286 val = JS_GetPropertyValue(ctx, this_val, index_val); 54287 if (JS_IsException(val)) 54288 goto exception; 54289 args[0] = val; 54290 args[1] = index_val; 54291 args[2] = this_val; 54292 res = JS_Call(ctx, func, this_arg, 3, args); 54293 if (JS_IsException(res)) 54294 goto exception; 54295 if (JS_ToBoolFree(ctx, res)) { 54296 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { 54297 JS_FreeValue(ctx, val); 54298 return index_val; 54299 } else { 54300 return val; 54301 } 54302 } 54303 JS_FreeValue(ctx, val); 54304 } 54305 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) 54306 return JS_NewInt32(ctx, -1); 54307 else 54308 return JS_UNDEFINED; 54309 54310 exception: 54311 JS_FreeValue(ctx, val); 54312 return JS_EXCEPTION; 54313 } 54314 54315 #define special_indexOf 0 54316 #define special_lastIndexOf 1 54317 #define special_includes -1 54318 54319 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, 54320 int argc, JSValueConst *argv, int special) 54321 { 54322 JSObject *p; 54323 int len, tag, is_int, is_bigint, k, stop, inc, res = -1; 54324 int64_t v64; 54325 double d; 54326 float f; 54327 54328 len = js_typed_array_get_length_internal(ctx, this_val); 54329 if (len < 0) 54330 goto exception; 54331 if (len == 0) 54332 goto done; 54333 54334 if (special == special_lastIndexOf) { 54335 k = len - 1; 54336 if (argc > 1) { 54337 if (JS_ToFloat64(ctx, &d, argv[1])) 54338 goto exception; 54339 if (isnan(d)) { 54340 k = 0; 54341 } else { 54342 if (d >= 0) { 54343 if (d < k) { 54344 k = d; 54345 } 54346 } else { 54347 d += len; 54348 if (d < 0) 54349 goto done; 54350 k = d; 54351 } 54352 } 54353 } 54354 stop = -1; 54355 inc = -1; 54356 } else { 54357 k = 0; 54358 if (argc > 1) { 54359 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len)) 54360 goto exception; 54361 } 54362 stop = len; 54363 inc = 1; 54364 } 54365 54366 p = JS_VALUE_GET_OBJ(this_val); 54367 /* if the array was detached, no need to go further (but no 54368 exception is raised) */ 54369 if (typed_array_is_detached(ctx, p)) { 54370 /* "includes" scans all the properties, so "undefined" can match */ 54371 if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0) 54372 res = 0; 54373 goto done; 54374 } 54375 54376 is_bigint = 0; 54377 is_int = 0; /* avoid warning */ 54378 v64 = 0; /* avoid warning */ 54379 tag = JS_VALUE_GET_NORM_TAG(argv[0]); 54380 if (tag == JS_TAG_INT) { 54381 is_int = 1; 54382 v64 = JS_VALUE_GET_INT(argv[0]); 54383 d = v64; 54384 } else 54385 if (tag == JS_TAG_FLOAT64) { 54386 d = JS_VALUE_GET_FLOAT64(argv[0]); 54387 if (d >= INT64_MIN && d < 0x1p63) { 54388 v64 = d; 54389 is_int = (v64 == d); 54390 } 54391 } else if (tag == JS_TAG_BIG_INT) { 54392 JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]); 54393 54394 if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { 54395 if (bf_get_int64(&v64, &p1->num, 0) != 0) 54396 goto done; 54397 } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) { 54398 if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0) 54399 goto done; 54400 } else { 54401 goto done; 54402 } 54403 d = 0; 54404 is_bigint = 1; 54405 } else { 54406 goto done; 54407 } 54408 54409 switch (p->class_id) { 54410 case JS_CLASS_INT8_ARRAY: 54411 if (is_int && (int8_t)v64 == v64) 54412 goto scan8; 54413 break; 54414 case JS_CLASS_UINT8C_ARRAY: 54415 case JS_CLASS_UINT8_ARRAY: 54416 if (is_int && (uint8_t)v64 == v64) { 54417 const uint8_t *pv, *pp; 54418 uint16_t v; 54419 scan8: 54420 pv = p->u.array.u.uint8_ptr; 54421 v = v64; 54422 if (inc > 0) { 54423 pp = memchr(pv + k, v, len - k); 54424 if (pp) 54425 res = pp - pv; 54426 } else { 54427 for (; k != stop; k += inc) { 54428 if (pv[k] == v) { 54429 res = k; 54430 break; 54431 } 54432 } 54433 } 54434 } 54435 break; 54436 case JS_CLASS_INT16_ARRAY: 54437 if (is_int && (int16_t)v64 == v64) 54438 goto scan16; 54439 break; 54440 case JS_CLASS_UINT16_ARRAY: 54441 if (is_int && (uint16_t)v64 == v64) { 54442 const uint16_t *pv; 54443 uint16_t v; 54444 scan16: 54445 pv = p->u.array.u.uint16_ptr; 54446 v = v64; 54447 for (; k != stop; k += inc) { 54448 if (pv[k] == v) { 54449 res = k; 54450 break; 54451 } 54452 } 54453 } 54454 break; 54455 case JS_CLASS_INT32_ARRAY: 54456 if (is_int && (int32_t)v64 == v64) 54457 goto scan32; 54458 break; 54459 case JS_CLASS_UINT32_ARRAY: 54460 if (is_int && (uint32_t)v64 == v64) { 54461 const uint32_t *pv; 54462 uint32_t v; 54463 scan32: 54464 pv = p->u.array.u.uint32_ptr; 54465 v = v64; 54466 for (; k != stop; k += inc) { 54467 if (pv[k] == v) { 54468 res = k; 54469 break; 54470 } 54471 } 54472 } 54473 break; 54474 case JS_CLASS_FLOAT32_ARRAY: 54475 if (is_bigint) 54476 break; 54477 if (isnan(d)) { 54478 const float *pv = p->u.array.u.float_ptr; 54479 /* special case: indexOf returns -1, includes finds NaN */ 54480 if (special != special_includes) 54481 goto done; 54482 for (; k != stop; k += inc) { 54483 if (isnan(pv[k])) { 54484 res = k; 54485 break; 54486 } 54487 } 54488 } else if ((f = (float)d) == d) { 54489 const float *pv = p->u.array.u.float_ptr; 54490 for (; k != stop; k += inc) { 54491 if (pv[k] == f) { 54492 res = k; 54493 break; 54494 } 54495 } 54496 } 54497 break; 54498 case JS_CLASS_FLOAT64_ARRAY: 54499 if (is_bigint) 54500 break; 54501 if (isnan(d)) { 54502 const double *pv = p->u.array.u.double_ptr; 54503 /* special case: indexOf returns -1, includes finds NaN */ 54504 if (special != special_includes) 54505 goto done; 54506 for (; k != stop; k += inc) { 54507 if (isnan(pv[k])) { 54508 res = k; 54509 break; 54510 } 54511 } 54512 } else { 54513 const double *pv = p->u.array.u.double_ptr; 54514 for (; k != stop; k += inc) { 54515 if (pv[k] == d) { 54516 res = k; 54517 break; 54518 } 54519 } 54520 } 54521 break; 54522 case JS_CLASS_BIG_INT64_ARRAY: 54523 if (is_bigint || (is_math_mode(ctx) && is_int && 54524 v64 >= -MAX_SAFE_INTEGER && 54525 v64 <= MAX_SAFE_INTEGER)) { 54526 goto scan64; 54527 } 54528 break; 54529 case JS_CLASS_BIG_UINT64_ARRAY: 54530 if (is_bigint || (is_math_mode(ctx) && is_int && 54531 v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) { 54532 const uint64_t *pv; 54533 uint64_t v; 54534 scan64: 54535 pv = p->u.array.u.uint64_ptr; 54536 v = v64; 54537 for (; k != stop; k += inc) { 54538 if (pv[k] == v) { 54539 res = k; 54540 break; 54541 } 54542 } 54543 } 54544 break; 54545 } 54546 54547 done: 54548 if (special == special_includes) 54549 return JS_NewBool(ctx, res >= 0); 54550 else 54551 return JS_NewInt32(ctx, res); 54552 54553 exception: 54554 return JS_EXCEPTION; 54555 } 54556 54557 static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val, 54558 int argc, JSValueConst *argv, int toLocaleString) 54559 { 54560 JSValue sep = JS_UNDEFINED, el; 54561 StringBuffer b_s, *b = &b_s; 54562 JSString *p = NULL; 54563 int i, n; 54564 int c; 54565 54566 n = js_typed_array_get_length_internal(ctx, this_val); 54567 if (n < 0) 54568 goto exception; 54569 54570 c = ','; /* default separator */ 54571 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) { 54572 sep = JS_ToString(ctx, argv[0]); 54573 if (JS_IsException(sep)) 54574 goto exception; 54575 p = JS_VALUE_GET_STRING(sep); 54576 if (p->len == 1 && !p->is_wide_char) 54577 c = p->u.str8[0]; 54578 else 54579 c = -1; 54580 } 54581 string_buffer_init(ctx, b, 0); 54582 54583 /* XXX: optimize with direct access */ 54584 for(i = 0; i < n; i++) { 54585 if (i > 0) { 54586 if (c >= 0) { 54587 if (string_buffer_putc8(b, c)) 54588 goto fail; 54589 } else { 54590 if (string_buffer_concat(b, p, 0, p->len)) 54591 goto fail; 54592 } 54593 } 54594 el = JS_GetPropertyUint32(ctx, this_val, i); 54595 /* Can return undefined for example if the typed array is detached */ 54596 if (!JS_IsNull(el) && !JS_IsUndefined(el)) { 54597 if (JS_IsException(el)) 54598 goto fail; 54599 if (toLocaleString) { 54600 el = JS_ToLocaleStringFree(ctx, el); 54601 } 54602 if (string_buffer_concat_value_free(b, el)) 54603 goto fail; 54604 } 54605 } 54606 JS_FreeValue(ctx, sep); 54607 return string_buffer_end(b); 54608 54609 fail: 54610 string_buffer_free(b); 54611 JS_FreeValue(ctx, sep); 54612 exception: 54613 return JS_EXCEPTION; 54614 } 54615 54616 static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, 54617 int argc, JSValueConst *argv) 54618 { 54619 JSObject *p; 54620 int len; 54621 54622 len = js_typed_array_get_length_internal(ctx, this_val); 54623 if (len < 0) 54624 return JS_EXCEPTION; 54625 if (len > 0) { 54626 p = JS_VALUE_GET_OBJ(this_val); 54627 switch (typed_array_size_log2(p->class_id)) { 54628 case 0: 54629 { 54630 uint8_t *p1 = p->u.array.u.uint8_ptr; 54631 uint8_t *p2 = p1 + len - 1; 54632 while (p1 < p2) { 54633 uint8_t v = *p1; 54634 *p1++ = *p2; 54635 *p2-- = v; 54636 } 54637 } 54638 break; 54639 case 1: 54640 { 54641 uint16_t *p1 = p->u.array.u.uint16_ptr; 54642 uint16_t *p2 = p1 + len - 1; 54643 while (p1 < p2) { 54644 uint16_t v = *p1; 54645 *p1++ = *p2; 54646 *p2-- = v; 54647 } 54648 } 54649 break; 54650 case 2: 54651 { 54652 uint32_t *p1 = p->u.array.u.uint32_ptr; 54653 uint32_t *p2 = p1 + len - 1; 54654 while (p1 < p2) { 54655 uint32_t v = *p1; 54656 *p1++ = *p2; 54657 *p2-- = v; 54658 } 54659 } 54660 break; 54661 case 3: 54662 { 54663 uint64_t *p1 = p->u.array.u.uint64_ptr; 54664 uint64_t *p2 = p1 + len - 1; 54665 while (p1 < p2) { 54666 uint64_t v = *p1; 54667 *p1++ = *p2; 54668 *p2-- = v; 54669 } 54670 } 54671 break; 54672 default: 54673 abort(); 54674 } 54675 } 54676 return JS_DupValue(ctx, this_val); 54677 } 54678 54679 static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val, 54680 int argc, JSValueConst *argv) 54681 { 54682 JSValue arr, ret; 54683 JSObject *p; 54684 54685 p = get_typed_array(ctx, this_val, /*is_dataview*/0); 54686 if (!p) 54687 return JS_EXCEPTION; 54688 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 54689 p->class_id); 54690 if (JS_IsException(arr)) 54691 return JS_EXCEPTION; 54692 ret = js_typed_array_reverse(ctx, arr, argc, argv); 54693 JS_FreeValue(ctx, arr); 54694 return ret; 54695 } 54696 54697 static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, 54698 int argc, JSValueConst *argv) 54699 { 54700 JSValueConst args[2]; 54701 JSValue arr, val; 54702 JSObject *p, *p1; 54703 int n, len, start, final, count, shift; 54704 54705 arr = JS_UNDEFINED; 54706 len = js_typed_array_get_length_internal(ctx, this_val); 54707 if (len < 0) 54708 goto exception; 54709 54710 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) 54711 goto exception; 54712 final = len; 54713 if (!JS_IsUndefined(argv[1])) { 54714 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) 54715 goto exception; 54716 } 54717 count = max_int(final - start, 0); 54718 54719 p = get_typed_array(ctx, this_val, 0); 54720 if (p == NULL) 54721 goto exception; 54722 shift = typed_array_size_log2(p->class_id); 54723 54724 args[0] = this_val; 54725 args[1] = JS_NewInt32(ctx, count); 54726 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args); 54727 if (JS_IsException(arr)) 54728 goto exception; 54729 54730 if (count > 0) { 54731 if (validate_typed_array(ctx, this_val) 54732 || validate_typed_array(ctx, arr)) 54733 goto exception; 54734 54735 p1 = get_typed_array(ctx, arr, 0); 54736 if (p1 != NULL && p->class_id == p1->class_id && 54737 typed_array_get_length(ctx, p1) >= count && 54738 typed_array_get_length(ctx, p) >= start + count) { 54739 memcpy(p1->u.array.u.uint8_ptr, 54740 p->u.array.u.uint8_ptr + (start << shift), 54741 count << shift); 54742 } else { 54743 for (n = 0; n < count; n++) { 54744 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n)); 54745 if (JS_IsException(val)) 54746 goto exception; 54747 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val, 54748 JS_PROP_THROW) < 0) 54749 goto exception; 54750 } 54751 } 54752 } 54753 return arr; 54754 54755 exception: 54756 JS_FreeValue(ctx, arr); 54757 return JS_EXCEPTION; 54758 } 54759 54760 static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val, 54761 int argc, JSValueConst *argv) 54762 { 54763 JSValueConst args[4]; 54764 JSValue arr, byteOffset, ta_buffer; 54765 JSObject *p; 54766 int len, start, final, count, shift, offset; 54767 54768 p = get_typed_array(ctx, this_val, 0); 54769 if (!p) 54770 goto exception; 54771 len = p->u.array.count; 54772 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) 54773 goto exception; 54774 54775 final = len; 54776 if (!JS_IsUndefined(argv[1])) { 54777 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len)) 54778 goto exception; 54779 } 54780 count = max_int(final - start, 0); 54781 byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0); 54782 if (JS_IsException(byteOffset)) 54783 goto exception; 54784 shift = typed_array_size_log2(p->class_id); 54785 offset = JS_VALUE_GET_INT(byteOffset) + (start << shift); 54786 JS_FreeValue(ctx, byteOffset); 54787 ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0); 54788 if (JS_IsException(ta_buffer)) 54789 goto exception; 54790 args[0] = this_val; 54791 args[1] = ta_buffer; 54792 args[2] = JS_NewInt32(ctx, offset); 54793 args[3] = JS_NewInt32(ctx, count); 54794 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args); 54795 JS_FreeValue(ctx, ta_buffer); 54796 return arr; 54797 54798 exception: 54799 return JS_EXCEPTION; 54800 } 54801 54802 /* TypedArray.prototype.sort */ 54803 54804 static int js_cmp_doubles(double x, double y) 54805 { 54806 if (isnan(x)) return isnan(y) ? 0 : +1; 54807 if (isnan(y)) return -1; 54808 if (x < y) return -1; 54809 if (x > y) return 1; 54810 if (x != 0) return 0; 54811 if (signbit(x)) return signbit(y) ? 0 : -1; 54812 else return signbit(y) ? 1 : 0; 54813 } 54814 54815 static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) { 54816 return *(const int8_t *)a - *(const int8_t *)b; 54817 } 54818 54819 static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) { 54820 return *(const uint8_t *)a - *(const uint8_t *)b; 54821 } 54822 54823 static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) { 54824 return *(const int16_t *)a - *(const int16_t *)b; 54825 } 54826 54827 static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) { 54828 return *(const uint16_t *)a - *(const uint16_t *)b; 54829 } 54830 54831 static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) { 54832 int32_t x = *(const int32_t *)a; 54833 int32_t y = *(const int32_t *)b; 54834 return (y < x) - (y > x); 54835 } 54836 54837 static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { 54838 uint32_t x = *(const uint32_t *)a; 54839 uint32_t y = *(const uint32_t *)b; 54840 return (y < x) - (y > x); 54841 } 54842 54843 static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { 54844 int64_t x = *(const int64_t *)a; 54845 int64_t y = *(const int64_t *)b; 54846 return (y < x) - (y > x); 54847 } 54848 54849 static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { 54850 uint64_t x = *(const uint64_t *)a; 54851 uint64_t y = *(const uint64_t *)b; 54852 return (y < x) - (y > x); 54853 } 54854 54855 static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { 54856 return js_cmp_doubles(*(const float *)a, *(const float *)b); 54857 } 54858 54859 static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) { 54860 return js_cmp_doubles(*(const double *)a, *(const double *)b); 54861 } 54862 54863 static JSValue js_TA_get_int8(JSContext *ctx, const void *a) { 54864 return JS_NewInt32(ctx, *(const int8_t *)a); 54865 } 54866 54867 static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) { 54868 return JS_NewInt32(ctx, *(const uint8_t *)a); 54869 } 54870 54871 static JSValue js_TA_get_int16(JSContext *ctx, const void *a) { 54872 return JS_NewInt32(ctx, *(const int16_t *)a); 54873 } 54874 54875 static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) { 54876 return JS_NewInt32(ctx, *(const uint16_t *)a); 54877 } 54878 54879 static JSValue js_TA_get_int32(JSContext *ctx, const void *a) { 54880 return JS_NewInt32(ctx, *(const int32_t *)a); 54881 } 54882 54883 static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { 54884 return JS_NewUint32(ctx, *(const uint32_t *)a); 54885 } 54886 54887 static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { 54888 return JS_NewBigInt64(ctx, *(int64_t *)a); 54889 } 54890 54891 static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { 54892 return JS_NewBigUint64(ctx, *(uint64_t *)a); 54893 } 54894 54895 static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { 54896 return __JS_NewFloat64(ctx, *(const float *)a); 54897 } 54898 54899 static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { 54900 return __JS_NewFloat64(ctx, *(const double *)a); 54901 } 54902 54903 struct TA_sort_context { 54904 JSContext *ctx; 54905 int exception; /* 1 = exception, 2 = detached typed array */ 54906 JSValueConst arr; 54907 JSValueConst cmp; 54908 JSValue (*getfun)(JSContext *ctx, const void *a); 54909 uint8_t *array_ptr; /* cannot change unless the array is detached */ 54910 int elt_size; 54911 }; 54912 54913 static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { 54914 struct TA_sort_context *psc = opaque; 54915 JSContext *ctx = psc->ctx; 54916 uint32_t a_idx, b_idx; 54917 JSValueConst argv[2]; 54918 JSValue res; 54919 int cmp; 54920 54921 cmp = 0; 54922 if (!psc->exception) { 54923 /* Note: the typed array can be detached without causing an 54924 error */ 54925 a_idx = *(uint32_t *)a; 54926 b_idx = *(uint32_t *)b; 54927 argv[0] = psc->getfun(ctx, psc->array_ptr + 54928 a_idx * (size_t)psc->elt_size); 54929 argv[1] = psc->getfun(ctx, psc->array_ptr + 54930 b_idx * (size_t)(psc->elt_size)); 54931 res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv); 54932 if (JS_IsException(res)) { 54933 psc->exception = 1; 54934 goto done; 54935 } 54936 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) { 54937 int val = JS_VALUE_GET_INT(res); 54938 cmp = (val > 0) - (val < 0); 54939 } else { 54940 double val; 54941 if (JS_ToFloat64Free(ctx, &val, res) < 0) { 54942 psc->exception = 1; 54943 goto done; 54944 } else { 54945 cmp = (val > 0) - (val < 0); 54946 } 54947 } 54948 if (cmp == 0) { 54949 /* make sort stable: compare array offsets */ 54950 cmp = (a_idx > b_idx) - (a_idx < b_idx); 54951 } 54952 if (unlikely(typed_array_is_detached(ctx, 54953 JS_VALUE_GET_PTR(psc->arr)))) { 54954 psc->exception = 2; 54955 } 54956 done: 54957 JS_FreeValue(ctx, (JSValue)argv[0]); 54958 JS_FreeValue(ctx, (JSValue)argv[1]); 54959 } 54960 return cmp; 54961 } 54962 54963 static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, 54964 int argc, JSValueConst *argv) 54965 { 54966 JSObject *p; 54967 int len; 54968 size_t elt_size; 54969 struct TA_sort_context tsc; 54970 void *array_ptr; 54971 int (*cmpfun)(const void *a, const void *b, void *opaque); 54972 54973 tsc.ctx = ctx; 54974 tsc.exception = 0; 54975 tsc.arr = this_val; 54976 tsc.cmp = argv[0]; 54977 54978 if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) 54979 return JS_EXCEPTION; 54980 len = js_typed_array_get_length_internal(ctx, this_val); 54981 if (len < 0) 54982 return JS_EXCEPTION; 54983 54984 if (len > 1) { 54985 p = JS_VALUE_GET_OBJ(this_val); 54986 switch (p->class_id) { 54987 case JS_CLASS_INT8_ARRAY: 54988 tsc.getfun = js_TA_get_int8; 54989 cmpfun = js_TA_cmp_int8; 54990 break; 54991 case JS_CLASS_UINT8C_ARRAY: 54992 case JS_CLASS_UINT8_ARRAY: 54993 tsc.getfun = js_TA_get_uint8; 54994 cmpfun = js_TA_cmp_uint8; 54995 break; 54996 case JS_CLASS_INT16_ARRAY: 54997 tsc.getfun = js_TA_get_int16; 54998 cmpfun = js_TA_cmp_int16; 54999 break; 55000 case JS_CLASS_UINT16_ARRAY: 55001 tsc.getfun = js_TA_get_uint16; 55002 cmpfun = js_TA_cmp_uint16; 55003 break; 55004 case JS_CLASS_INT32_ARRAY: 55005 tsc.getfun = js_TA_get_int32; 55006 cmpfun = js_TA_cmp_int32; 55007 break; 55008 case JS_CLASS_UINT32_ARRAY: 55009 tsc.getfun = js_TA_get_uint32; 55010 cmpfun = js_TA_cmp_uint32; 55011 break; 55012 case JS_CLASS_BIG_INT64_ARRAY: 55013 tsc.getfun = js_TA_get_int64; 55014 cmpfun = js_TA_cmp_int64; 55015 break; 55016 case JS_CLASS_BIG_UINT64_ARRAY: 55017 tsc.getfun = js_TA_get_uint64; 55018 cmpfun = js_TA_cmp_uint64; 55019 break; 55020 case JS_CLASS_FLOAT32_ARRAY: 55021 tsc.getfun = js_TA_get_float32; 55022 cmpfun = js_TA_cmp_float32; 55023 break; 55024 case JS_CLASS_FLOAT64_ARRAY: 55025 tsc.getfun = js_TA_get_float64; 55026 cmpfun = js_TA_cmp_float64; 55027 break; 55028 default: 55029 abort(); 55030 } 55031 array_ptr = p->u.array.u.ptr; 55032 elt_size = 1 << typed_array_size_log2(p->class_id); 55033 if (!JS_IsUndefined(tsc.cmp)) { 55034 uint32_t *array_idx; 55035 void *array_tmp; 55036 size_t i, j; 55037 55038 /* XXX: a stable sort would use less memory */ 55039 array_idx = js_malloc(ctx, len * sizeof(array_idx[0])); 55040 if (!array_idx) 55041 return JS_EXCEPTION; 55042 for(i = 0; i < len; i++) 55043 array_idx[i] = i; 55044 tsc.array_ptr = array_ptr; 55045 tsc.elt_size = elt_size; 55046 rqsort(array_idx, len, sizeof(array_idx[0]), 55047 js_TA_cmp_generic, &tsc); 55048 if (tsc.exception) { 55049 if (tsc.exception == 1) 55050 goto fail; 55051 /* detached typed array during the sort: no error */ 55052 } else { 55053 array_tmp = js_malloc(ctx, len * elt_size); 55054 if (!array_tmp) { 55055 fail: 55056 js_free(ctx, array_idx); 55057 return JS_EXCEPTION; 55058 } 55059 memcpy(array_tmp, array_ptr, len * elt_size); 55060 switch(elt_size) { 55061 case 1: 55062 for(i = 0; i < len; i++) { 55063 j = array_idx[i]; 55064 ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; 55065 } 55066 break; 55067 case 2: 55068 for(i = 0; i < len; i++) { 55069 j = array_idx[i]; 55070 ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; 55071 } 55072 break; 55073 case 4: 55074 for(i = 0; i < len; i++) { 55075 j = array_idx[i]; 55076 ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; 55077 } 55078 break; 55079 case 8: 55080 for(i = 0; i < len; i++) { 55081 j = array_idx[i]; 55082 ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; 55083 } 55084 break; 55085 default: 55086 abort(); 55087 } 55088 js_free(ctx, array_tmp); 55089 } 55090 js_free(ctx, array_idx); 55091 } else { 55092 rqsort(array_ptr, len, elt_size, cmpfun, &tsc); 55093 if (tsc.exception) 55094 return JS_EXCEPTION; 55095 } 55096 } 55097 return JS_DupValue(ctx, this_val); 55098 } 55099 55100 static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val, 55101 int argc, JSValueConst *argv) 55102 { 55103 JSValue arr, ret; 55104 JSObject *p; 55105 55106 p = get_typed_array(ctx, this_val, /*is_dataview*/0); 55107 if (!p) 55108 return JS_EXCEPTION; 55109 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, 55110 p->class_id); 55111 if (JS_IsException(arr)) 55112 return JS_EXCEPTION; 55113 ret = js_typed_array_sort(ctx, arr, argc, argv); 55114 JS_FreeValue(ctx, arr); 55115 return ret; 55116 } 55117 55118 static const JSCFunctionListEntry js_typed_array_base_funcs[] = { 55119 JS_CFUNC_DEF("from", 1, js_typed_array_from ), 55120 JS_CFUNC_DEF("of", 0, js_typed_array_of ), 55121 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), 55122 //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ), 55123 //JS_CFUNC_DEF("__create", 2, js_typed_array___create ), 55124 //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ), 55125 }; 55126 55127 static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { 55128 JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), 55129 JS_CFUNC_DEF("at", 1, js_typed_array_at ), 55130 JS_CFUNC_DEF("with", 2, js_typed_array_with ), 55131 JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ), 55132 JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ), 55133 JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ), 55134 JS_CFUNC_DEF("set", 1, js_typed_array_set ), 55135 JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ), 55136 JS_ALIAS_DEF("[Symbol.iterator]", "values" ), 55137 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ), 55138 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ), 55139 JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ), 55140 JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ), 55141 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ), 55142 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ), 55143 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ), 55144 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ), 55145 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ), 55146 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), 55147 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), 55148 JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), 55149 JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ), 55150 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ), 55151 JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ), 55152 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ), 55153 JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), 55154 JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ), 55155 JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), 55156 JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), 55157 JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), 55158 JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ), 55159 JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), 55160 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), 55161 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), 55162 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ), 55163 JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ), 55164 //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@ 55165 }; 55166 55167 static JSValue js_typed_array_base_constructor(JSContext *ctx, 55168 JSValueConst this_val, 55169 int argc, JSValueConst *argv) 55170 { 55171 return JS_ThrowTypeError(ctx, "cannot be called"); 55172 } 55173 55174 /* 'obj' must be an allocated typed array object */ 55175 static int typed_array_init(JSContext *ctx, JSValueConst obj, 55176 JSValue buffer, uint64_t offset, uint64_t len) 55177 { 55178 JSTypedArray *ta; 55179 JSObject *p, *pbuffer; 55180 JSArrayBuffer *abuf; 55181 int size_log2; 55182 55183 p = JS_VALUE_GET_OBJ(obj); 55184 size_log2 = typed_array_size_log2(p->class_id); 55185 ta = js_malloc(ctx, sizeof(*ta)); 55186 if (!ta) { 55187 JS_FreeValue(ctx, buffer); 55188 return -1; 55189 } 55190 pbuffer = JS_VALUE_GET_OBJ(buffer); 55191 abuf = pbuffer->u.array_buffer; 55192 ta->obj = p; 55193 ta->buffer = pbuffer; 55194 ta->offset = offset; 55195 ta->length = len << size_log2; 55196 list_add_tail(&ta->link, &abuf->array_list); 55197 p->u.typed_array = ta; 55198 p->u.array.count = len; 55199 p->u.array.u.ptr = abuf->data + offset; 55200 return 0; 55201 } 55202 55203 JSValue JS_NewTypedArraySimple(JSContext *ctx, JSValue array_buf, size_t bytes_per_element) 55204 { 55205 JSValue obj; 55206 JSObject *p = JS_VALUE_GET_OBJ(array_buf); 55207 JSArrayBuffer *abuf = NULL; 55208 55209 if (p->class_id != JS_CLASS_ARRAY_BUFFER) { 55210 return JS_ThrowTypeError(ctx, "expected array buffer"); 55211 } 55212 abuf = p->u.array_buffer; 55213 if (abuf->detached) { 55214 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55215 } 55216 obj = JS_NewObjectClass(ctx, JS_CLASS_UINT8_ARRAY); 55217 if (typed_array_init(ctx, obj, array_buf, 0, abuf->byte_length)) { 55218 JS_FreeValue(ctx, obj); 55219 return JS_EXCEPTION; 55220 } 55221 return obj; 55222 } 55223 55224 55225 static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, 55226 JSValueConst obj, JSValueConst method) 55227 { 55228 JSValue arr, iter, next_method = JS_UNDEFINED, val; 55229 BOOL done; 55230 uint32_t k; 55231 55232 *plen = 0; 55233 arr = JS_NewArray(ctx); 55234 if (JS_IsException(arr)) 55235 return arr; 55236 iter = JS_GetIterator2(ctx, obj, method); 55237 if (JS_IsException(iter)) 55238 goto fail; 55239 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next); 55240 if (JS_IsException(next_method)) 55241 goto fail; 55242 k = 0; 55243 for(;;) { 55244 val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done); 55245 if (JS_IsException(val)) 55246 goto fail; 55247 if (done) { 55248 JS_FreeValue(ctx, val); 55249 break; 55250 } 55251 if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0) 55252 goto fail; 55253 k++; 55254 } 55255 JS_FreeValue(ctx, next_method); 55256 JS_FreeValue(ctx, iter); 55257 *plen = k; 55258 return arr; 55259 fail: 55260 JS_FreeValue(ctx, next_method); 55261 JS_FreeValue(ctx, iter); 55262 JS_FreeValue(ctx, arr); 55263 return JS_EXCEPTION; 55264 } 55265 55266 static JSValue js_typed_array_constructor_obj(JSContext *ctx, 55267 JSValueConst new_target, 55268 JSValueConst obj, 55269 int classid) 55270 { 55271 JSValue iter, ret, arr = JS_UNDEFINED, val, buffer; 55272 uint32_t i; 55273 int size_log2; 55274 int64_t len; 55275 55276 size_log2 = typed_array_size_log2(classid); 55277 ret = js_create_from_ctor(ctx, new_target, classid); 55278 if (JS_IsException(ret)) 55279 return JS_EXCEPTION; 55280 55281 iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); 55282 if (JS_IsException(iter)) 55283 goto fail; 55284 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) { 55285 uint32_t len1; 55286 arr = js_array_from_iterator(ctx, &len1, obj, iter); 55287 JS_FreeValue(ctx, iter); 55288 if (JS_IsException(arr)) 55289 goto fail; 55290 len = len1; 55291 } else { 55292 if (js_get_length64(ctx, &len, obj)) 55293 goto fail; 55294 arr = JS_DupValue(ctx, obj); 55295 } 55296 55297 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 55298 len << size_log2); 55299 if (JS_IsException(buffer)) 55300 goto fail; 55301 if (typed_array_init(ctx, ret, buffer, 0, len)) 55302 goto fail; 55303 55304 for(i = 0; i < len; i++) { 55305 val = JS_GetPropertyUint32(ctx, arr, i); 55306 if (JS_IsException(val)) 55307 goto fail; 55308 if (JS_SetPropertyUint32(ctx, ret, i, val) < 0) 55309 goto fail; 55310 } 55311 JS_FreeValue(ctx, arr); 55312 return ret; 55313 fail: 55314 JS_FreeValue(ctx, arr); 55315 JS_FreeValue(ctx, ret); 55316 return JS_EXCEPTION; 55317 } 55318 55319 static JSValue js_typed_array_constructor_ta(JSContext *ctx, 55320 JSValueConst new_target, 55321 JSValueConst src_obj, 55322 int classid) 55323 { 55324 JSObject *p, *src_buffer; 55325 JSTypedArray *ta; 55326 JSValue obj, buffer; 55327 uint32_t len, i; 55328 int size_log2; 55329 JSArrayBuffer *src_abuf, *abuf; 55330 55331 obj = js_create_from_ctor(ctx, new_target, classid); 55332 if (JS_IsException(obj)) 55333 return obj; 55334 p = JS_VALUE_GET_OBJ(src_obj); 55335 if (typed_array_is_detached(ctx, p)) { 55336 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55337 goto fail; 55338 } 55339 ta = p->u.typed_array; 55340 len = p->u.array.count; 55341 src_buffer = ta->buffer; 55342 src_abuf = src_buffer->u.array_buffer; 55343 size_log2 = typed_array_size_log2(classid); 55344 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 55345 (uint64_t)len << size_log2); 55346 if (JS_IsException(buffer)) 55347 goto fail; 55348 /* necessary because it could have been detached */ 55349 if (typed_array_is_detached(ctx, p)) { 55350 JS_FreeValue(ctx, buffer); 55351 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55352 goto fail; 55353 } 55354 abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER); 55355 if (typed_array_init(ctx, obj, buffer, 0, len)) 55356 goto fail; 55357 if (p->class_id == classid) { 55358 /* same type: copy the content */ 55359 memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length); 55360 } else { 55361 for(i = 0; i < len; i++) { 55362 JSValue val; 55363 val = JS_GetPropertyUint32(ctx, src_obj, i); 55364 if (JS_IsException(val)) 55365 goto fail; 55366 if (JS_SetPropertyUint32(ctx, obj, i, val) < 0) 55367 goto fail; 55368 } 55369 } 55370 return obj; 55371 fail: 55372 JS_FreeValue(ctx, obj); 55373 return JS_EXCEPTION; 55374 } 55375 55376 static JSValue js_typed_array_constructor(JSContext *ctx, 55377 JSValueConst new_target, 55378 int argc, JSValueConst *argv, 55379 int classid) 55380 { 55381 JSValue buffer, obj; 55382 JSArrayBuffer *abuf; 55383 int size_log2; 55384 uint64_t len, offset; 55385 55386 size_log2 = typed_array_size_log2(classid); 55387 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) { 55388 if (JS_ToIndex(ctx, &len, argv[0])) 55389 return JS_EXCEPTION; 55390 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, 55391 len << size_log2); 55392 if (JS_IsException(buffer)) 55393 return JS_EXCEPTION; 55394 offset = 0; 55395 } else { 55396 JSObject *p = JS_VALUE_GET_OBJ(argv[0]); 55397 if (p->class_id == JS_CLASS_ARRAY_BUFFER || 55398 p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) { 55399 abuf = p->u.array_buffer; 55400 if (JS_ToIndex(ctx, &offset, argv[1])) 55401 return JS_EXCEPTION; 55402 if (abuf->detached) 55403 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55404 if ((offset & ((1 << size_log2) - 1)) != 0 || 55405 offset > abuf->byte_length) 55406 return JS_ThrowRangeError(ctx, "invalid offset"); 55407 if (JS_IsUndefined(argv[2])) { 55408 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0) 55409 goto invalid_length; 55410 len = (abuf->byte_length - offset) >> size_log2; 55411 } else { 55412 if (JS_ToIndex(ctx, &len, argv[2])) 55413 return JS_EXCEPTION; 55414 if (abuf->detached) 55415 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55416 if ((offset + (len << size_log2)) > abuf->byte_length) { 55417 invalid_length: 55418 return JS_ThrowRangeError(ctx, "invalid length"); 55419 } 55420 } 55421 buffer = JS_DupValue(ctx, argv[0]); 55422 } else { 55423 if (p->class_id >= JS_CLASS_UINT8C_ARRAY && 55424 p->class_id <= JS_CLASS_FLOAT64_ARRAY) { 55425 return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid); 55426 } else { 55427 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid); 55428 } 55429 } 55430 } 55431 55432 obj = js_create_from_ctor(ctx, new_target, classid); 55433 if (JS_IsException(obj)) { 55434 JS_FreeValue(ctx, buffer); 55435 return JS_EXCEPTION; 55436 } 55437 if (typed_array_init(ctx, obj, buffer, offset, len)) { 55438 JS_FreeValue(ctx, obj); 55439 return JS_EXCEPTION; 55440 } 55441 return obj; 55442 } 55443 55444 static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) 55445 { 55446 JSObject *p = JS_VALUE_GET_OBJ(val); 55447 JSTypedArray *ta = p->u.typed_array; 55448 if (ta) { 55449 /* during the GC the finalizers are called in an arbitrary 55450 order so the ArrayBuffer finalizer may have been called */ 55451 if (ta->link.next) { 55452 list_del(&ta->link); 55453 } 55454 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); 55455 js_free_rt(rt, ta); 55456 } 55457 } 55458 55459 static void js_typed_array_mark(JSRuntime *rt, JSValueConst val, 55460 JS_MarkFunc *mark_func) 55461 { 55462 JSObject *p = JS_VALUE_GET_OBJ(val); 55463 JSTypedArray *ta = p->u.typed_array; 55464 if (ta) { 55465 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func); 55466 } 55467 } 55468 55469 static JSValue js_dataview_constructor(JSContext *ctx, 55470 JSValueConst new_target, 55471 int argc, JSValueConst *argv) 55472 { 55473 JSArrayBuffer *abuf; 55474 uint64_t offset; 55475 uint32_t len; 55476 JSValueConst buffer; 55477 JSValue obj; 55478 JSTypedArray *ta; 55479 JSObject *p; 55480 55481 buffer = argv[0]; 55482 abuf = js_get_array_buffer(ctx, buffer); 55483 if (!abuf) 55484 return JS_EXCEPTION; 55485 offset = 0; 55486 if (argc > 1) { 55487 if (JS_ToIndex(ctx, &offset, argv[1])) 55488 return JS_EXCEPTION; 55489 } 55490 if (abuf->detached) 55491 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55492 if (offset > abuf->byte_length) 55493 return JS_ThrowRangeError(ctx, "invalid byteOffset"); 55494 len = abuf->byte_length - offset; 55495 if (argc > 2 && !JS_IsUndefined(argv[2])) { 55496 uint64_t l; 55497 if (JS_ToIndex(ctx, &l, argv[2])) 55498 return JS_EXCEPTION; 55499 if (l > len) 55500 return JS_ThrowRangeError(ctx, "invalid byteLength"); 55501 len = l; 55502 } 55503 55504 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW); 55505 if (JS_IsException(obj)) 55506 return JS_EXCEPTION; 55507 if (abuf->detached) { 55508 /* could have been detached in js_create_from_ctor() */ 55509 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55510 goto fail; 55511 } 55512 ta = js_malloc(ctx, sizeof(*ta)); 55513 if (!ta) { 55514 fail: 55515 JS_FreeValue(ctx, obj); 55516 return JS_EXCEPTION; 55517 } 55518 p = JS_VALUE_GET_OBJ(obj); 55519 ta->obj = p; 55520 ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer)); 55521 ta->offset = offset; 55522 ta->length = len; 55523 list_add_tail(&ta->link, &abuf->array_list); 55524 p->u.typed_array = ta; 55525 return obj; 55526 } 55527 55528 static JSValue js_dataview_getValue(JSContext *ctx, 55529 JSValueConst this_obj, 55530 int argc, JSValueConst *argv, int class_id) 55531 { 55532 JSTypedArray *ta; 55533 JSArrayBuffer *abuf; 55534 BOOL littleEndian, is_swap; 55535 int size; 55536 uint8_t *ptr; 55537 uint32_t v; 55538 uint64_t pos; 55539 55540 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); 55541 if (!ta) 55542 return JS_EXCEPTION; 55543 size = 1 << typed_array_size_log2(class_id); 55544 if (JS_ToIndex(ctx, &pos, argv[0])) 55545 return JS_EXCEPTION; 55546 littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]); 55547 is_swap = littleEndian ^ !is_be(); 55548 abuf = ta->buffer->u.array_buffer; 55549 if (abuf->detached) 55550 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55551 if ((pos + size) > ta->length) 55552 return JS_ThrowRangeError(ctx, "out of bound"); 55553 ptr = abuf->data + ta->offset + pos; 55554 55555 switch(class_id) { 55556 case JS_CLASS_INT8_ARRAY: 55557 return JS_NewInt32(ctx, *(int8_t *)ptr); 55558 case JS_CLASS_UINT8_ARRAY: 55559 return JS_NewInt32(ctx, *(uint8_t *)ptr); 55560 case JS_CLASS_INT16_ARRAY: 55561 v = get_u16(ptr); 55562 if (is_swap) 55563 v = bswap16(v); 55564 return JS_NewInt32(ctx, (int16_t)v); 55565 case JS_CLASS_UINT16_ARRAY: 55566 v = get_u16(ptr); 55567 if (is_swap) 55568 v = bswap16(v); 55569 return JS_NewInt32(ctx, v); 55570 case JS_CLASS_INT32_ARRAY: 55571 v = get_u32(ptr); 55572 if (is_swap) 55573 v = bswap32(v); 55574 return JS_NewInt32(ctx, v); 55575 case JS_CLASS_UINT32_ARRAY: 55576 v = get_u32(ptr); 55577 if (is_swap) 55578 v = bswap32(v); 55579 return JS_NewUint32(ctx, v); 55580 case JS_CLASS_BIG_INT64_ARRAY: 55581 { 55582 uint64_t v; 55583 v = get_u64(ptr); 55584 if (is_swap) 55585 v = bswap64(v); 55586 return JS_NewBigInt64(ctx, v); 55587 } 55588 break; 55589 case JS_CLASS_BIG_UINT64_ARRAY: 55590 { 55591 uint64_t v; 55592 v = get_u64(ptr); 55593 if (is_swap) 55594 v = bswap64(v); 55595 return JS_NewBigUint64(ctx, v); 55596 } 55597 break; 55598 case JS_CLASS_FLOAT32_ARRAY: 55599 { 55600 union { 55601 float f; 55602 uint32_t i; 55603 } u; 55604 v = get_u32(ptr); 55605 if (is_swap) 55606 v = bswap32(v); 55607 u.i = v; 55608 return __JS_NewFloat64(ctx, u.f); 55609 } 55610 case JS_CLASS_FLOAT64_ARRAY: 55611 { 55612 union { 55613 double f; 55614 uint64_t i; 55615 } u; 55616 u.i = get_u64(ptr); 55617 if (is_swap) 55618 u.i = bswap64(u.i); 55619 return __JS_NewFloat64(ctx, u.f); 55620 } 55621 default: 55622 abort(); 55623 } 55624 } 55625 55626 static JSValue js_dataview_setValue(JSContext *ctx, 55627 JSValueConst this_obj, 55628 int argc, JSValueConst *argv, int class_id) 55629 { 55630 JSTypedArray *ta; 55631 JSArrayBuffer *abuf; 55632 BOOL littleEndian, is_swap; 55633 int size; 55634 uint8_t *ptr; 55635 uint64_t v64; 55636 uint32_t v; 55637 uint64_t pos; 55638 JSValueConst val; 55639 55640 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW); 55641 if (!ta) 55642 return JS_EXCEPTION; 55643 size = 1 << typed_array_size_log2(class_id); 55644 if (JS_ToIndex(ctx, &pos, argv[0])) 55645 return JS_EXCEPTION; 55646 val = argv[1]; 55647 v = 0; /* avoid warning */ 55648 v64 = 0; /* avoid warning */ 55649 if (class_id <= JS_CLASS_UINT32_ARRAY) { 55650 if (JS_ToUint32(ctx, &v, val)) 55651 return JS_EXCEPTION; 55652 } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { 55653 if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) 55654 return JS_EXCEPTION; 55655 } else { 55656 double d; 55657 if (JS_ToFloat64(ctx, &d, val)) 55658 return JS_EXCEPTION; 55659 if (class_id == JS_CLASS_FLOAT32_ARRAY) { 55660 union { 55661 float f; 55662 uint32_t i; 55663 } u; 55664 u.f = d; 55665 v = u.i; 55666 } else { 55667 JSFloat64Union u; 55668 u.d = d; 55669 v64 = u.u64; 55670 } 55671 } 55672 littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]); 55673 is_swap = littleEndian ^ !is_be(); 55674 abuf = ta->buffer->u.array_buffer; 55675 if (abuf->detached) 55676 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55677 if ((pos + size) > ta->length) 55678 return JS_ThrowRangeError(ctx, "out of bound"); 55679 ptr = abuf->data + ta->offset + pos; 55680 55681 switch(class_id) { 55682 case JS_CLASS_INT8_ARRAY: 55683 case JS_CLASS_UINT8_ARRAY: 55684 *ptr = v; 55685 break; 55686 case JS_CLASS_INT16_ARRAY: 55687 case JS_CLASS_UINT16_ARRAY: 55688 if (is_swap) 55689 v = bswap16(v); 55690 put_u16(ptr, v); 55691 break; 55692 case JS_CLASS_INT32_ARRAY: 55693 case JS_CLASS_UINT32_ARRAY: 55694 case JS_CLASS_FLOAT32_ARRAY: 55695 if (is_swap) 55696 v = bswap32(v); 55697 put_u32(ptr, v); 55698 break; 55699 case JS_CLASS_BIG_INT64_ARRAY: 55700 case JS_CLASS_BIG_UINT64_ARRAY: 55701 case JS_CLASS_FLOAT64_ARRAY: 55702 if (is_swap) 55703 v64 = bswap64(v64); 55704 put_u64(ptr, v64); 55705 break; 55706 default: 55707 abort(); 55708 } 55709 return JS_UNDEFINED; 55710 } 55711 55712 static const JSCFunctionListEntry js_dataview_proto_funcs[] = { 55713 JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ), 55714 JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ), 55715 JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ), 55716 JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ), 55717 JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ), 55718 JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ), 55719 JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), 55720 JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), 55721 JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), 55722 JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), 55723 JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), 55724 JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), 55725 JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), 55726 JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), 55727 JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ), 55728 JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ), 55729 JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), 55730 JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), 55731 JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), 55732 JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), 55733 JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), 55734 JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), 55735 JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), 55736 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), 55737 }; 55738 55739 /* Atomics */ 55740 #ifdef CONFIG_ATOMICS 55741 55742 typedef enum AtomicsOpEnum { 55743 ATOMICS_OP_ADD, 55744 ATOMICS_OP_AND, 55745 ATOMICS_OP_OR, 55746 ATOMICS_OP_SUB, 55747 ATOMICS_OP_XOR, 55748 ATOMICS_OP_EXCHANGE, 55749 ATOMICS_OP_COMPARE_EXCHANGE, 55750 ATOMICS_OP_LOAD, 55751 } AtomicsOpEnum; 55752 55753 static void *js_atomics_get_ptr(JSContext *ctx, 55754 JSArrayBuffer **pabuf, 55755 int *psize_log2, JSClassID *pclass_id, 55756 JSValueConst obj, JSValueConst idx_val, 55757 int is_waitable) 55758 { 55759 JSObject *p; 55760 JSTypedArray *ta; 55761 JSArrayBuffer *abuf; 55762 void *ptr; 55763 uint64_t idx; 55764 BOOL err; 55765 int size_log2; 55766 55767 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) 55768 goto fail; 55769 p = JS_VALUE_GET_OBJ(obj); 55770 if (is_waitable) 55771 err = (p->class_id != JS_CLASS_INT32_ARRAY && 55772 p->class_id != JS_CLASS_BIG_INT64_ARRAY); 55773 else 55774 err = !(p->class_id >= JS_CLASS_INT8_ARRAY && 55775 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); 55776 if (err) { 55777 fail: 55778 JS_ThrowTypeError(ctx, "integer TypedArray expected"); 55779 return NULL; 55780 } 55781 ta = p->u.typed_array; 55782 abuf = ta->buffer->u.array_buffer; 55783 if (!abuf->shared) { 55784 if (is_waitable == 2) { 55785 JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray"); 55786 return NULL; 55787 } 55788 if (abuf->detached) { 55789 JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55790 return NULL; 55791 } 55792 } 55793 if (JS_ToIndex(ctx, &idx, idx_val)) { 55794 return NULL; 55795 } 55796 /* if the array buffer is detached, p->u.array.count = 0 */ 55797 if (idx >= p->u.array.count) { 55798 JS_ThrowRangeError(ctx, "out-of-bound access"); 55799 return NULL; 55800 } 55801 size_log2 = typed_array_size_log2(p->class_id); 55802 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2); 55803 if (pabuf) 55804 *pabuf = abuf; 55805 if (psize_log2) 55806 *psize_log2 = size_log2; 55807 if (pclass_id) 55808 *pclass_id = p->class_id; 55809 return ptr; 55810 } 55811 55812 static JSValue js_atomics_op(JSContext *ctx, 55813 JSValueConst this_obj, 55814 int argc, JSValueConst *argv, int op) 55815 { 55816 int size_log2; 55817 uint64_t v, a, rep_val; 55818 void *ptr; 55819 JSValue ret; 55820 JSClassID class_id; 55821 JSArrayBuffer *abuf; 55822 55823 ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id, 55824 argv[0], argv[1], 0); 55825 if (!ptr) 55826 return JS_EXCEPTION; 55827 rep_val = 0; 55828 if (op == ATOMICS_OP_LOAD) { 55829 v = 0; 55830 } else { 55831 if (size_log2 == 3) { 55832 int64_t v64; 55833 if (JS_ToBigInt64(ctx, &v64, argv[2])) 55834 return JS_EXCEPTION; 55835 v = v64; 55836 if (op == ATOMICS_OP_COMPARE_EXCHANGE) { 55837 if (JS_ToBigInt64(ctx, &v64, argv[3])) 55838 return JS_EXCEPTION; 55839 rep_val = v64; 55840 } 55841 } else { 55842 uint32_t v32; 55843 if (JS_ToUint32(ctx, &v32, argv[2])) 55844 return JS_EXCEPTION; 55845 v = v32; 55846 if (op == ATOMICS_OP_COMPARE_EXCHANGE) { 55847 if (JS_ToUint32(ctx, &v32, argv[3])) 55848 return JS_EXCEPTION; 55849 rep_val = v32; 55850 } 55851 } 55852 if (abuf->detached) 55853 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55854 } 55855 55856 switch(op | (size_log2 << 3)) { 55857 55858 #define OP(op_name, func_name) \ 55859 case ATOMICS_OP_ ## op_name | (0 << 3): \ 55860 a = func_name((_Atomic(uint8_t) *)ptr, v); \ 55861 break; \ 55862 case ATOMICS_OP_ ## op_name | (1 << 3): \ 55863 a = func_name((_Atomic(uint16_t) *)ptr, v); \ 55864 break; \ 55865 case ATOMICS_OP_ ## op_name | (2 << 3): \ 55866 a = func_name((_Atomic(uint32_t) *)ptr, v); \ 55867 break; \ 55868 case ATOMICS_OP_ ## op_name | (3 << 3): \ 55869 a = func_name((_Atomic(uint64_t) *)ptr, v); \ 55870 break; 55871 55872 OP(ADD, atomic_fetch_add) 55873 OP(AND, atomic_fetch_and) 55874 OP(OR, atomic_fetch_or) 55875 OP(SUB, atomic_fetch_sub) 55876 OP(XOR, atomic_fetch_xor) 55877 OP(EXCHANGE, atomic_exchange) 55878 #undef OP 55879 55880 case ATOMICS_OP_LOAD | (0 << 3): 55881 a = atomic_load((_Atomic(uint8_t) *)ptr); 55882 break; 55883 case ATOMICS_OP_LOAD | (1 << 3): 55884 a = atomic_load((_Atomic(uint16_t) *)ptr); 55885 break; 55886 case ATOMICS_OP_LOAD | (2 << 3): 55887 a = atomic_load((_Atomic(uint32_t) *)ptr); 55888 break; 55889 case ATOMICS_OP_LOAD | (3 << 3): 55890 a = atomic_load((_Atomic(uint64_t) *)ptr); 55891 break; 55892 55893 case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): 55894 { 55895 uint8_t v1 = v; 55896 atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val); 55897 a = v1; 55898 } 55899 break; 55900 case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3): 55901 { 55902 uint16_t v1 = v; 55903 atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val); 55904 a = v1; 55905 } 55906 break; 55907 case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3): 55908 { 55909 uint32_t v1 = v; 55910 atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val); 55911 a = v1; 55912 } 55913 break; 55914 case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): 55915 { 55916 uint64_t v1 = v; 55917 atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val); 55918 a = v1; 55919 } 55920 break; 55921 default: 55922 abort(); 55923 } 55924 55925 switch(class_id) { 55926 case JS_CLASS_INT8_ARRAY: 55927 a = (int8_t)a; 55928 goto done; 55929 case JS_CLASS_UINT8_ARRAY: 55930 a = (uint8_t)a; 55931 goto done; 55932 case JS_CLASS_INT16_ARRAY: 55933 a = (int16_t)a; 55934 goto done; 55935 case JS_CLASS_UINT16_ARRAY: 55936 a = (uint16_t)a; 55937 goto done; 55938 case JS_CLASS_INT32_ARRAY: 55939 done: 55940 ret = JS_NewInt32(ctx, a); 55941 break; 55942 case JS_CLASS_UINT32_ARRAY: 55943 ret = JS_NewUint32(ctx, a); 55944 break; 55945 case JS_CLASS_BIG_INT64_ARRAY: 55946 ret = JS_NewBigInt64(ctx, a); 55947 break; 55948 case JS_CLASS_BIG_UINT64_ARRAY: 55949 ret = JS_NewBigUint64(ctx, a); 55950 break; 55951 default: 55952 abort(); 55953 } 55954 return ret; 55955 } 55956 55957 static JSValue js_atomics_store(JSContext *ctx, 55958 JSValueConst this_obj, 55959 int argc, JSValueConst *argv) 55960 { 55961 int size_log2; 55962 void *ptr; 55963 JSValue ret; 55964 JSArrayBuffer *abuf; 55965 55966 ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL, 55967 argv[0], argv[1], 0); 55968 if (!ptr) 55969 return JS_EXCEPTION; 55970 if (size_log2 == 3) { 55971 int64_t v64; 55972 ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); 55973 if (JS_IsException(ret)) 55974 return ret; 55975 if (JS_ToBigInt64(ctx, &v64, ret)) { 55976 JS_FreeValue(ctx, ret); 55977 return JS_EXCEPTION; 55978 } 55979 if (abuf->detached) 55980 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55981 atomic_store((_Atomic(uint64_t) *)ptr, v64); 55982 } else { 55983 uint32_t v; 55984 /* XXX: spec, would be simpler to return the written value */ 55985 ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); 55986 if (JS_IsException(ret)) 55987 return ret; 55988 if (JS_ToUint32(ctx, &v, ret)) { 55989 JS_FreeValue(ctx, ret); 55990 return JS_EXCEPTION; 55991 } 55992 if (abuf->detached) 55993 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 55994 switch(size_log2) { 55995 case 0: 55996 atomic_store((_Atomic(uint8_t) *)ptr, v); 55997 break; 55998 case 1: 55999 atomic_store((_Atomic(uint16_t) *)ptr, v); 56000 break; 56001 case 2: 56002 atomic_store((_Atomic(uint32_t) *)ptr, v); 56003 break; 56004 default: 56005 abort(); 56006 } 56007 } 56008 return ret; 56009 } 56010 56011 static JSValue js_atomics_isLockFree(JSContext *ctx, 56012 JSValueConst this_obj, 56013 int argc, JSValueConst *argv) 56014 { 56015 int v, ret; 56016 if (JS_ToInt32Sat(ctx, &v, argv[0])) 56017 return JS_EXCEPTION; 56018 ret = (v == 1 || v == 2 || v == 4 || v == 8); 56019 return JS_NewBool(ctx, ret); 56020 } 56021 56022 typedef struct JSAtomicsWaiter { 56023 struct list_head link; 56024 BOOL linked; 56025 pthread_cond_t cond; 56026 int32_t *ptr; 56027 } JSAtomicsWaiter; 56028 56029 static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER; 56030 static struct list_head js_atomics_waiter_list = 56031 LIST_HEAD_INIT(js_atomics_waiter_list); 56032 56033 static JSValue js_atomics_wait(JSContext *ctx, 56034 JSValueConst this_obj, 56035 int argc, JSValueConst *argv) 56036 { 56037 int64_t v; 56038 int32_t v32; 56039 void *ptr; 56040 int64_t timeout; 56041 struct timespec ts; 56042 JSAtomicsWaiter waiter_s, *waiter; 56043 int ret, size_log2, res; 56044 double d; 56045 56046 ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL, 56047 argv[0], argv[1], 2); 56048 if (!ptr) 56049 return JS_EXCEPTION; 56050 if (size_log2 == 3) { 56051 if (JS_ToBigInt64(ctx, &v, argv[2])) 56052 return JS_EXCEPTION; 56053 } else { 56054 if (JS_ToInt32(ctx, &v32, argv[2])) 56055 return JS_EXCEPTION; 56056 v = v32; 56057 } 56058 if (JS_ToFloat64(ctx, &d, argv[3])) 56059 return JS_EXCEPTION; 56060 /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ 56061 if (isnan(d) || d >= 0x1p63) 56062 timeout = INT64_MAX; 56063 else if (d < 0) 56064 timeout = 0; 56065 else 56066 timeout = (int64_t)d; 56067 if (!ctx->rt->can_block) 56068 return JS_ThrowTypeError(ctx, "cannot block in this thread"); 56069 56070 /* XXX: inefficient if large number of waiters, should hash on 56071 'ptr' value */ 56072 /* XXX: use Linux futexes when available ? */ 56073 pthread_mutex_lock(&js_atomics_mutex); 56074 if (size_log2 == 3) { 56075 res = *(int64_t *)ptr != v; 56076 } else { 56077 res = *(int32_t *)ptr != v; 56078 } 56079 if (res) { 56080 pthread_mutex_unlock(&js_atomics_mutex); 56081 return JS_AtomToString(ctx, JS_ATOM_not_equal); 56082 } 56083 56084 waiter = &waiter_s; 56085 waiter->ptr = ptr; 56086 pthread_cond_init(&waiter->cond, NULL); 56087 waiter->linked = TRUE; 56088 list_add_tail(&waiter->link, &js_atomics_waiter_list); 56089 56090 if (timeout == INT64_MAX) { 56091 pthread_cond_wait(&waiter->cond, &js_atomics_mutex); 56092 ret = 0; 56093 } else { 56094 /* XXX: use clock monotonic */ 56095 clock_gettime(CLOCK_REALTIME, &ts); 56096 ts.tv_sec += timeout / 1000; 56097 ts.tv_nsec += (timeout % 1000) * 1000000; 56098 if (ts.tv_nsec >= 1000000000) { 56099 ts.tv_nsec -= 1000000000; 56100 ts.tv_sec++; 56101 } 56102 ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex, 56103 &ts); 56104 } 56105 if (waiter->linked) 56106 list_del(&waiter->link); 56107 pthread_mutex_unlock(&js_atomics_mutex); 56108 pthread_cond_destroy(&waiter->cond); 56109 if (ret == ETIMEDOUT) { 56110 return JS_AtomToString(ctx, JS_ATOM_timed_out); 56111 } else { 56112 return JS_AtomToString(ctx, JS_ATOM_ok); 56113 } 56114 } 56115 56116 static JSValue js_atomics_notify(JSContext *ctx, 56117 JSValueConst this_obj, 56118 int argc, JSValueConst *argv) 56119 { 56120 struct list_head *el, *el1, waiter_list; 56121 int32_t count, n; 56122 void *ptr; 56123 JSAtomicsWaiter *waiter; 56124 JSArrayBuffer *abuf; 56125 56126 ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1); 56127 if (!ptr) 56128 return JS_EXCEPTION; 56129 56130 if (JS_IsUndefined(argv[2])) { 56131 count = INT32_MAX; 56132 } else { 56133 if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0)) 56134 return JS_EXCEPTION; 56135 } 56136 if (abuf->detached) 56137 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); 56138 56139 n = 0; 56140 if (abuf->shared && count > 0) { 56141 pthread_mutex_lock(&js_atomics_mutex); 56142 init_list_head(&waiter_list); 56143 list_for_each_safe(el, el1, &js_atomics_waiter_list) { 56144 waiter = list_entry(el, JSAtomicsWaiter, link); 56145 if (waiter->ptr == ptr) { 56146 list_del(&waiter->link); 56147 waiter->linked = FALSE; 56148 list_add_tail(&waiter->link, &waiter_list); 56149 n++; 56150 if (n >= count) 56151 break; 56152 } 56153 } 56154 list_for_each(el, &waiter_list) { 56155 waiter = list_entry(el, JSAtomicsWaiter, link); 56156 pthread_cond_signal(&waiter->cond); 56157 } 56158 pthread_mutex_unlock(&js_atomics_mutex); 56159 } 56160 return JS_NewInt32(ctx, n); 56161 } 56162 56163 static const JSCFunctionListEntry js_atomics_funcs[] = { 56164 JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ), 56165 JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ), 56166 JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ), 56167 JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ), 56168 JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ), 56169 JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ), 56170 JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ), 56171 JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ), 56172 JS_CFUNC_DEF("store", 3, js_atomics_store ), 56173 JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ), 56174 JS_CFUNC_DEF("wait", 4, js_atomics_wait ), 56175 JS_CFUNC_DEF("notify", 3, js_atomics_notify ), 56176 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ), 56177 }; 56178 56179 static const JSCFunctionListEntry js_atomics_obj[] = { 56180 JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), 56181 }; 56182 56183 void JS_AddIntrinsicAtomics(JSContext *ctx) 56184 { 56185 /* add Atomics as autoinit object */ 56186 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj)); 56187 } 56188 56189 #endif /* CONFIG_ATOMICS */ 56190 56191 void JS_AddIntrinsicTypedArrays(JSContext *ctx) 56192 { 56193 JSValue typed_array_base_proto, typed_array_base_func; 56194 JSValueConst array_buffer_func, shared_array_buffer_func; 56195 int i; 56196 56197 ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx); 56198 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER], 56199 js_array_buffer_proto_funcs, 56200 countof(js_array_buffer_proto_funcs)); 56201 56202 array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer", 56203 js_array_buffer_constructor, 1, 56204 ctx->class_proto[JS_CLASS_ARRAY_BUFFER]); 56205 JS_SetPropertyFunctionList(ctx, array_buffer_func, 56206 js_array_buffer_funcs, 56207 countof(js_array_buffer_funcs)); 56208 56209 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx); 56210 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER], 56211 js_shared_array_buffer_proto_funcs, 56212 countof(js_shared_array_buffer_proto_funcs)); 56213 56214 shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer", 56215 js_shared_array_buffer_constructor, 1, 56216 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]); 56217 JS_SetPropertyFunctionList(ctx, shared_array_buffer_func, 56218 js_shared_array_buffer_funcs, 56219 countof(js_shared_array_buffer_funcs)); 56220 56221 typed_array_base_proto = JS_NewObject(ctx); 56222 JS_SetPropertyFunctionList(ctx, typed_array_base_proto, 56223 js_typed_array_base_proto_funcs, 56224 countof(js_typed_array_base_proto_funcs)); 56225 56226 /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */ 56227 JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString); 56228 /* XXX: should use alias method in JSCFunctionListEntry */ //@@@ 56229 JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj, 56230 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); 56231 56232 typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor, 56233 "TypedArray", 0); 56234 JS_SetPropertyFunctionList(ctx, typed_array_base_func, 56235 js_typed_array_base_funcs, 56236 countof(js_typed_array_base_funcs)); 56237 JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); 56238 56239 /* Used to squelch a -Wcast-function-type warning. */ 56240 JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; 56241 for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { 56242 JSValue func_obj; 56243 char buf[ATOM_GET_STR_BUF_SIZE]; 56244 const char *name; 56245 56246 ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto); 56247 JS_DefinePropertyValueStr(ctx, ctx->class_proto[i], 56248 "BYTES_PER_ELEMENT", 56249 JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 56250 0); 56251 name = JS_AtomGetStr(ctx, buf, sizeof(buf), 56252 JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); 56253 func_obj = JS_NewCFunction3(ctx, ft.generic, 56254 name, 3, JS_CFUNC_constructor_magic, i, 56255 typed_array_base_func); 56256 JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); 56257 JS_DefinePropertyValueStr(ctx, func_obj, 56258 "BYTES_PER_ELEMENT", 56259 JS_NewInt32(ctx, 1 << typed_array_size_log2(i)), 56260 0); 56261 } 56262 JS_FreeValue(ctx, typed_array_base_proto); 56263 JS_FreeValue(ctx, typed_array_base_func); 56264 56265 /* DataView */ 56266 ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx); 56267 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW], 56268 js_dataview_proto_funcs, 56269 countof(js_dataview_proto_funcs)); 56270 JS_NewGlobalCConstructorOnly(ctx, "DataView", 56271 js_dataview_constructor, 1, 56272 ctx->class_proto[JS_CLASS_DATAVIEW]); 56273 /* Atomics */ 56274 #ifdef CONFIG_ATOMICS 56275 JS_AddIntrinsicAtomics(ctx); 56276 #endif 56277 }