1/*
2 * QuickJS Javascript Engine
3 *
4 * Copyright (c) 2017-2025 Fabrice Bellard
5 * Copyright (c) 2017-2025 Charlie Gordon
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25#include <stdlib.h>
26#include <stdio.h>
27#include <stdarg.h>
28#include <inttypes.h>
29#include <string.h>
30#include <assert.h>
31#include <sys/time.h>
32#include <time.h>
33#include <fenv.h>
34#include <math.h>
35#if defined(__APPLE__)
36#include <malloc/malloc.h>
37#elif defined(__linux__) || defined(__GLIBC__)
38#include <malloc.h>
39#elif defined(__FreeBSD__)
40#include <malloc_np.h>
41#endif
42
43#include "cutils.h"
44#include "list.h"
45#include "quickjs.h"
46#include "libregexp.h"
47#include "libunicode.h"
48#include "dtoa.h"
49
50#define OPTIMIZE 1
51#define SHORT_OPCODES 1
52#if defined(EMSCRIPTEN)
53#define DIRECT_DISPATCH 0
54#else
55#define DIRECT_DISPATCH 1
56#endif
57
58#if defined(__APPLE__)
59#define MALLOC_OVERHEAD 0
60#else
61#define MALLOC_OVERHEAD 8
62#endif
63
64#if !defined(_WIN32)
65/* define it if printf uses the RNDN rounding mode instead of RNDNA */
66#define CONFIG_PRINTF_RNDN
67#endif
68
69/* define to include Atomics.* operations which depend on the OS
70 threads */
71#if !defined(EMSCRIPTEN)
72#define CONFIG_ATOMICS
73#endif
74
75#if !defined(EMSCRIPTEN)
76/* enable stack limitation */
77#define CONFIG_STACK_CHECK
78#endif
79
80
81/* dump object free */
82//#define DUMP_FREE
83//#define DUMP_CLOSURE
84/* dump the bytecode of the compiled functions: combination of bits
85 1: dump pass 3 final byte code
86 2: dump pass 2 code
87 4: dump pass 1 code
88 8: dump stdlib functions
89 16: dump bytecode in hex
90 32: dump line number table
91 64: dump compute_stack_size
92 */
93//#define DUMP_BYTECODE (1)
94/* dump the occurence of the automatic GC */
95//#define DUMP_GC
96/* dump objects freed by the garbage collector */
97//#define DUMP_GC_FREE
98/* dump objects leaking when freeing the runtime */
99//#define DUMP_LEAKS 1
100/* dump memory usage before running the garbage collector */
101//#define DUMP_MEM
102//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
103//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
104//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
105//#define DUMP_MODULE_RESOLVE
106//#define DUMP_PROMISE
107//#define DUMP_READ_OBJECT
108//#define DUMP_ROPE_REBALANCE
109
110/* test the GC by forcing it before each object allocation */
111//#define FORCE_GC_AT_MALLOC
112
113#ifdef CONFIG_ATOMICS
114#include <pthread.h>
115#include <stdatomic.h>
116#include <errno.h>
117#endif
118
119enum {
120 /* classid tag */ /* union usage | properties */
121 JS_CLASS_OBJECT = 1, /* must be first */
122 JS_CLASS_ARRAY, /* u.array | length */
123 JS_CLASS_ERROR,
124 JS_CLASS_NUMBER, /* u.object_data */
125 JS_CLASS_STRING, /* u.object_data */
126 JS_CLASS_BOOLEAN, /* u.object_data */
127 JS_CLASS_SYMBOL, /* u.object_data */
128 JS_CLASS_ARGUMENTS, /* u.array | length */
129 JS_CLASS_MAPPED_ARGUMENTS, /* | length */
130 JS_CLASS_DATE, /* u.object_data */
131 JS_CLASS_MODULE_NS,
132 JS_CLASS_C_FUNCTION, /* u.cfunc */
133 JS_CLASS_BYTECODE_FUNCTION, /* u.func */
134 JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
135 JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
136 JS_CLASS_GENERATOR_FUNCTION, /* u.func */
137 JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
138 JS_CLASS_REGEXP, /* u.regexp */
139 JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */
140 JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
141 JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */
142 JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */
143 JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */
144 JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */
145 JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */
146 JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */
147 JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */
148 JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
149 JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
150 JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
151 JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
152 JS_CLASS_DATAVIEW, /* u.typed_array */
153 JS_CLASS_BIG_INT, /* u.object_data */
154 JS_CLASS_MAP, /* u.map_state */
155 JS_CLASS_SET, /* u.map_state */
156 JS_CLASS_WEAKMAP, /* u.map_state */
157 JS_CLASS_WEAKSET, /* u.map_state */
158 JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
159 JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
160 JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
161 JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
162 JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
163 JS_CLASS_GENERATOR, /* u.generator_data */
164 JS_CLASS_PROXY, /* u.proxy_data */
165 JS_CLASS_PROMISE, /* u.promise_data */
166 JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
167 JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */
168 JS_CLASS_ASYNC_FUNCTION, /* u.func */
169 JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */
170 JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */
171 JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
172 JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
173 JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
174 JS_CLASS_WEAK_REF,
175 JS_CLASS_FINALIZATION_REGISTRY,
176
177 JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
178};
179
180/* number of typed array types */
181#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
182static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
183#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
184
185typedef enum JSErrorEnum {
186 JS_EVAL_ERROR,
187 JS_RANGE_ERROR,
188 JS_REFERENCE_ERROR,
189 JS_SYNTAX_ERROR,
190 JS_TYPE_ERROR,
191 JS_URI_ERROR,
192 JS_INTERNAL_ERROR,
193 JS_AGGREGATE_ERROR,
194
195 JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
196} JSErrorEnum;
197
198/* the variable and scope indexes must fit on 16 bits. The (-1) and
199 ARG_SCOPE_END values are reserved. */
200#define JS_MAX_LOCAL_VARS 65534
201#define JS_STACK_SIZE_MAX 65534
202#define JS_STRING_LEN_MAX ((1 << 30) - 1)
203
204/* strings <= this length are not concatenated using ropes. if too
205 small, the rope memory overhead becomes high. */
206#define JS_STRING_ROPE_SHORT_LEN 512
207/* specific threshold for initial rope use */
208#define JS_STRING_ROPE_SHORT2_LEN 8192
209/* rope depth at which we rebalance */
210#define JS_STRING_ROPE_MAX_DEPTH 60
211
212#define __exception __attribute__((warn_unused_result))
213
214typedef struct JSShape JSShape;
215typedef struct JSString JSString;
216typedef struct JSString JSAtomStruct;
217typedef struct JSObject JSObject;
218
219#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
220#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
221#define JS_VALUE_GET_STRING_ROPE(v) ((JSStringRope *)JS_VALUE_GET_PTR(v))
222
223typedef enum {
224 JS_GC_PHASE_NONE,
225 JS_GC_PHASE_DECREF,
226 JS_GC_PHASE_REMOVE_CYCLES,
227} JSGCPhaseEnum;
228
229typedef enum OPCodeEnum OPCodeEnum;
230
231struct JSRuntime {
232 JSMallocFunctions mf;
233 JSMallocState malloc_state;
234 const char *rt_info;
235
236 int atom_hash_size; /* power of two */
237 int atom_count;
238 int atom_size;
239 int atom_count_resize; /* resize hash table at this count */
240 uint32_t *atom_hash;
241 JSAtomStruct **atom_array;
242 int atom_free_index; /* 0 = none */
243
244 int class_count; /* size of class_array */
245 JSClass *class_array;
246
247 struct list_head context_list; /* list of JSContext.link */
248 /* list of JSGCObjectHeader.link. List of allocated GC objects (used
249 by the garbage collector) */
250 struct list_head gc_obj_list;
251 /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
252 struct list_head gc_zero_ref_count_list;
253 struct list_head tmp_obj_list; /* used during GC */
254 JSGCPhaseEnum gc_phase : 8;
255 size_t malloc_gc_threshold;
256 struct list_head weakref_list; /* list of JSWeakRefHeader.link */
257#ifdef DUMP_LEAKS
258 struct list_head string_list; /* list of JSString.link */
259#endif
260 /* stack limitation */
261 uintptr_t stack_size; /* in bytes, 0 if no limit */
262 uintptr_t stack_top;
263 uintptr_t stack_limit; /* lower stack limit */
264
265 JSValue current_exception;
266 /* true if inside an out of memory error, to avoid recursing */
267 BOOL in_out_of_memory : 8;
268
269 struct JSStackFrame *current_stack_frame;
270
271 JSInterruptHandler *interrupt_handler;
272 void *interrupt_opaque;
273
274 JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
275 void *host_promise_rejection_tracker_opaque;
276
277 struct list_head job_list; /* list of JSJobEntry.link */
278
279 JSModuleNormalizeFunc *module_normalize_func;
280 JSModuleLoaderFunc *module_loader_func;
281 void *module_loader_opaque;
282 /* timestamp for internal use in module evaluation */
283 int64_t module_async_evaluation_next_timestamp;
284
285 BOOL can_block : 8; /* TRUE if Atomics.wait can block */
286 /* used to allocate, free and clone SharedArrayBuffers */
287 JSSharedArrayBufferFunctions sab_funcs;
288 /* see JS_SetStripInfo() */
289 uint8_t strip_flags;
290
291 /* Shape hash table */
292 int shape_hash_bits;
293 int shape_hash_size;
294 int shape_hash_count; /* number of hashed shapes */
295 JSShape **shape_hash;
296 void *user_opaque;
297};
298
299struct JSClass {
300 uint32_t class_id; /* 0 means free entry */
301 JSAtom class_name;
302 JSClassFinalizer *finalizer;
303 JSClassGCMark *gc_mark;
304 JSClassCall *call;
305 /* pointers for exotic behavior, can be NULL if none are present */
306 const JSClassExoticMethods *exotic;
307};
308
309#define JS_MODE_STRICT (1 << 0)
310#define JS_MODE_ASYNC (1 << 2) /* async function */
311#define JS_MODE_BACKTRACE_BARRIER (1 << 3) /* stop backtrace before this frame */
312
313typedef struct JSStackFrame {
314 struct JSStackFrame *prev_frame; /* NULL if first stack frame */
315 JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
316 JSValue *arg_buf; /* arguments */
317 JSValue *var_buf; /* variables */
318 struct list_head var_ref_list; /* list of JSVarRef.var_ref_link */
319 const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
320 instruction after the call */
321 int arg_count;
322 int js_mode; /* not supported for C functions */
323 /* only used in generators. Current stack pointer value. NULL if
324 the function is running. */
325 JSValue *cur_sp;
326} JSStackFrame;
327
328typedef enum {
329 JS_GC_OBJ_TYPE_JS_OBJECT,
330 JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
331 JS_GC_OBJ_TYPE_SHAPE,
332 JS_GC_OBJ_TYPE_VAR_REF,
333 JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
334 JS_GC_OBJ_TYPE_JS_CONTEXT,
335} JSGCObjectTypeEnum;
336
337/* header for GC objects. GC objects are C data structures with a
338 reference count that can reference other GC objects. JS Objects are
339 a particular type of GC object. */
340struct JSGCObjectHeader {
341 int ref_count; /* must come first, 32-bit */
342 JSGCObjectTypeEnum gc_obj_type : 4;
343 uint8_t mark : 4; /* used by the GC */
344 uint8_t dummy1; /* not used by the GC */
345 uint16_t dummy2; /* not used by the GC */
346 struct list_head link;
347};
348
349typedef enum {
350 JS_WEAKREF_TYPE_MAP,
351 JS_WEAKREF_TYPE_WEAKREF,
352 JS_WEAKREF_TYPE_FINREC,
353} JSWeakRefHeaderTypeEnum;
354
355typedef struct {
356 struct list_head link;
357 JSWeakRefHeaderTypeEnum weakref_type;
358} JSWeakRefHeader;
359
360typedef struct JSVarRef {
361 union {
362 JSGCObjectHeader header; /* must come first */
363 struct {
364 int __gc_ref_count; /* corresponds to header.ref_count */
365 uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
366 uint8_t is_detached;
367 };
368 };
369 JSValue *pvalue; /* pointer to the value, either on the stack or
370 to 'value' */
371 union {
372 JSValue value; /* used when is_detached = TRUE */
373 struct {
374 struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */
375 struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */
376 }; /* used when is_detached = FALSE */
377 };
378} JSVarRef;
379
380/* bigint */
381
382#if JS_LIMB_BITS == 32
383
384typedef int32_t js_slimb_t;
385typedef uint32_t js_limb_t;
386typedef int64_t js_sdlimb_t;
387typedef uint64_t js_dlimb_t;
388
389#define JS_LIMB_DIGITS 9
390
391#else
392
393typedef __int128 int128_t;
394typedef unsigned __int128 uint128_t;
395typedef int64_t js_slimb_t;
396typedef uint64_t js_limb_t;
397typedef int128_t js_sdlimb_t;
398typedef uint128_t js_dlimb_t;
399
400#define JS_LIMB_DIGITS 19
401
402#endif
403
404typedef struct JSBigInt {
405 JSRefCountHeader header; /* must come first, 32-bit */
406 uint32_t len; /* number of limbs, >= 1 */
407 js_limb_t tab[]; /* two's complement representation, always
408 normalized so that 'len' is the minimum
409 possible length >= 1 */
410} JSBigInt;
411
412/* this bigint structure can hold a 64 bit integer */
413typedef struct {
414 js_limb_t big_int_buf[sizeof(JSBigInt) / sizeof(js_limb_t)]; /* for JSBigInt */
415 /* must come just after */
416 js_limb_t tab[(64 + JS_LIMB_BITS - 1) / JS_LIMB_BITS];
417} JSBigIntBuf;
418
419typedef enum {
420 JS_AUTOINIT_ID_PROTOTYPE,
421 JS_AUTOINIT_ID_MODULE_NS,
422 JS_AUTOINIT_ID_PROP,
423} JSAutoInitIDEnum;
424
425/* must be large enough to have a negligible runtime cost and small
426 enough to call the interrupt callback often. */
427#define JS_INTERRUPT_COUNTER_INIT 10000
428
429struct JSContext {
430 JSGCObjectHeader header; /* must come first */
431 JSRuntime *rt;
432 struct list_head link;
433
434 uint16_t binary_object_count;
435 int binary_object_size;
436
437 JSShape *array_shape; /* initial shape for Array objects */
438
439 JSValue *class_proto;
440 JSValue function_proto;
441 JSValue function_ctor;
442 JSValue array_ctor;
443 JSValue regexp_ctor;
444 JSValue promise_ctor;
445 JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
446 JSValue iterator_proto;
447 JSValue async_iterator_proto;
448 JSValue array_proto_values;
449 JSValue throw_type_error;
450 JSValue eval_obj;
451
452 JSValue global_obj; /* global object */
453 JSValue global_var_obj; /* contains the global let/const definitions */
454
455 uint64_t random_state;
456
457 /* when the counter reaches zero, JSRutime.interrupt_handler is called */
458 int interrupt_counter;
459
460 struct list_head loaded_modules; /* list of JSModuleDef.link */
461
462 /* if NULL, RegExp compilation is not supported */
463 JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
464 JSValueConst flags);
465 /* if NULL, eval is not supported */
466 JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
467 const char *input, size_t input_len,
468 const char *filename, int flags, int scope_idx);
469 void *user_opaque;
470};
471
472typedef union JSFloat64Union {
473 double d;
474 uint64_t u64;
475 uint32_t u32[2];
476} JSFloat64Union;
477
478enum {
479 JS_ATOM_TYPE_STRING = 1,
480 JS_ATOM_TYPE_GLOBAL_SYMBOL,
481 JS_ATOM_TYPE_SYMBOL,
482 JS_ATOM_TYPE_PRIVATE,
483};
484
485typedef enum {
486 JS_ATOM_KIND_STRING,
487 JS_ATOM_KIND_SYMBOL,
488 JS_ATOM_KIND_PRIVATE,
489} JSAtomKindEnum;
490
491#define JS_ATOM_HASH_MASK ((1 << 30) - 1)
492#define JS_ATOM_HASH_PRIVATE JS_ATOM_HASH_MASK
493
494struct JSString {
495 JSRefCountHeader header; /* must come first, 32-bit */
496 uint32_t len : 31;
497 uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
498 /* for JS_ATOM_TYPE_SYMBOL: hash = weakref_count, atom_type = 3,
499 for JS_ATOM_TYPE_PRIVATE: hash = JS_ATOM_HASH_PRIVATE, atom_type = 3
500 XXX: could change encoding to have one more bit in hash */
501 uint32_t hash : 30;
502 uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
503 uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
504#ifdef DUMP_LEAKS
505 struct list_head link; /* string list */
506#endif
507 union {
508 uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
509 uint16_t str16[0];
510 } u;
511};
512
513typedef struct JSStringRope {
514 JSRefCountHeader header; /* must come first, 32-bit */
515 uint32_t len;
516 uint8_t is_wide_char; /* 0 = 8 bits, 1 = 16 bits characters */
517 uint8_t depth; /* max depth of the rope tree */
518 /* XXX: could reduce memory usage by using a direct pointer with
519 bit 0 to select rope or string */
520 JSValue left;
521 JSValue right; /* might be the empty string */
522} JSStringRope;
523
524typedef struct JSClosureVar {
525 uint8_t is_local : 1;
526 uint8_t is_arg : 1;
527 uint8_t is_const : 1;
528 uint8_t is_lexical : 1;
529 uint8_t var_kind : 4; /* see JSVarKindEnum */
530 /* 8 bits available */
531 uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
532 parent function. otherwise: index to a closure
533 variable of the parent function */
534 JSAtom var_name;
535} JSClosureVar;
536
537#define ARG_SCOPE_INDEX 1
538#define ARG_SCOPE_END (-2)
539
540typedef struct JSVarScope {
541 int parent; /* index into fd->scopes of the enclosing scope */
542 int first; /* index into fd->vars of the last variable in this scope */
543} JSVarScope;
544
545typedef enum {
546 /* XXX: add more variable kinds here instead of using bit fields */
547 JS_VAR_NORMAL,
548 JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
549 JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
550 function declaration */
551 JS_VAR_CATCH,
552 JS_VAR_FUNCTION_NAME, /* function expression name */
553 JS_VAR_PRIVATE_FIELD,
554 JS_VAR_PRIVATE_METHOD,
555 JS_VAR_PRIVATE_GETTER,
556 JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
557 JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
558} JSVarKindEnum;
559
560/* XXX: could use a different structure in bytecode functions to save
561 memory */
562typedef struct JSVarDef {
563 JSAtom var_name;
564 /* index into fd->scopes of this variable lexical scope */
565 int scope_level;
566 /* during compilation:
567 - if scope_level = 0: scope in which the variable is defined
568 - if scope_level != 0: index into fd->vars of the next
569 variable in the same or enclosing lexical scope
570 in a bytecode function:
571 index into fd->vars of the next
572 variable in the same or enclosing lexical scope
573 */
574 int scope_next;
575 uint8_t is_const : 1;
576 uint8_t is_lexical : 1;
577 uint8_t is_captured : 1;
578 uint8_t is_static_private : 1; /* only used during private class field parsing */
579 uint8_t var_kind : 4; /* see JSVarKindEnum */
580 /* only used during compilation: function pool index for lexical
581 variables with var_kind =
582 JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
583 the definition of the 'var' variables (they have scope_level =
584 0) */
585 int func_pool_idx : 24; /* only used during compilation : index in
586 the constant pool for hoisted function
587 definition */
588} JSVarDef;
589
590/* for the encoding of the pc2line table */
591#define PC2LINE_BASE (-1)
592#define PC2LINE_RANGE 5
593#define PC2LINE_OP_FIRST 1
594#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
595
596typedef enum JSFunctionKindEnum {
597 JS_FUNC_NORMAL = 0,
598 JS_FUNC_GENERATOR = (1 << 0),
599 JS_FUNC_ASYNC = (1 << 1),
600 JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
601} JSFunctionKindEnum;
602
603typedef struct JSFunctionBytecode {
604 JSGCObjectHeader header; /* must come first */
605 uint8_t js_mode;
606 uint8_t has_prototype : 1; /* true if a prototype field is necessary */
607 uint8_t has_simple_parameter_list : 1;
608 uint8_t is_derived_class_constructor : 1;
609 /* true if home_object needs to be initialized */
610 uint8_t need_home_object : 1;
611 uint8_t func_kind : 2;
612 uint8_t new_target_allowed : 1;
613 uint8_t super_call_allowed : 1;
614 uint8_t super_allowed : 1;
615 uint8_t arguments_allowed : 1;
616 uint8_t has_debug : 1;
617 uint8_t read_only_bytecode : 1;
618 uint8_t is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */
619 /* XXX: 10 bits available */
620 uint8_t *byte_code_buf; /* (self pointer) */
621 int byte_code_len;
622 JSAtom func_name;
623 JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
624 JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
625 uint16_t arg_count;
626 uint16_t var_count;
627 uint16_t defined_arg_count; /* for length function property */
628 uint16_t stack_size; /* maximum stack size */
629 JSContext *realm; /* function realm */
630 JSValue *cpool; /* constant pool (self pointer) */
631 int cpool_count;
632 int closure_var_count;
633 struct {
634 /* debug info, move to separate structure to save memory? */
635 JSAtom filename;
636 int source_len;
637 int pc2line_len;
638 uint8_t *pc2line_buf;
639 char *source;
640 } debug;
641} JSFunctionBytecode;
642
643typedef struct JSBoundFunction {
644 JSValue func_obj;
645 JSValue this_val;
646 int argc;
647 JSValue argv[0];
648} JSBoundFunction;
649
650typedef enum JSIteratorKindEnum {
651 JS_ITERATOR_KIND_KEY,
652 JS_ITERATOR_KIND_VALUE,
653 JS_ITERATOR_KIND_KEY_AND_VALUE,
654} JSIteratorKindEnum;
655
656typedef struct JSForInIterator {
657 JSValue obj;
658 uint32_t idx;
659 uint32_t atom_count;
660 uint8_t in_prototype_chain;
661 uint8_t is_array;
662 JSPropertyEnum *tab_atom; /* is_array = FALSE */
663} JSForInIterator;
664
665typedef struct JSRegExp {
666 JSString *pattern;
667 JSString *bytecode; /* also contains the flags */
668} JSRegExp;
669
670typedef struct JSProxyData {
671 JSValue target;
672 JSValue handler;
673 uint8_t is_func;
674 uint8_t is_revoked;
675} JSProxyData;
676
677typedef struct JSArrayBuffer {
678 int byte_length; /* 0 if detached */
679 uint8_t detached;
680 uint8_t shared; /* if shared, the array buffer cannot be detached */
681 uint8_t *data; /* NULL if detached */
682 struct list_head array_list;
683 void *opaque;
684 JSFreeArrayBufferDataFunc *free_func;
685} JSArrayBuffer;
686
687typedef struct JSTypedArray {
688 struct list_head link; /* link to arraybuffer */
689 JSObject *obj; /* back pointer to the TypedArray/DataView object */
690 JSObject *buffer; /* based array buffer */
691 uint32_t offset; /* offset in the array buffer */
692 uint32_t length; /* length in the array buffer */
693} JSTypedArray;
694
695typedef struct JSAsyncFunctionState {
696 JSGCObjectHeader header;
697 JSValue this_val; /* 'this' argument */
698 int argc; /* number of function arguments */
699 BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
700 BOOL is_completed; /* TRUE if the function has returned. The stack
701 frame is no longer valid */
702 JSValue resolving_funcs[2]; /* only used in JS async functions */
703 JSStackFrame frame;
704} JSAsyncFunctionState;
705
706typedef enum {
707 /* binary operators */
708 JS_OVOP_ADD,
709 JS_OVOP_SUB,
710 JS_OVOP_MUL,
711 JS_OVOP_DIV,
712 JS_OVOP_MOD,
713 JS_OVOP_POW,
714 JS_OVOP_OR,
715 JS_OVOP_AND,
716 JS_OVOP_XOR,
717 JS_OVOP_SHL,
718 JS_OVOP_SAR,
719 JS_OVOP_SHR,
720 JS_OVOP_EQ,
721 JS_OVOP_LESS,
722
723 JS_OVOP_BINARY_COUNT,
724 /* unary operators */
725 JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
726 JS_OVOP_NEG,
727 JS_OVOP_INC,
728 JS_OVOP_DEC,
729 JS_OVOP_NOT,
730
731 JS_OVOP_COUNT,
732} JSOverloadableOperatorEnum;
733
734typedef struct {
735 uint32_t operator_index;
736 JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
737} JSBinaryOperatorDefEntry;
738
739typedef struct {
740 int count;
741 JSBinaryOperatorDefEntry *tab;
742} JSBinaryOperatorDef;
743
744typedef struct {
745 uint32_t operator_counter;
746 BOOL is_primitive; /* OperatorSet for a primitive type */
747 /* NULL if no operator is defined */
748 JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
749 JSBinaryOperatorDef left;
750 JSBinaryOperatorDef right;
751} JSOperatorSetData;
752
753typedef struct JSReqModuleEntry {
754 JSAtom module_name;
755 JSModuleDef *module; /* used using resolution */
756} JSReqModuleEntry;
757
758typedef enum JSExportTypeEnum {
759 JS_EXPORT_TYPE_LOCAL,
760 JS_EXPORT_TYPE_INDIRECT,
761} JSExportTypeEnum;
762
763typedef struct JSExportEntry {
764 union {
765 struct {
766 int var_idx; /* closure variable index */
767 JSVarRef *var_ref; /* if != NULL, reference to the variable */
768 } local; /* for local export */
769 int req_module_idx; /* module for indirect export */
770 } u;
771 JSExportTypeEnum export_type;
772 JSAtom local_name; /* '*' if export ns from. not used for local
773 export after compilation */
774 JSAtom export_name; /* exported variable name */
775} JSExportEntry;
776
777typedef struct JSStarExportEntry {
778 int req_module_idx; /* in req_module_entries */
779} JSStarExportEntry;
780
781typedef struct JSImportEntry {
782 int var_idx; /* closure variable index */
783 JSAtom import_name;
784 int req_module_idx; /* in req_module_entries */
785} JSImportEntry;
786
787typedef enum {
788 JS_MODULE_STATUS_UNLINKED,
789 JS_MODULE_STATUS_LINKING,
790 JS_MODULE_STATUS_LINKED,
791 JS_MODULE_STATUS_EVALUATING,
792 JS_MODULE_STATUS_EVALUATING_ASYNC,
793 JS_MODULE_STATUS_EVALUATED,
794} JSModuleStatus;
795
796struct JSModuleDef {
797 JSRefCountHeader header; /* must come first, 32-bit */
798 JSAtom module_name;
799 struct list_head link;
800
801 JSReqModuleEntry *req_module_entries;
802 int req_module_entries_count;
803 int req_module_entries_size;
804
805 JSExportEntry *export_entries;
806 int export_entries_count;
807 int export_entries_size;
808
809 JSStarExportEntry *star_export_entries;
810 int star_export_entries_count;
811 int star_export_entries_size;
812
813 JSImportEntry *import_entries;
814 int import_entries_count;
815 int import_entries_size;
816
817 JSValue module_ns;
818 JSValue func_obj; /* only used for JS modules */
819 JSModuleInitFunc *init_func; /* only used for C modules */
820 BOOL has_tla : 8; /* true if func_obj contains await */
821 BOOL resolved : 8;
822 BOOL func_created : 8;
823 JSModuleStatus status : 8;
824 /* temp use during js_module_link() & js_module_evaluate() */
825 int dfs_index, dfs_ancestor_index;
826 JSModuleDef *stack_prev;
827 /* temp use during js_module_evaluate() */
828 JSModuleDef **async_parent_modules;
829 int async_parent_modules_count;
830 int async_parent_modules_size;
831 int pending_async_dependencies;
832 BOOL async_evaluation;
833 int64_t async_evaluation_timestamp;
834 JSModuleDef *cycle_root;
835 JSValue promise; /* corresponds to spec field: capability */
836 JSValue resolving_funcs[2]; /* corresponds to spec field: capability */
837
838 /* true if evaluation yielded an exception. It is saved in
839 eval_exception */
840 BOOL eval_has_exception : 8;
841 JSValue eval_exception;
842 JSValue meta_obj; /* for import.meta */
843};
844
845typedef struct JSJobEntry {
846 struct list_head link;
847 JSContext *ctx;
848 JSJobFunc *job_func;
849 int argc;
850 JSValue argv[0];
851} JSJobEntry;
852
853typedef struct JSProperty {
854 union {
855 JSValue value; /* JS_PROP_NORMAL */
856 struct { /* JS_PROP_GETSET */
857 JSObject *getter; /* NULL if undefined */
858 JSObject *setter; /* NULL if undefined */
859 } getset;
860 JSVarRef *var_ref; /* JS_PROP_VARREF */
861 struct { /* JS_PROP_AUTOINIT */
862 /* in order to use only 2 pointers, we compress the realm
863 and the init function pointer */
864 uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
865 in the 2 low bits */
866 void *opaque;
867 } init;
868 } u;
869} JSProperty;
870
871#define JS_PROP_INITIAL_SIZE 2
872#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
873#define JS_ARRAY_INITIAL_SIZE 2
874
875typedef struct JSShapeProperty {
876 uint32_t hash_next : 26; /* 0 if last in list */
877 uint32_t flags : 6; /* JS_PROP_XXX */
878 JSAtom atom; /* JS_ATOM_NULL = free property entry */
879} JSShapeProperty;
880
881struct JSShape {
882 /* hash table of size hash_mask + 1 before the start of the
883 structure (see prop_hash_end()). */
884 JSGCObjectHeader header;
885 /* true if the shape is inserted in the shape hash table. If not,
886 JSShape.hash is not valid */
887 uint8_t is_hashed;
888 /* If true, the shape may have small array index properties 'n' with 0
889 <= n <= 2^31-1. If false, the shape is guaranteed not to have
890 small array index properties */
891 uint8_t has_small_array_index;
892 uint32_t hash; /* current hash value */
893 uint32_t prop_hash_mask;
894 int prop_size; /* allocated properties */
895 int prop_count; /* include deleted properties */
896 int deleted_prop_count;
897 JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
898 JSObject *proto;
899 JSShapeProperty prop[0]; /* prop_size elements */
900};
901
902struct JSObject {
903 union {
904 JSGCObjectHeader header;
905 struct {
906 int __gc_ref_count; /* corresponds to header.ref_count */
907 uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
908
909 uint8_t extensible : 1;
910 uint8_t free_mark : 1; /* only used when freeing objects with cycles */
911 uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
912 uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
913 uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
914 uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
915 uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
916 uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
917 uint16_t class_id; /* see JS_CLASS_x */
918 };
919 };
920 /* count the number of weak references to this object. The object
921 structure is freed only if header.ref_count = 0 and
922 weakref_count = 0 */
923 uint32_t weakref_count;
924 JSShape *shape; /* prototype and property names + flag */
925 JSProperty *prop; /* array of properties */
926 union {
927 void *opaque;
928 struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
929 struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
930 struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
931 struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
932 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
933 struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
934 struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
935 struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
936 struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
937 struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
938 struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
939 struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
940 struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
941 struct JSAsyncFunctionState *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
942 struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
943 struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
944 struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
945 /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
946 struct JSFunctionBytecode *function_bytecode;
947 JSVarRef **var_refs;
948 JSObject *home_object; /* for 'super' access */
949 } func;
950 struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
951 JSContext *realm;
952 JSCFunctionType c_function;
953 uint8_t length;
954 uint8_t cproto;
955 int16_t magic;
956 } cfunc;
957 /* array part for fast arrays and typed arrays */
958 struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
959 union {
960 uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
961 struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
962 } u1;
963 union {
964 JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
965 void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
966 int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */
967 uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
968 int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */
969 uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */
970 int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */
971 uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */
972 int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */
973 uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */
974 float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */
975 double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */
976 } u;
977 uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
978 } array; /* 12/20 bytes */
979 JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
980 JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
981 } u;
982};
983
984enum {
985 __JS_ATOM_NULL = JS_ATOM_NULL,
986#define DEF(name, str) JS_ATOM_ ## name,
987#include "quickjs-atom.h"
988#undef DEF
989 JS_ATOM_END,
990};
991#define JS_ATOM_LAST_KEYWORD JS_ATOM_super
992#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
993
994static const char js_atom_init[] =
995#define DEF(name, str) str "\0"
996#include "quickjs-atom.h"
997#undef DEF
998;
999
1000typedef enum OPCodeFormat {
1001#define FMT(f) OP_FMT_ ## f,
1002#define DEF(id, size, n_pop, n_push, f)
1003#include "quickjs-opcode.h"
1004#undef DEF
1005#undef FMT
1006} OPCodeFormat;
1007
1008enum OPCodeEnum {
1009#define FMT(f)
1010#define DEF(id, size, n_pop, n_push, f) OP_ ## id,
1011#define def(id, size, n_pop, n_push, f)
1012#include "quickjs-opcode.h"
1013#undef def
1014#undef DEF
1015#undef FMT
1016 OP_COUNT, /* excluding temporary opcodes */
1017 /* temporary opcodes : overlap with the short opcodes */
1018 OP_TEMP_START = OP_nop + 1,
1019 OP___dummy = OP_TEMP_START - 1,
1020#define FMT(f)
1021#define DEF(id, size, n_pop, n_push, f)
1022#define def(id, size, n_pop, n_push, f) OP_ ## id,
1023#include "quickjs-opcode.h"
1024#undef def
1025#undef DEF
1026#undef FMT
1027 OP_TEMP_END,
1028};
1029
1030static int JS_InitAtoms(JSRuntime *rt);
1031static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
1032 int atom_type);
1033static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
1034static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
1035static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
1036 JSValueConst this_obj,
1037 int argc, JSValueConst *argv, int flags);
1038static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
1039 JSValueConst this_obj,
1040 int argc, JSValueConst *argv, int flags);
1041static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
1042 JSValueConst this_obj, JSValueConst new_target,
1043 int argc, JSValue *argv, int flags);
1044static JSValue JS_CallConstructorInternal(JSContext *ctx,
1045 JSValueConst func_obj,
1046 JSValueConst new_target,
1047 int argc, JSValue *argv, int flags);
1048static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
1049 int argc, JSValueConst *argv);
1050static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
1051 int argc, JSValueConst *argv);
1052static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
1053 JSValue val, BOOL is_array_ctor);
1054static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
1055 JSValueConst val, int flags, int scope_idx);
1056JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
1057static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
1058static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p);
1059static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
1060static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
1061static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
1062static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val);
1063static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
1064static __maybe_unused void JS_PrintValue(JSContext *ctx,
1065 const char *str,
1066 JSValueConst val);
1067static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
1068static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
1069 int argc, JSValueConst *argv, int magic);
1070static void js_array_finalizer(JSRuntime *rt, JSValue val);
1071static void js_array_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
1072static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
1073static void js_object_data_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
1074static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
1075static void js_c_function_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
1076static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
1077static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
1078 JS_MarkFunc *mark_func);
1079static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
1080static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
1081 JS_MarkFunc *mark_func);
1082static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
1083static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
1084 JS_MarkFunc *mark_func);
1085static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
1086static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
1087static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
1088static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
1089 JS_MarkFunc *mark_func);
1090static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
1091static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
1092 JS_MarkFunc *mark_func);
1093static void js_map_finalizer(JSRuntime *rt, JSValue val);
1094static void js_map_mark(JSRuntime *rt, JSValueConst val,
1095 JS_MarkFunc *mark_func);
1096static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
1097static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
1098 JS_MarkFunc *mark_func);
1099static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
1100static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
1101 JS_MarkFunc *mark_func);
1102static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
1103static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
1104 JS_MarkFunc *mark_func);
1105static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
1106static void js_generator_mark(JSRuntime *rt, JSValueConst val,
1107 JS_MarkFunc *mark_func);
1108static void js_promise_finalizer(JSRuntime *rt, JSValue val);
1109static void js_promise_mark(JSRuntime *rt, JSValueConst val,
1110 JS_MarkFunc *mark_func);
1111static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
1112static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
1113 JS_MarkFunc *mark_func);
1114
1115#define HINT_STRING 0
1116#define HINT_NUMBER 1
1117#define HINT_NONE 2
1118#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive
1119static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint);
1120static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
1121static int JS_ToBoolFree(JSContext *ctx, JSValue val);
1122static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
1123static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
1124static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
1125static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len);
1126static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
1127 JSValueConst flags);
1128static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
1129 JSValue pattern, JSValue bc);
1130static void gc_decref(JSRuntime *rt);
1131static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
1132 const JSClassDef *class_def, JSAtom name);
1133
1134typedef enum JSStrictEqModeEnum {
1135 JS_EQ_STRICT,
1136 JS_EQ_SAME_VALUE,
1137 JS_EQ_SAME_VALUE_ZERO,
1138} JSStrictEqModeEnum;
1139
1140static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
1141 JSStrictEqModeEnum eq_mode);
1142static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1143static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1144static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
1145static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
1146static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
1147static JSProperty *add_property(JSContext *ctx,
1148 JSObject *p, JSAtom prop, int prop_flags);
1149static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
1150JSValue JS_ThrowOutOfMemory(JSContext *ctx);
1151static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
1152
1153static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception);
1154static int JS_CreateProperty(JSContext *ctx, JSObject *p,
1155 JSAtom prop, JSValueConst val,
1156 JSValueConst getter, JSValueConst setter,
1157 int flags);
1158static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
1159 int pos2, int len);
1160static JSValue js_array_buffer_constructor3(JSContext *ctx,
1161 JSValueConst new_target,
1162 uint64_t len, JSClassID class_id,
1163 uint8_t *buf,
1164 JSFreeArrayBufferDataFunc *free_func,
1165 void *opaque, BOOL alloc_flag);
1166static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
1167static JSValue js_typed_array_constructor(JSContext *ctx,
1168 JSValueConst this_val,
1169 int argc, JSValueConst *argv,
1170 int classid);
1171static JSValue js_typed_array_constructor_ta(JSContext *ctx,
1172 JSValueConst new_target,
1173 JSValueConst src_obj,
1174 int classid);
1175static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
1176static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
1177static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
1178static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
1179 BOOL is_arg);
1180static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
1181static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s);
1182static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
1183 JSValueConst this_obj,
1184 int argc, JSValueConst *argv,
1185 int flags);
1186static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
1187static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
1188 JS_MarkFunc *mark_func);
1189static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
1190 const char *input, size_t input_len,
1191 const char *filename, int flags, int scope_idx);
1192static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
1193static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
1194 JS_MarkFunc *mark_func);
1195static JSValue js_import_meta(JSContext *ctx);
1196static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
1197static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
1198static JSValue js_new_promise_capability(JSContext *ctx,
1199 JSValue *resolving_funcs,
1200 JSValueConst ctor);
1201static __exception int perform_promise_then(JSContext *ctx,
1202 JSValueConst promise,
1203 JSValueConst *resolve_reject,
1204 JSValueConst *cap_resolving_funcs);
1205static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
1206 int argc, JSValueConst *argv, int magic);
1207static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
1208 int argc, JSValueConst *argv);
1209static int js_string_compare(JSContext *ctx,
1210 const JSString *p1, const JSString *p2);
1211static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
1212static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
1213 JSValue prop, JSValue val, int flags);
1214static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
1215static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
1216static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
1217static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
1218 JSObject *p, JSAtom prop);
1219static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
1220static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
1221static void js_free_shape(JSRuntime *rt, JSShape *sh);
1222static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
1223static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
1224 JSShapeProperty **pprs);
1225static int init_shape_hash(JSRuntime *rt);
1226static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
1227 JSValueConst obj);
1228static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
1229 JSValueConst obj);
1230static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
1231static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
1232 JSValueConst array_arg);
1233static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
1234 JSValue **arrpp, uint32_t *countp);
1235static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
1236 JSValueConst sync_iter);
1237static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
1238static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
1239 JS_MarkFunc *mark_func);
1240static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
1241 JSValueConst this_val,
1242 int argc, JSValueConst *argv, int flags);
1243static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
1244static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
1245 JSGCObjectTypeEnum type);
1246static void remove_gc_object(JSGCObjectHeader *h);
1247static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
1248static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
1249 void *opaque);
1250static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
1251 JSAtom atom, void *opaque);
1252static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
1253 int argc, JSValueConst *argv, int is_map);
1254static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh);
1255static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
1256static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
1257static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects);
1258static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
1259 JSValueConst obj, JSValueConst method);
1260
1261static const JSClassExoticMethods js_arguments_exotic_methods;
1262static const JSClassExoticMethods js_string_exotic_methods;
1263static const JSClassExoticMethods js_proxy_exotic_methods;
1264static const JSClassExoticMethods js_module_ns_exotic_methods;
1265static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
1266
1267static void js_trigger_gc(JSRuntime *rt, size_t size)
1268{
1269 BOOL force_gc;
1270#ifdef FORCE_GC_AT_MALLOC
1271 force_gc = TRUE;
1272#else
1273 force_gc = ((rt->malloc_state.malloc_size + size) >
1274 rt->malloc_gc_threshold);
1275#endif
1276 if (force_gc) {
1277#ifdef DUMP_GC
1278 printf("GC: size=%" PRIu64 "\n",
1279 (uint64_t)rt->malloc_state.malloc_size);
1280#endif
1281 JS_RunGC(rt);
1282 rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
1283 (rt->malloc_state.malloc_size >> 1);
1284 }
1285}
1286
1287static size_t js_malloc_usable_size_unknown(const void *ptr)
1288{
1289 return 0;
1290}
1291
1292void *js_malloc_rt(JSRuntime *rt, size_t size)
1293{
1294 return rt->mf.js_malloc(&rt->malloc_state, size);
1295}
1296
1297void js_free_rt(JSRuntime *rt, void *ptr)
1298{
1299 rt->mf.js_free(&rt->malloc_state, ptr);
1300}
1301
1302void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
1303{
1304 return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
1305}
1306
1307size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
1308{
1309 return rt->mf.js_malloc_usable_size(ptr);
1310}
1311
1312void *js_mallocz_rt(JSRuntime *rt, size_t size)
1313{
1314 void *ptr;
1315 ptr = js_malloc_rt(rt, size);
1316 if (!ptr)
1317 return NULL;
1318 return memset(ptr, 0, size);
1319}
1320
1321/* Throw out of memory in case of error */
1322void *js_malloc(JSContext *ctx, size_t size)
1323{
1324 void *ptr;
1325 ptr = js_malloc_rt(ctx->rt, size);
1326 if (unlikely(!ptr)) {
1327 JS_ThrowOutOfMemory(ctx);
1328 return NULL;
1329 }
1330 return ptr;
1331}
1332
1333/* Throw out of memory in case of error */
1334void *js_mallocz(JSContext *ctx, size_t size)
1335{
1336 void *ptr;
1337 ptr = js_mallocz_rt(ctx->rt, size);
1338 if (unlikely(!ptr)) {
1339 JS_ThrowOutOfMemory(ctx);
1340 return NULL;
1341 }
1342 return ptr;
1343}
1344
1345void js_free(JSContext *ctx, void *ptr)
1346{
1347 js_free_rt(ctx->rt, ptr);
1348}
1349
1350/* Throw out of memory in case of error */
1351void *js_realloc(JSContext *ctx, void *ptr, size_t size)
1352{
1353 void *ret;
1354 ret = js_realloc_rt(ctx->rt, ptr, size);
1355 if (unlikely(!ret && size != 0)) {
1356 JS_ThrowOutOfMemory(ctx);
1357 return NULL;
1358 }
1359 return ret;
1360}
1361
1362/* store extra allocated size in *pslack if successful */
1363void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
1364{
1365 void *ret;
1366 ret = js_realloc_rt(ctx->rt, ptr, size);
1367 if (unlikely(!ret && size != 0)) {
1368 JS_ThrowOutOfMemory(ctx);
1369 return NULL;
1370 }
1371 if (pslack) {
1372 size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
1373 *pslack = (new_size > size) ? new_size - size : 0;
1374 }
1375 return ret;
1376}
1377
1378size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
1379{
1380 return js_malloc_usable_size_rt(ctx->rt, ptr);
1381}
1382
1383/* Throw out of memory exception in case of error */
1384char *js_strndup(JSContext *ctx, const char *s, size_t n)
1385{
1386 char *ptr;
1387 ptr = js_malloc(ctx, n + 1);
1388 if (ptr) {
1389 memcpy(ptr, s, n);
1390 ptr[n] = '\0';
1391 }
1392 return ptr;
1393}
1394
1395char *js_strdup(JSContext *ctx, const char *str)
1396{
1397 return js_strndup(ctx, str, strlen(str));
1398}
1399
1400static no_inline int js_realloc_array(JSContext *ctx, void **parray,
1401 int elem_size, int *psize, int req_size)
1402{
1403 int new_size;
1404 size_t slack;
1405 void *new_array;
1406 /* XXX: potential arithmetic overflow */
1407 new_size = max_int(req_size, *psize * 3 / 2);
1408 new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
1409 if (!new_array)
1410 return -1;
1411 new_size += slack / elem_size;
1412 *psize = new_size;
1413 *parray = new_array;
1414 return 0;
1415}
1416
1417/* resize the array and update its size if req_size > *psize */
1418static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
1419 int *psize, int req_size)
1420{
1421 if (unlikely(req_size > *psize))
1422 return js_realloc_array(ctx, parray, elem_size, psize, req_size);
1423 else
1424 return 0;
1425}
1426
1427static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
1428{
1429 dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
1430}
1431
1432static inline int is_digit(int c) {
1433 return c >= '0' && c <= '9';
1434}
1435
1436static inline int string_get(const JSString *p, int idx) {
1437 return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
1438}
1439
1440typedef struct JSClassShortDef {
1441 JSAtom class_name;
1442 JSClassFinalizer *finalizer;
1443 JSClassGCMark *gc_mark;
1444} JSClassShortDef;
1445
1446static JSClassShortDef const js_std_class_def[] = {
1447 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
1448 { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
1449 { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
1450 { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
1451 { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
1452 { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
1453 { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
1454 { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */
1455 { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */
1456 { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
1457 { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */
1458 { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
1459 { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
1460 { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
1461 { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
1462 { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
1463 { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
1464 { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
1465 { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */
1466 { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */
1467 { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
1468 { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */
1469 { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */
1470 { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */
1471 { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */
1472 { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */
1473 { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */
1474 { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */
1475 { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */
1476 { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */
1477 { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
1478 { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
1479 { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
1480 { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
1481 { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
1482 { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
1483 { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
1484 { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
1485 { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
1486 { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
1487 { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
1488 { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
1489 { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
1490};
1491
1492static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
1493 int start, int count)
1494{
1495 JSClassDef cm_s, *cm = &cm_s;
1496 int i, class_id;
1497
1498 for(i = 0; i < count; i++) {
1499 class_id = i + start;
1500 memset(cm, 0, sizeof(*cm));
1501 cm->finalizer = tab[i].finalizer;
1502 cm->gc_mark = tab[i].gc_mark;
1503 if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
1504 return -1;
1505 }
1506 return 0;
1507}
1508
1509#if !defined(CONFIG_STACK_CHECK)
1510/* no stack limitation */
1511static inline uintptr_t js_get_stack_pointer(void)
1512{
1513 return 0;
1514}
1515
1516static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1517{
1518 return FALSE;
1519}
1520#else
1521/* Note: OS and CPU dependent */
1522static inline uintptr_t js_get_stack_pointer(void)
1523{
1524 return (uintptr_t)__builtin_frame_address(0);
1525}
1526
1527static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
1528{
1529 uintptr_t sp;
1530 sp = js_get_stack_pointer() - alloca_size;
1531 return unlikely(sp < rt->stack_limit);
1532}
1533#endif
1534
1535JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
1536{
1537 JSRuntime *rt;
1538 JSMallocState ms;
1539
1540 memset(&ms, 0, sizeof(ms));
1541 ms.opaque = opaque;
1542 ms.malloc_limit = -1;
1543
1544 rt = mf->js_malloc(&ms, sizeof(JSRuntime));
1545 if (!rt)
1546 return NULL;
1547 memset(rt, 0, sizeof(*rt));
1548 rt->mf = *mf;
1549 if (!rt->mf.js_malloc_usable_size) {
1550 /* use dummy function if none provided */
1551 rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
1552 }
1553 rt->malloc_state = ms;
1554 rt->malloc_gc_threshold = 256 * 1024;
1555
1556 init_list_head(&rt->context_list);
1557 init_list_head(&rt->gc_obj_list);
1558 init_list_head(&rt->gc_zero_ref_count_list);
1559 rt->gc_phase = JS_GC_PHASE_NONE;
1560 init_list_head(&rt->weakref_list);
1561
1562#ifdef DUMP_LEAKS
1563 init_list_head(&rt->string_list);
1564#endif
1565 init_list_head(&rt->job_list);
1566
1567 if (JS_InitAtoms(rt))
1568 goto fail;
1569
1570 /* create the object, array and function classes */
1571 if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
1572 countof(js_std_class_def)) < 0)
1573 goto fail;
1574 rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
1575 rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
1576 rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
1577
1578 rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
1579 rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
1580 rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
1581 rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
1582 if (init_shape_hash(rt))
1583 goto fail;
1584
1585 rt->stack_size = JS_DEFAULT_STACK_SIZE;
1586 JS_UpdateStackTop(rt);
1587
1588 rt->current_exception = JS_UNINITIALIZED;
1589
1590 return rt;
1591 fail:
1592 JS_FreeRuntime(rt);
1593 return NULL;
1594}
1595
1596void *JS_GetRuntimeOpaque(JSRuntime *rt)
1597{
1598 return rt->user_opaque;
1599}
1600
1601void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
1602{
1603 rt->user_opaque = opaque;
1604}
1605
1606/* default memory allocation functions with memory limitation */
1607static size_t js_def_malloc_usable_size(const void *ptr)
1608{
1609#if defined(__APPLE__)
1610 return malloc_size(ptr);
1611#elif defined(_WIN32)
1612 return _msize((void *)ptr);
1613#elif defined(EMSCRIPTEN)
1614 return 0;
1615#elif defined(__linux__) || defined(__GLIBC__)
1616 return malloc_usable_size((void *)ptr);
1617#else
1618 /* change this to `return 0;` if compilation fails */
1619 return malloc_usable_size((void *)ptr);
1620#endif
1621}
1622
1623static void *js_def_malloc(JSMallocState *s, size_t size)
1624{
1625 void *ptr;
1626
1627 /* Do not allocate zero bytes: behavior is platform dependent */
1628 assert(size != 0);
1629
1630 if (unlikely(s->malloc_size + size > s->malloc_limit))
1631 return NULL;
1632
1633 ptr = malloc(size);
1634 if (!ptr)
1635 return NULL;
1636
1637 s->malloc_count++;
1638 s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1639 return ptr;
1640}
1641
1642static void js_def_free(JSMallocState *s, void *ptr)
1643{
1644 if (!ptr)
1645 return;
1646
1647 s->malloc_count--;
1648 s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
1649 free(ptr);
1650}
1651
1652static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
1653{
1654 size_t old_size;
1655
1656 if (!ptr) {
1657 if (size == 0)
1658 return NULL;
1659 return js_def_malloc(s, size);
1660 }
1661 old_size = js_def_malloc_usable_size(ptr);
1662 if (size == 0) {
1663 s->malloc_count--;
1664 s->malloc_size -= old_size + MALLOC_OVERHEAD;
1665 free(ptr);
1666 return NULL;
1667 }
1668 if (s->malloc_size + size - old_size > s->malloc_limit)
1669 return NULL;
1670
1671 ptr = realloc(ptr, size);
1672 if (!ptr)
1673 return NULL;
1674
1675 s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
1676 return ptr;
1677}
1678
1679static const JSMallocFunctions def_malloc_funcs = {
1680 js_def_malloc,
1681 js_def_free,
1682 js_def_realloc,
1683 js_def_malloc_usable_size,
1684};
1685
1686JSRuntime *JS_NewRuntime(void)
1687{
1688 return JS_NewRuntime2(&def_malloc_funcs, NULL);
1689}
1690
1691void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
1692{
1693 rt->malloc_state.malloc_limit = limit;
1694}
1695
1696/* use -1 to disable automatic GC */
1697void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
1698{
1699 rt->malloc_gc_threshold = gc_threshold;
1700}
1701
1702#define malloc(s) malloc_is_forbidden(s)
1703#define free(p) free_is_forbidden(p)
1704#define realloc(p,s) realloc_is_forbidden(p,s)
1705
1706void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
1707{
1708 rt->interrupt_handler = cb;
1709 rt->interrupt_opaque = opaque;
1710}
1711
1712void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
1713{
1714 rt->can_block = can_block;
1715}
1716
1717void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
1718 const JSSharedArrayBufferFunctions *sf)
1719{
1720 rt->sab_funcs = *sf;
1721}
1722
1723void JS_SetStripInfo(JSRuntime *rt, int flags)
1724{
1725 rt->strip_flags = flags;
1726}
1727
1728int JS_GetStripInfo(JSRuntime *rt)
1729{
1730 return rt->strip_flags;
1731}
1732
1733/* return 0 if OK, < 0 if exception */
1734int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
1735 int argc, JSValueConst *argv)
1736{
1737 JSRuntime *rt = ctx->rt;
1738 JSJobEntry *e;
1739 int i;
1740
1741 e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
1742 if (!e)
1743 return -1;
1744 e->ctx = ctx;
1745 e->job_func = job_func;
1746 e->argc = argc;
1747 for(i = 0; i < argc; i++) {
1748 e->argv[i] = JS_DupValue(ctx, argv[i]);
1749 }
1750 list_add_tail(&e->link, &rt->job_list);
1751 return 0;
1752}
1753
1754BOOL JS_IsJobPending(JSRuntime *rt)
1755{
1756 return !list_empty(&rt->job_list);
1757}
1758
1759/* return < 0 if exception, 0 if no job pending, 1 if a job was
1760 executed successfully. the context of the job is stored in '*pctx' */
1761int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
1762{
1763 JSContext *ctx;
1764 JSJobEntry *e;
1765 JSValue res;
1766 int i, ret;
1767
1768 if (list_empty(&rt->job_list)) {
1769 *pctx = NULL;
1770 return 0;
1771 }
1772
1773 /* get the first pending job and execute it */
1774 e = list_entry(rt->job_list.next, JSJobEntry, link);
1775 list_del(&e->link);
1776 ctx = e->ctx;
1777 res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
1778 for(i = 0; i < e->argc; i++)
1779 JS_FreeValue(ctx, e->argv[i]);
1780 if (JS_IsException(res))
1781 ret = -1;
1782 else
1783 ret = 1;
1784 JS_FreeValue(ctx, res);
1785 js_free(ctx, e);
1786 *pctx = ctx;
1787 return ret;
1788}
1789
1790static inline uint32_t atom_get_free(const JSAtomStruct *p)
1791{
1792 return (uintptr_t)p >> 1;
1793}
1794
1795static inline BOOL atom_is_free(const JSAtomStruct *p)
1796{
1797 return (uintptr_t)p & 1;
1798}
1799
1800static inline JSAtomStruct *atom_set_free(uint32_t v)
1801{
1802 return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
1803}
1804
1805/* Note: the string contents are uninitialized */
1806static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
1807{
1808 JSString *str;
1809 str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
1810 if (unlikely(!str))
1811 return NULL;
1812 str->header.ref_count = 1;
1813 str->is_wide_char = is_wide_char;
1814 str->len = max_len;
1815 str->atom_type = 0;
1816 str->hash = 0; /* optional but costless */
1817 str->hash_next = 0; /* optional */
1818#ifdef DUMP_LEAKS
1819 list_add_tail(&str->link, &rt->string_list);
1820#endif
1821 return str;
1822}
1823
1824static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
1825{
1826 JSString *p;
1827 p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
1828 if (unlikely(!p)) {
1829 JS_ThrowOutOfMemory(ctx);
1830 return NULL;
1831 }
1832 return p;
1833}
1834
1835/* same as JS_FreeValueRT() but faster */
1836static inline void js_free_string(JSRuntime *rt, JSString *str)
1837{
1838 if (--str->header.ref_count <= 0) {
1839 if (str->atom_type) {
1840 JS_FreeAtomStruct(rt, str);
1841 } else {
1842#ifdef DUMP_LEAKS
1843 list_del(&str->link);
1844#endif
1845 js_free_rt(rt, str);
1846 }
1847 }
1848}
1849
1850void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
1851{
1852 if (rt)
1853 rt->rt_info = s;
1854}
1855
1856void JS_FreeRuntime(JSRuntime *rt)
1857{
1858 struct list_head *el, *el1;
1859 int i;
1860
1861 JS_FreeValueRT(rt, rt->current_exception);
1862
1863 list_for_each_safe(el, el1, &rt->job_list) {
1864 JSJobEntry *e = list_entry(el, JSJobEntry, link);
1865 for(i = 0; i < e->argc; i++)
1866 JS_FreeValueRT(rt, e->argv[i]);
1867 js_free_rt(rt, e);
1868 }
1869 init_list_head(&rt->job_list);
1870
1871 /* don't remove the weak objects to avoid create new jobs with
1872 FinalizationRegistry */
1873 JS_RunGCInternal(rt, FALSE);
1874
1875#ifdef DUMP_LEAKS
1876 /* leaking objects */
1877 {
1878 BOOL header_done;
1879 JSGCObjectHeader *p;
1880 int count;
1881
1882 /* remove the internal refcounts to display only the object
1883 referenced externally */
1884 list_for_each(el, &rt->gc_obj_list) {
1885 p = list_entry(el, JSGCObjectHeader, link);
1886 p->mark = 0;
1887 }
1888 gc_decref(rt);
1889
1890 header_done = FALSE;
1891 list_for_each(el, &rt->gc_obj_list) {
1892 p = list_entry(el, JSGCObjectHeader, link);
1893 if (p->ref_count != 0) {
1894 if (!header_done) {
1895 printf("Object leaks:\n");
1896 JS_DumpObjectHeader(rt);
1897 header_done = TRUE;
1898 }
1899 JS_DumpGCObject(rt, p);
1900 }
1901 }
1902
1903 count = 0;
1904 list_for_each(el, &rt->gc_obj_list) {
1905 p = list_entry(el, JSGCObjectHeader, link);
1906 if (p->ref_count == 0) {
1907 count++;
1908 }
1909 }
1910 if (count != 0)
1911 printf("Secondary object leaks: %d\n", count);
1912 }
1913#endif
1914 assert(list_empty(&rt->gc_obj_list));
1915 assert(list_empty(&rt->weakref_list));
1916
1917 /* free the classes */
1918 for(i = 0; i < rt->class_count; i++) {
1919 JSClass *cl = &rt->class_array[i];
1920 if (cl->class_id != 0) {
1921 JS_FreeAtomRT(rt, cl->class_name);
1922 }
1923 }
1924 js_free_rt(rt, rt->class_array);
1925
1926#ifdef DUMP_LEAKS
1927 /* only the atoms defined in JS_InitAtoms() should be left */
1928 {
1929 BOOL header_done = FALSE;
1930
1931 for(i = 0; i < rt->atom_size; i++) {
1932 JSAtomStruct *p = rt->atom_array[i];
1933 if (!atom_is_free(p) /* && p->str*/) {
1934 if (i >= JS_ATOM_END || p->header.ref_count != 1) {
1935 if (!header_done) {
1936 header_done = TRUE;
1937 if (rt->rt_info) {
1938 printf("%s:1: atom leakage:", rt->rt_info);
1939 } else {
1940 printf("Atom leaks:\n"
1941 " %6s %6s %s\n",
1942 "ID", "REFCNT", "NAME");
1943 }
1944 }
1945 if (rt->rt_info) {
1946 printf(" ");
1947 } else {
1948 printf(" %6u %6u ", i, p->header.ref_count);
1949 }
1950 switch (p->atom_type) {
1951 case JS_ATOM_TYPE_STRING:
1952 JS_DumpString(rt, p);
1953 break;
1954 case JS_ATOM_TYPE_GLOBAL_SYMBOL:
1955 printf("Symbol.for(");
1956 JS_DumpString(rt, p);
1957 printf(")");
1958 break;
1959 case JS_ATOM_TYPE_SYMBOL:
1960 if (p->hash != JS_ATOM_HASH_PRIVATE) {
1961 printf("Symbol(");
1962 JS_DumpString(rt, p);
1963 printf(")");
1964 } else {
1965 printf("Private(");
1966 JS_DumpString(rt, p);
1967 printf(")");
1968 }
1969 break;
1970 }
1971 if (rt->rt_info) {
1972 printf(":%u", p->header.ref_count);
1973 } else {
1974 printf("\n");
1975 }
1976 }
1977 }
1978 }
1979 if (rt->rt_info && header_done)
1980 printf("\n");
1981 }
1982#endif
1983
1984 /* free the atoms */
1985 for(i = 0; i < rt->atom_size; i++) {
1986 JSAtomStruct *p = rt->atom_array[i];
1987 if (!atom_is_free(p)) {
1988#ifdef DUMP_LEAKS
1989 list_del(&p->link);
1990#endif
1991 js_free_rt(rt, p);
1992 }
1993 }
1994 js_free_rt(rt, rt->atom_array);
1995 js_free_rt(rt, rt->atom_hash);
1996 js_free_rt(rt, rt->shape_hash);
1997#ifdef DUMP_LEAKS
1998 if (!list_empty(&rt->string_list)) {
1999 if (rt->rt_info) {
2000 printf("%s:1: string leakage:", rt->rt_info);
2001 } else {
2002 printf("String leaks:\n"
2003 " %6s %s\n",
2004 "REFCNT", "VALUE");
2005 }
2006 list_for_each_safe(el, el1, &rt->string_list) {
2007 JSString *str = list_entry(el, JSString, link);
2008 if (rt->rt_info) {
2009 printf(" ");
2010 } else {
2011 printf(" %6u ", str->header.ref_count);
2012 }
2013 JS_DumpString(rt, str);
2014 if (rt->rt_info) {
2015 printf(":%u", str->header.ref_count);
2016 } else {
2017 printf("\n");
2018 }
2019 list_del(&str->link);
2020 js_free_rt(rt, str);
2021 }
2022 if (rt->rt_info)
2023 printf("\n");
2024 }
2025 {
2026 JSMallocState *s = &rt->malloc_state;
2027 if (s->malloc_count > 1) {
2028 if (rt->rt_info)
2029 printf("%s:1: ", rt->rt_info);
2030 printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
2031 (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
2032 (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
2033 }
2034 }
2035#endif
2036
2037 {
2038 JSMallocState ms = rt->malloc_state;
2039 rt->mf.js_free(&ms, rt);
2040 }
2041}
2042
2043JSContext *JS_NewContextRaw(JSRuntime *rt)
2044{
2045 JSContext *ctx;
2046 int i;
2047
2048 ctx = js_mallocz_rt(rt, sizeof(JSContext));
2049 if (!ctx)
2050 return NULL;
2051 ctx->header.ref_count = 1;
2052 add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
2053
2054 ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
2055 rt->class_count);
2056 if (!ctx->class_proto) {
2057 js_free_rt(rt, ctx);
2058 return NULL;
2059 }
2060 ctx->rt = rt;
2061 list_add_tail(&ctx->link, &rt->context_list);
2062 for(i = 0; i < rt->class_count; i++)
2063 ctx->class_proto[i] = JS_NULL;
2064 ctx->array_ctor = JS_NULL;
2065 ctx->regexp_ctor = JS_NULL;
2066 ctx->promise_ctor = JS_NULL;
2067 init_list_head(&ctx->loaded_modules);
2068
2069 JS_AddIntrinsicBasicObjects(ctx);
2070 return ctx;
2071}
2072
2073JSContext *JS_NewContext(JSRuntime *rt)
2074{
2075 JSContext *ctx;
2076
2077 ctx = JS_NewContextRaw(rt);
2078 if (!ctx)
2079 return NULL;
2080
2081 JS_AddIntrinsicBaseObjects(ctx);
2082 JS_AddIntrinsicDate(ctx);
2083 JS_AddIntrinsicEval(ctx);
2084 JS_AddIntrinsicStringNormalize(ctx);
2085 JS_AddIntrinsicRegExp(ctx);
2086 JS_AddIntrinsicJSON(ctx);
2087 JS_AddIntrinsicProxy(ctx);
2088 JS_AddIntrinsicMapSet(ctx);
2089 JS_AddIntrinsicTypedArrays(ctx);
2090 JS_AddIntrinsicPromise(ctx);
2091 JS_AddIntrinsicWeakRef(ctx);
2092 return ctx;
2093}
2094
2095void *JS_GetContextOpaque(JSContext *ctx)
2096{
2097 return ctx->user_opaque;
2098}
2099
2100void JS_SetContextOpaque(JSContext *ctx, void *opaque)
2101{
2102 ctx->user_opaque = opaque;
2103}
2104
2105/* set the new value and free the old value after (freeing the value
2106 can reallocate the object data) */
2107static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
2108{
2109 JSValue old_val;
2110 old_val = *pval;
2111 *pval = new_val;
2112 JS_FreeValue(ctx, old_val);
2113}
2114
2115void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
2116{
2117 JSRuntime *rt = ctx->rt;
2118 assert(class_id < rt->class_count);
2119 set_value(ctx, &ctx->class_proto[class_id], obj);
2120}
2121
2122JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
2123{
2124 JSRuntime *rt = ctx->rt;
2125 assert(class_id < rt->class_count);
2126 return JS_DupValue(ctx, ctx->class_proto[class_id]);
2127}
2128
2129typedef enum JSFreeModuleEnum {
2130 JS_FREE_MODULE_ALL,
2131 JS_FREE_MODULE_NOT_RESOLVED,
2132} JSFreeModuleEnum;
2133
2134/* XXX: would be more efficient with separate module lists */
2135static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
2136{
2137 struct list_head *el, *el1;
2138 list_for_each_safe(el, el1, &ctx->loaded_modules) {
2139 JSModuleDef *m = list_entry(el, JSModuleDef, link);
2140 if (flag == JS_FREE_MODULE_ALL ||
2141 (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) {
2142 js_free_module_def(ctx, m);
2143 }
2144 }
2145}
2146
2147JSContext *JS_DupContext(JSContext *ctx)
2148{
2149 ctx->header.ref_count++;
2150 return ctx;
2151}
2152
2153/* used by the GC */
2154static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
2155 JS_MarkFunc *mark_func)
2156{
2157 int i;
2158 struct list_head *el;
2159
2160 /* modules are not seen by the GC, so we directly mark the objects
2161 referenced by each module */
2162 list_for_each(el, &ctx->loaded_modules) {
2163 JSModuleDef *m = list_entry(el, JSModuleDef, link);
2164 js_mark_module_def(rt, m, mark_func);
2165 }
2166
2167 JS_MarkValue(rt, ctx->global_obj, mark_func);
2168 JS_MarkValue(rt, ctx->global_var_obj, mark_func);
2169
2170 JS_MarkValue(rt, ctx->throw_type_error, mark_func);
2171 JS_MarkValue(rt, ctx->eval_obj, mark_func);
2172
2173 JS_MarkValue(rt, ctx->array_proto_values, mark_func);
2174 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2175 JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
2176 }
2177 for(i = 0; i < rt->class_count; i++) {
2178 JS_MarkValue(rt, ctx->class_proto[i], mark_func);
2179 }
2180 JS_MarkValue(rt, ctx->iterator_proto, mark_func);
2181 JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
2182 JS_MarkValue(rt, ctx->promise_ctor, mark_func);
2183 JS_MarkValue(rt, ctx->array_ctor, mark_func);
2184 JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
2185 JS_MarkValue(rt, ctx->function_ctor, mark_func);
2186 JS_MarkValue(rt, ctx->function_proto, mark_func);
2187
2188 if (ctx->array_shape)
2189 mark_func(rt, &ctx->array_shape->header);
2190}
2191
2192void JS_FreeContext(JSContext *ctx)
2193{
2194 JSRuntime *rt = ctx->rt;
2195 int i;
2196
2197 if (--ctx->header.ref_count > 0)
2198 return;
2199 assert(ctx->header.ref_count == 0);
2200
2201#ifdef DUMP_ATOMS
2202 JS_DumpAtoms(ctx->rt);
2203#endif
2204#ifdef DUMP_SHAPES
2205 JS_DumpShapes(ctx->rt);
2206#endif
2207#ifdef DUMP_OBJECTS
2208 {
2209 struct list_head *el;
2210 JSGCObjectHeader *p;
2211 printf("JSObjects: {\n");
2212 JS_DumpObjectHeader(ctx->rt);
2213 list_for_each(el, &rt->gc_obj_list) {
2214 p = list_entry(el, JSGCObjectHeader, link);
2215 JS_DumpGCObject(rt, p);
2216 }
2217 printf("}\n");
2218 }
2219#endif
2220#ifdef DUMP_MEM
2221 {
2222 JSMemoryUsage stats;
2223 JS_ComputeMemoryUsage(rt, &stats);
2224 JS_DumpMemoryUsage(stdout, &stats, rt);
2225 }
2226#endif
2227
2228 js_free_modules(ctx, JS_FREE_MODULE_ALL);
2229
2230 JS_FreeValue(ctx, ctx->global_obj);
2231 JS_FreeValue(ctx, ctx->global_var_obj);
2232
2233 JS_FreeValue(ctx, ctx->throw_type_error);
2234 JS_FreeValue(ctx, ctx->eval_obj);
2235
2236 JS_FreeValue(ctx, ctx->array_proto_values);
2237 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
2238 JS_FreeValue(ctx, ctx->native_error_proto[i]);
2239 }
2240 for(i = 0; i < rt->class_count; i++) {
2241 JS_FreeValue(ctx, ctx->class_proto[i]);
2242 }
2243 js_free_rt(rt, ctx->class_proto);
2244 JS_FreeValue(ctx, ctx->iterator_proto);
2245 JS_FreeValue(ctx, ctx->async_iterator_proto);
2246 JS_FreeValue(ctx, ctx->promise_ctor);
2247 JS_FreeValue(ctx, ctx->array_ctor);
2248 JS_FreeValue(ctx, ctx->regexp_ctor);
2249 JS_FreeValue(ctx, ctx->function_ctor);
2250 JS_FreeValue(ctx, ctx->function_proto);
2251
2252 js_free_shape_null(ctx->rt, ctx->array_shape);
2253
2254 list_del(&ctx->link);
2255 remove_gc_object(&ctx->header);
2256 js_free_rt(ctx->rt, ctx);
2257}
2258
2259JSRuntime *JS_GetRuntime(JSContext *ctx)
2260{
2261 return ctx->rt;
2262}
2263
2264static void update_stack_limit(JSRuntime *rt)
2265{
2266 if (rt->stack_size == 0) {
2267 rt->stack_limit = 0; /* no limit */
2268 } else {
2269 rt->stack_limit = rt->stack_top - rt->stack_size;
2270 }
2271}
2272
2273void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
2274{
2275 rt->stack_size = stack_size;
2276 update_stack_limit(rt);
2277}
2278
2279void JS_UpdateStackTop(JSRuntime *rt)
2280{
2281 rt->stack_top = js_get_stack_pointer();
2282 update_stack_limit(rt);
2283}
2284
2285static inline BOOL is_strict_mode(JSContext *ctx)
2286{
2287 JSStackFrame *sf = ctx->rt->current_stack_frame;
2288 return (sf && (sf->js_mode & JS_MODE_STRICT));
2289}
2290
2291/* JSAtom support */
2292
2293#define JS_ATOM_TAG_INT (1U << 31)
2294#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
2295#define JS_ATOM_MAX ((1U << 30) - 1)
2296
2297/* return the max count from the hash size */
2298#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
2299
2300static inline BOOL __JS_AtomIsConst(JSAtom v)
2301{
2302#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
2303 return (int32_t)v <= 0;
2304#else
2305 return (int32_t)v < JS_ATOM_END;
2306#endif
2307}
2308
2309static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
2310{
2311 return (v & JS_ATOM_TAG_INT) != 0;
2312}
2313
2314static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
2315{
2316 return v | JS_ATOM_TAG_INT;
2317}
2318
2319static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
2320{
2321 return atom & ~JS_ATOM_TAG_INT;
2322}
2323
2324static inline int is_num(int c)
2325{
2326 return c >= '0' && c <= '9';
2327}
2328
2329/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
2330static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
2331{
2332 uint32_t n;
2333 uint64_t n64;
2334 int c, i, len;
2335
2336 len = p->len;
2337 if (len == 0 || len > 10)
2338 return FALSE;
2339 c = string_get(p, 0);
2340 if (is_num(c)) {
2341 if (c == '0') {
2342 if (len != 1)
2343 return FALSE;
2344 n = 0;
2345 } else {
2346 n = c - '0';
2347 for(i = 1; i < len; i++) {
2348 c = string_get(p, i);
2349 if (!is_num(c))
2350 return FALSE;
2351 n64 = (uint64_t)n * 10 + (c - '0');
2352 if ((n64 >> 32) != 0)
2353 return FALSE;
2354 n = n64;
2355 }
2356 }
2357 *pval = n;
2358 return TRUE;
2359 } else {
2360 return FALSE;
2361 }
2362}
2363
2364/* XXX: could use faster version ? */
2365static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
2366{
2367 size_t i;
2368
2369 for(i = 0; i < len; i++)
2370 h = h * 263 + str[i];
2371 return h;
2372}
2373
2374static inline uint32_t hash_string16(const uint16_t *str,
2375 size_t len, uint32_t h)
2376{
2377 size_t i;
2378
2379 for(i = 0; i < len; i++)
2380 h = h * 263 + str[i];
2381 return h;
2382}
2383
2384static uint32_t hash_string(const JSString *str, uint32_t h)
2385{
2386 if (str->is_wide_char)
2387 h = hash_string16(str->u.str16, str->len, h);
2388 else
2389 h = hash_string8(str->u.str8, str->len, h);
2390 return h;
2391}
2392
2393static uint32_t hash_string_rope(JSValueConst val, uint32_t h)
2394{
2395 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
2396 return hash_string(JS_VALUE_GET_STRING(val), h);
2397 } else {
2398 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
2399 h = hash_string_rope(r->left, h);
2400 return hash_string_rope(r->right, h);
2401 }
2402}
2403
2404static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep)
2405{
2406 if (c == sep || c == '\\') {
2407 putchar('\\');
2408 putchar(c);
2409 } else if (c >= ' ' && c <= 126) {
2410 putchar(c);
2411 } else if (c == '\n') {
2412 putchar('\\');
2413 putchar('n');
2414 } else {
2415 printf("\\u%04x", c);
2416 }
2417}
2418
2419static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p)
2420{
2421 int i, sep;
2422
2423 if (p == NULL) {
2424 printf("<null>");
2425 return;
2426 }
2427 printf("%d", p->header.ref_count);
2428 sep = (p->header.ref_count == 1) ? '\"' : '\'';
2429 putchar(sep);
2430 for(i = 0; i < p->len; i++) {
2431 JS_DumpChar(rt, string_get(p, i), sep);
2432 }
2433 putchar(sep);
2434}
2435
2436static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
2437{
2438 JSAtomStruct *p;
2439 int h, i;
2440 /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
2441 printf("JSAtom count=%d size=%d hash_size=%d:\n",
2442 rt->atom_count, rt->atom_size, rt->atom_hash_size);
2443 printf("JSAtom hash table: {\n");
2444 for(i = 0; i < rt->atom_hash_size; i++) {
2445 h = rt->atom_hash[i];
2446 if (h) {
2447 printf(" %d:", i);
2448 while (h) {
2449 p = rt->atom_array[h];
2450 printf(" ");
2451 JS_DumpString(rt, p);
2452 h = p->hash_next;
2453 }
2454 printf("\n");
2455 }
2456 }
2457 printf("}\n");
2458 printf("JSAtom table: {\n");
2459 for(i = 0; i < rt->atom_size; i++) {
2460 p = rt->atom_array[i];
2461 if (!atom_is_free(p)) {
2462 printf(" %d: { %d %08x ", i, p->atom_type, p->hash);
2463 if (!(p->len == 0 && p->is_wide_char != 0))
2464 JS_DumpString(rt, p);
2465 printf(" %d }\n", p->hash_next);
2466 }
2467 }
2468 printf("}\n");
2469}
2470
2471static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
2472{
2473 JSAtomStruct *p;
2474 uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
2475
2476 assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
2477 new_hash_mask = new_hash_size - 1;
2478 new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
2479 if (!new_hash)
2480 return -1;
2481 for(i = 0; i < rt->atom_hash_size; i++) {
2482 h = rt->atom_hash[i];
2483 while (h != 0) {
2484 p = rt->atom_array[h];
2485 hash_next1 = p->hash_next;
2486 /* add in new hash table */
2487 j = p->hash & new_hash_mask;
2488 p->hash_next = new_hash[j];
2489 new_hash[j] = h;
2490 h = hash_next1;
2491 }
2492 }
2493 js_free_rt(rt, rt->atom_hash);
2494 rt->atom_hash = new_hash;
2495 rt->atom_hash_size = new_hash_size;
2496 rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
2497 // JS_DumpAtoms(rt);
2498 return 0;
2499}
2500
2501static int JS_InitAtoms(JSRuntime *rt)
2502{
2503 int i, len, atom_type;
2504 const char *p;
2505
2506 rt->atom_hash_size = 0;
2507 rt->atom_hash = NULL;
2508 rt->atom_count = 0;
2509 rt->atom_size = 0;
2510 rt->atom_free_index = 0;
2511 if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */
2512 return -1;
2513
2514 p = js_atom_init;
2515 for(i = 1; i < JS_ATOM_END; i++) {
2516 if (i == JS_ATOM_Private_brand)
2517 atom_type = JS_ATOM_TYPE_PRIVATE;
2518 else if (i >= JS_ATOM_Symbol_toPrimitive)
2519 atom_type = JS_ATOM_TYPE_SYMBOL;
2520 else
2521 atom_type = JS_ATOM_TYPE_STRING;
2522 len = strlen(p);
2523 if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
2524 return -1;
2525 p = p + len + 1;
2526 }
2527 return 0;
2528}
2529
2530static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
2531{
2532 JSAtomStruct *p;
2533
2534 if (!__JS_AtomIsConst(v)) {
2535 p = rt->atom_array[v];
2536 p->header.ref_count++;
2537 }
2538 return v;
2539}
2540
2541JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
2542{
2543 JSRuntime *rt;
2544 JSAtomStruct *p;
2545
2546 if (!__JS_AtomIsConst(v)) {
2547 rt = ctx->rt;
2548 p = rt->atom_array[v];
2549 p->header.ref_count++;
2550 }
2551 return v;
2552}
2553
2554static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
2555{
2556 JSRuntime *rt;
2557 JSAtomStruct *p;
2558
2559 rt = ctx->rt;
2560 if (__JS_AtomIsTaggedInt(v))
2561 return JS_ATOM_KIND_STRING;
2562 p = rt->atom_array[v];
2563 switch(p->atom_type) {
2564 case JS_ATOM_TYPE_STRING:
2565 return JS_ATOM_KIND_STRING;
2566 case JS_ATOM_TYPE_GLOBAL_SYMBOL:
2567 return JS_ATOM_KIND_SYMBOL;
2568 case JS_ATOM_TYPE_SYMBOL:
2569 if (p->hash == JS_ATOM_HASH_PRIVATE)
2570 return JS_ATOM_KIND_PRIVATE;
2571 else
2572 return JS_ATOM_KIND_SYMBOL;
2573 default:
2574 abort();
2575 }
2576}
2577
2578static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
2579{
2580 return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
2581}
2582
2583static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
2584{
2585 uint32_t i = p->hash_next; /* atom_index */
2586 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2587 JSAtomStruct *p1;
2588
2589 i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
2590 p1 = rt->atom_array[i];
2591 while (p1 != p) {
2592 assert(i != 0);
2593 i = p1->hash_next;
2594 p1 = rt->atom_array[i];
2595 }
2596 }
2597 return i;
2598}
2599
2600/* string case (internal). Return JS_ATOM_NULL if error. 'str' is
2601 freed. */
2602static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
2603{
2604 uint32_t h, h1, i;
2605 JSAtomStruct *p;
2606 int len;
2607
2608#if 0
2609 printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
2610#endif
2611 if (atom_type < JS_ATOM_TYPE_SYMBOL) {
2612 /* str is not NULL */
2613 if (str->atom_type == atom_type) {
2614 /* str is the atom, return its index */
2615 i = js_get_atom_index(rt, str);
2616 /* reduce string refcount and increase atom's unless constant */
2617 if (__JS_AtomIsConst(i))
2618 str->header.ref_count--;
2619 return i;
2620 }
2621 /* try and locate an already registered atom */
2622 len = str->len;
2623 h = hash_string(str, atom_type);
2624 h &= JS_ATOM_HASH_MASK;
2625 h1 = h & (rt->atom_hash_size - 1);
2626 i = rt->atom_hash[h1];
2627 while (i != 0) {
2628 p = rt->atom_array[i];
2629 if (p->hash == h &&
2630 p->atom_type == atom_type &&
2631 p->len == len &&
2632 js_string_memcmp(p, 0, str, 0, len) == 0) {
2633 if (!__JS_AtomIsConst(i))
2634 p->header.ref_count++;
2635 goto done;
2636 }
2637 i = p->hash_next;
2638 }
2639 } else {
2640 h1 = 0; /* avoid warning */
2641 if (atom_type == JS_ATOM_TYPE_SYMBOL) {
2642 h = 0;
2643 } else {
2644 h = JS_ATOM_HASH_PRIVATE;
2645 atom_type = JS_ATOM_TYPE_SYMBOL;
2646 }
2647 }
2648
2649 if (rt->atom_free_index == 0) {
2650 /* allow new atom entries */
2651 uint32_t new_size, start;
2652 JSAtomStruct **new_array;
2653
2654 /* alloc new with size progression 3/2:
2655 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
2656 preallocating space for predefined atoms (at least 195).
2657 */
2658 new_size = max_int(211, rt->atom_size * 3 / 2);
2659 if (new_size > JS_ATOM_MAX)
2660 goto fail;
2661 /* XXX: should use realloc2 to use slack space */
2662 new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
2663 if (!new_array)
2664 goto fail;
2665 /* Note: the atom 0 is not used */
2666 start = rt->atom_size;
2667 if (start == 0) {
2668 /* JS_ATOM_NULL entry */
2669 p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
2670 if (!p) {
2671 js_free_rt(rt, new_array);
2672 goto fail;
2673 }
2674 p->header.ref_count = 1; /* not refcounted */
2675 p->atom_type = JS_ATOM_TYPE_SYMBOL;
2676#ifdef DUMP_LEAKS
2677 list_add_tail(&p->link, &rt->string_list);
2678#endif
2679 new_array[0] = p;
2680 rt->atom_count++;
2681 start = 1;
2682 }
2683 rt->atom_size = new_size;
2684 rt->atom_array = new_array;
2685 rt->atom_free_index = start;
2686 for(i = start; i < new_size; i++) {
2687 uint32_t next;
2688 if (i == (new_size - 1))
2689 next = 0;
2690 else
2691 next = i + 1;
2692 rt->atom_array[i] = atom_set_free(next);
2693 }
2694 }
2695
2696 if (str) {
2697 if (str->atom_type == 0) {
2698 p = str;
2699 p->atom_type = atom_type;
2700 } else {
2701 p = js_malloc_rt(rt, sizeof(JSString) +
2702 (str->len << str->is_wide_char) +
2703 1 - str->is_wide_char);
2704 if (unlikely(!p))
2705 goto fail;
2706 p->header.ref_count = 1;
2707 p->is_wide_char = str->is_wide_char;
2708 p->len = str->len;
2709#ifdef DUMP_LEAKS
2710 list_add_tail(&p->link, &rt->string_list);
2711#endif
2712 memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
2713 1 - str->is_wide_char);
2714 js_free_string(rt, str);
2715 }
2716 } else {
2717 p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
2718 if (!p)
2719 return JS_ATOM_NULL;
2720 p->header.ref_count = 1;
2721 p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
2722 p->len = 0;
2723#ifdef DUMP_LEAKS
2724 list_add_tail(&p->link, &rt->string_list);
2725#endif
2726 }
2727
2728 /* use an already free entry */
2729 i = rt->atom_free_index;
2730 rt->atom_free_index = atom_get_free(rt->atom_array[i]);
2731 rt->atom_array[i] = p;
2732
2733 p->hash = h;
2734 p->hash_next = i; /* atom_index */
2735 p->atom_type = atom_type;
2736
2737 rt->atom_count++;
2738
2739 if (atom_type != JS_ATOM_TYPE_SYMBOL) {
2740 p->hash_next = rt->atom_hash[h1];
2741 rt->atom_hash[h1] = i;
2742 if (unlikely(rt->atom_count >= rt->atom_count_resize))
2743 JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
2744 }
2745
2746 // JS_DumpAtoms(rt);
2747 return i;
2748
2749 fail:
2750 i = JS_ATOM_NULL;
2751 done:
2752 if (str)
2753 js_free_string(rt, str);
2754 return i;
2755}
2756
2757/* only works with zero terminated 8 bit strings */
2758static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
2759 int atom_type)
2760{
2761 JSString *p;
2762 p = js_alloc_string_rt(rt, len, 0);
2763 if (!p)
2764 return JS_ATOM_NULL;
2765 memcpy(p->u.str8, str, len);
2766 p->u.str8[len] = '\0';
2767 return __JS_NewAtom(rt, p, atom_type);
2768}
2769
2770/* Warning: str must be ASCII only */
2771static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
2772 int atom_type)
2773{
2774 uint32_t h, h1, i;
2775 JSAtomStruct *p;
2776
2777 h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
2778 h &= JS_ATOM_HASH_MASK;
2779 h1 = h & (rt->atom_hash_size - 1);
2780 i = rt->atom_hash[h1];
2781 while (i != 0) {
2782 p = rt->atom_array[i];
2783 if (p->hash == h &&
2784 p->atom_type == JS_ATOM_TYPE_STRING &&
2785 p->len == len &&
2786 p->is_wide_char == 0 &&
2787 memcmp(p->u.str8, str, len) == 0) {
2788 if (!__JS_AtomIsConst(i))
2789 p->header.ref_count++;
2790 return i;
2791 }
2792 i = p->hash_next;
2793 }
2794 return JS_ATOM_NULL;
2795}
2796
2797static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
2798{
2799#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
2800 if (unlikely(i == JS_ATOM_NULL)) {
2801 p->header.ref_count = INT32_MAX / 2;
2802 return;
2803 }
2804#endif
2805 uint32_t i = p->hash_next; /* atom_index */
2806 if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
2807 JSAtomStruct *p0, *p1;
2808 uint32_t h0;
2809
2810 h0 = p->hash & (rt->atom_hash_size - 1);
2811 i = rt->atom_hash[h0];
2812 p1 = rt->atom_array[i];
2813 if (p1 == p) {
2814 rt->atom_hash[h0] = p1->hash_next;
2815 } else {
2816 for(;;) {
2817 assert(i != 0);
2818 p0 = p1;
2819 i = p1->hash_next;
2820 p1 = rt->atom_array[i];
2821 if (p1 == p) {
2822 p0->hash_next = p1->hash_next;
2823 break;
2824 }
2825 }
2826 }
2827 }
2828 /* insert in free atom list */
2829 rt->atom_array[i] = atom_set_free(rt->atom_free_index);
2830 rt->atom_free_index = i;
2831 /* free the string structure */
2832#ifdef DUMP_LEAKS
2833 list_del(&p->link);
2834#endif
2835 if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
2836 p->hash != JS_ATOM_HASH_PRIVATE && p->hash != 0) {
2837 /* live weak references are still present on this object: keep
2838 it */
2839 } else {
2840 js_free_rt(rt, p);
2841 }
2842 rt->atom_count--;
2843 assert(rt->atom_count >= 0);
2844}
2845
2846static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
2847{
2848 JSAtomStruct *p;
2849
2850 p = rt->atom_array[i];
2851 if (--p->header.ref_count > 0)
2852 return;
2853 JS_FreeAtomStruct(rt, p);
2854}
2855
2856/* Warning: 'p' is freed */
2857static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
2858{
2859 JSRuntime *rt = ctx->rt;
2860 uint32_t n;
2861 if (is_num_string(&n, p)) {
2862 if (n <= JS_ATOM_MAX_INT) {
2863 js_free_string(rt, p);
2864 return __JS_AtomFromUInt32(n);
2865 }
2866 }
2867 /* XXX: should generate an exception */
2868 return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
2869}
2870
2871/* XXX: optimize */
2872static size_t count_ascii(const uint8_t *buf, size_t len)
2873{
2874 const uint8_t *p, *p_end;
2875 p = buf;
2876 p_end = buf + len;
2877 while (p < p_end && *p < 128)
2878 p++;
2879 return p - buf;
2880}
2881
2882/* str is UTF-8 encoded */
2883JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
2884{
2885 JSValue val;
2886
2887 if (len == 0 ||
2888 (!is_digit(*str) &&
2889 count_ascii((const uint8_t *)str, len) == len)) {
2890 JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
2891 if (atom)
2892 return atom;
2893 }
2894 val = JS_NewStringLen(ctx, str, len);
2895 if (JS_IsException(val))
2896 return JS_ATOM_NULL;
2897 return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
2898}
2899
2900JSAtom JS_NewAtom(JSContext *ctx, const char *str)
2901{
2902 return JS_NewAtomLen(ctx, str, strlen(str));
2903}
2904
2905JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
2906{
2907 if (n <= JS_ATOM_MAX_INT) {
2908 return __JS_AtomFromUInt32(n);
2909 } else {
2910 char buf[11];
2911 JSValue val;
2912 size_t len;
2913 len = u32toa(buf, n);
2914 val = js_new_string8_len(ctx, buf, len);
2915 if (JS_IsException(val))
2916 return JS_ATOM_NULL;
2917 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
2918 JS_ATOM_TYPE_STRING);
2919 }
2920}
2921
2922static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
2923{
2924 if ((uint64_t)n <= JS_ATOM_MAX_INT) {
2925 return __JS_AtomFromUInt32((uint32_t)n);
2926 } else {
2927 char buf[24];
2928 JSValue val;
2929 size_t len;
2930 len = i64toa(buf, n);
2931 val = js_new_string8_len(ctx, buf, len);
2932 if (JS_IsException(val))
2933 return JS_ATOM_NULL;
2934 return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
2935 JS_ATOM_TYPE_STRING);
2936 }
2937}
2938
2939/* 'p' is freed */
2940static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
2941{
2942 JSRuntime *rt = ctx->rt;
2943 JSAtom atom;
2944 atom = __JS_NewAtom(rt, p, atom_type);
2945 if (atom == JS_ATOM_NULL)
2946 return JS_ThrowOutOfMemory(ctx);
2947 return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
2948}
2949
2950/* descr must be a non-numeric string atom */
2951static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
2952 int atom_type)
2953{
2954 JSRuntime *rt = ctx->rt;
2955 JSString *p;
2956
2957 assert(!__JS_AtomIsTaggedInt(descr));
2958 assert(descr < rt->atom_size);
2959 p = rt->atom_array[descr];
2960 JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
2961 return JS_NewSymbol(ctx, p, atom_type);
2962}
2963
2964#define ATOM_GET_STR_BUF_SIZE 64
2965
2966/* Should only be used for debug. */
2967static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
2968 JSAtom atom)
2969{
2970 if (__JS_AtomIsTaggedInt(atom)) {
2971 snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
2972 } else {
2973 JSAtomStruct *p;
2974 assert(atom < rt->atom_size);
2975 if (atom == JS_ATOM_NULL) {
2976 snprintf(buf, buf_size, "<null>");
2977 } else {
2978 int i, c;
2979 char *q;
2980 JSString *str;
2981
2982 q = buf;
2983 p = rt->atom_array[atom];
2984 assert(!atom_is_free(p));
2985 str = p;
2986 if (str) {
2987 if (!str->is_wide_char) {
2988 /* special case ASCII strings */
2989 c = 0;
2990 for(i = 0; i < str->len; i++) {
2991 c |= str->u.str8[i];
2992 }
2993 if (c < 0x80)
2994 return (const char *)str->u.str8;
2995 }
2996 for(i = 0; i < str->len; i++) {
2997 c = string_get(str, i);
2998 if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
2999 break;
3000 if (c < 128) {
3001 *q++ = c;
3002 } else {
3003 q += unicode_to_utf8((uint8_t *)q, c);
3004 }
3005 }
3006 }
3007 *q = '\0';
3008 }
3009 }
3010 return buf;
3011}
3012
3013static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
3014{
3015 return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
3016}
3017
3018static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
3019{
3020 char buf[ATOM_GET_STR_BUF_SIZE];
3021
3022 if (__JS_AtomIsTaggedInt(atom)) {
3023 size_t len = u32toa(buf, __JS_AtomToUInt32(atom));
3024 return js_new_string8_len(ctx, buf, len);
3025 } else {
3026 JSRuntime *rt = ctx->rt;
3027 JSAtomStruct *p;
3028 assert(atom < rt->atom_size);
3029 p = rt->atom_array[atom];
3030 if (p->atom_type == JS_ATOM_TYPE_STRING) {
3031 goto ret_string;
3032 } else if (force_string) {
3033 if (p->len == 0 && p->is_wide_char != 0) {
3034 /* no description string */
3035 p = rt->atom_array[JS_ATOM_empty_string];
3036 }
3037 ret_string:
3038 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3039 } else {
3040 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
3041 }
3042 }
3043}
3044
3045JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
3046{
3047 return __JS_AtomToValue(ctx, atom, FALSE);
3048}
3049
3050JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
3051{
3052 return __JS_AtomToValue(ctx, atom, TRUE);
3053}
3054
3055/* return TRUE if the atom is an array index (i.e. 0 <= index <=
3056 2^32-2 and return its value */
3057static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
3058{
3059 if (__JS_AtomIsTaggedInt(atom)) {
3060 *pval = __JS_AtomToUInt32(atom);
3061 return TRUE;
3062 } else {
3063 JSRuntime *rt = ctx->rt;
3064 JSAtomStruct *p;
3065 uint32_t val;
3066
3067 assert(atom < rt->atom_size);
3068 p = rt->atom_array[atom];
3069 if (p->atom_type == JS_ATOM_TYPE_STRING &&
3070 is_num_string(&val, p) && val != -1) {
3071 *pval = val;
3072 return TRUE;
3073 } else {
3074 *pval = 0;
3075 return FALSE;
3076 }
3077 }
3078}
3079
3080/* This test must be fast if atom is not a numeric index (e.g. a
3081 method name). Return JS_UNDEFINED if not a numeric
3082 index. JS_EXCEPTION can also be returned. */
3083static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
3084{
3085 JSRuntime *rt = ctx->rt;
3086 JSAtomStruct *p1;
3087 JSString *p;
3088 int c, ret;
3089 JSValue num, str;
3090
3091 if (__JS_AtomIsTaggedInt(atom))
3092 return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
3093 assert(atom < rt->atom_size);
3094 p1 = rt->atom_array[atom];
3095 if (p1->atom_type != JS_ATOM_TYPE_STRING)
3096 return JS_UNDEFINED;
3097 switch(atom) {
3098 case JS_ATOM_minus_zero:
3099 return __JS_NewFloat64(ctx, -0.0);
3100 case JS_ATOM_Infinity:
3101 return __JS_NewFloat64(ctx, INFINITY);
3102 case JS_ATOM_minus_Infinity:
3103 return __JS_NewFloat64(ctx, -INFINITY);
3104 case JS_ATOM_NaN:
3105 return __JS_NewFloat64(ctx, NAN);
3106 default:
3107 break;
3108 }
3109 p = p1;
3110 if (p->len == 0)
3111 return JS_UNDEFINED;
3112 c = string_get(p, 0);
3113 if (!is_num(c) && c != '-')
3114 return JS_UNDEFINED;
3115 /* this is ECMA CanonicalNumericIndexString primitive */
3116 num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
3117 if (JS_IsException(num))
3118 return num;
3119 str = JS_ToString(ctx, num);
3120 if (JS_IsException(str)) {
3121 JS_FreeValue(ctx, num);
3122 return str;
3123 }
3124 ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
3125 JS_FreeValue(ctx, str);
3126 if (ret == 0) {
3127 return num;
3128 } else {
3129 JS_FreeValue(ctx, num);
3130 return JS_UNDEFINED;
3131 }
3132}
3133
3134/* return -1 if exception or TRUE/FALSE */
3135static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
3136{
3137 JSValue num;
3138 num = JS_AtomIsNumericIndex1(ctx, atom);
3139 if (likely(JS_IsUndefined(num)))
3140 return FALSE;
3141 if (JS_IsException(num))
3142 return -1;
3143 JS_FreeValue(ctx, num);
3144 return TRUE;
3145}
3146
3147void JS_FreeAtom(JSContext *ctx, JSAtom v)
3148{
3149 if (!__JS_AtomIsConst(v))
3150 __JS_FreeAtom(ctx->rt, v);
3151}
3152
3153void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
3154{
3155 if (!__JS_AtomIsConst(v))
3156 __JS_FreeAtom(rt, v);
3157}
3158
3159/* return TRUE if 'v' is a symbol with a string description */
3160static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
3161{
3162 JSRuntime *rt;
3163 JSAtomStruct *p;
3164
3165 rt = ctx->rt;
3166 if (__JS_AtomIsTaggedInt(v))
3167 return FALSE;
3168 p = rt->atom_array[v];
3169 return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
3170 p->hash != JS_ATOM_HASH_PRIVATE) ||
3171 p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
3172 !(p->len == 0 && p->is_wide_char != 0));
3173}
3174
3175static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
3176{
3177 char buf[ATOM_GET_STR_BUF_SIZE];
3178 const char *p;
3179 int i;
3180
3181 /* XXX: should handle embedded null characters */
3182 /* XXX: should move encoding code to JS_AtomGetStr */
3183 p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
3184 for (i = 0; p[i]; i++) {
3185 int c = (unsigned char)p[i];
3186 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
3187 (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
3188 break;
3189 }
3190 if (i > 0 && p[i] == '\0') {
3191 printf("%s", p);
3192 } else {
3193 putchar('"');
3194 printf("%.*s", i, p);
3195 for (; p[i]; i++) {
3196 int c = (unsigned char)p[i];
3197 if (c == '\"' || c == '\\') {
3198 putchar('\\');
3199 putchar(c);
3200 } else if (c >= ' ' && c <= 126) {
3201 putchar(c);
3202 } else if (c == '\n') {
3203 putchar('\\');
3204 putchar('n');
3205 } else {
3206 printf("\\u%04x", c);
3207 }
3208 }
3209 putchar('\"');
3210 }
3211}
3212
3213/* free with JS_FreeCString() */
3214const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
3215{
3216 JSValue str;
3217 const char *cstr;
3218
3219 str = JS_AtomToString(ctx, atom);
3220 if (JS_IsException(str))
3221 return NULL;
3222 cstr = JS_ToCString(ctx, str);
3223 JS_FreeValue(ctx, str);
3224 return cstr;
3225}
3226
3227/* return a string atom containing name concatenated with str1 */
3228static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
3229{
3230 JSValue str;
3231 JSAtom atom;
3232 const char *cstr;
3233 char *cstr2;
3234 size_t len, len1;
3235
3236 str = JS_AtomToString(ctx, name);
3237 if (JS_IsException(str))
3238 return JS_ATOM_NULL;
3239 cstr = JS_ToCStringLen(ctx, &len, str);
3240 if (!cstr)
3241 goto fail;
3242 len1 = strlen(str1);
3243 cstr2 = js_malloc(ctx, len + len1 + 1);
3244 if (!cstr2)
3245 goto fail;
3246 memcpy(cstr2, cstr, len);
3247 memcpy(cstr2 + len, str1, len1);
3248 cstr2[len + len1] = '\0';
3249 atom = JS_NewAtomLen(ctx, cstr2, len + len1);
3250 js_free(ctx, cstr2);
3251 JS_FreeCString(ctx, cstr);
3252 JS_FreeValue(ctx, str);
3253 return atom;
3254 fail:
3255 JS_FreeCString(ctx, cstr);
3256 JS_FreeValue(ctx, str);
3257 return JS_ATOM_NULL;
3258}
3259
3260static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
3261{
3262 char buf[16];
3263 size_t len;
3264 len = u32toa(buf, n);
3265 buf[len] = '\0';
3266 return js_atom_concat_str(ctx, name, buf);
3267}
3268
3269static inline BOOL JS_IsEmptyString(JSValueConst v)
3270{
3271 return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
3272}
3273
3274/* JSClass support */
3275
3276#ifdef CONFIG_ATOMICS
3277static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER;
3278#endif
3279
3280/* a new class ID is allocated if *pclass_id != 0 */
3281JSClassID JS_NewClassID(JSClassID *pclass_id)
3282{
3283 JSClassID class_id;
3284#ifdef CONFIG_ATOMICS
3285 pthread_mutex_lock(&js_class_id_mutex);
3286#endif
3287 class_id = *pclass_id;
3288 if (class_id == 0) {
3289 class_id = js_class_id_alloc++;
3290 *pclass_id = class_id;
3291 }
3292#ifdef CONFIG_ATOMICS
3293 pthread_mutex_unlock(&js_class_id_mutex);
3294#endif
3295 return class_id;
3296}
3297
3298JSClassID JS_GetClassID(JSValue v)
3299{
3300 JSObject *p;
3301 if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
3302 return JS_INVALID_CLASS_ID;
3303 p = JS_VALUE_GET_OBJ(v);
3304 return p->class_id;
3305}
3306
3307BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
3308{
3309 return (class_id < rt->class_count &&
3310 rt->class_array[class_id].class_id != 0);
3311}
3312
3313/* create a new object internal class. Return -1 if error, 0 if
3314 OK. The finalizer can be NULL if none is needed. */
3315static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
3316 const JSClassDef *class_def, JSAtom name)
3317{
3318 int new_size, i;
3319 JSClass *cl, *new_class_array;
3320 struct list_head *el;
3321
3322 if (class_id >= (1 << 16))
3323 return -1;
3324 if (class_id < rt->class_count &&
3325 rt->class_array[class_id].class_id != 0)
3326 return -1;
3327
3328 if (class_id >= rt->class_count) {
3329 new_size = max_int(JS_CLASS_INIT_COUNT,
3330 max_int(class_id + 1, rt->class_count * 3 / 2));
3331
3332 /* reallocate the context class prototype array, if any */
3333 list_for_each(el, &rt->context_list) {
3334 JSContext *ctx = list_entry(el, JSContext, link);
3335 JSValue *new_tab;
3336 new_tab = js_realloc_rt(rt, ctx->class_proto,
3337 sizeof(ctx->class_proto[0]) * new_size);
3338 if (!new_tab)
3339 return -1;
3340 for(i = rt->class_count; i < new_size; i++)
3341 new_tab[i] = JS_NULL;
3342 ctx->class_proto = new_tab;
3343 }
3344 /* reallocate the class array */
3345 new_class_array = js_realloc_rt(rt, rt->class_array,
3346 sizeof(JSClass) * new_size);
3347 if (!new_class_array)
3348 return -1;
3349 memset(new_class_array + rt->class_count, 0,
3350 (new_size - rt->class_count) * sizeof(JSClass));
3351 rt->class_array = new_class_array;
3352 rt->class_count = new_size;
3353 }
3354 cl = &rt->class_array[class_id];
3355 cl->class_id = class_id;
3356 cl->class_name = JS_DupAtomRT(rt, name);
3357 cl->finalizer = class_def->finalizer;
3358 cl->gc_mark = class_def->gc_mark;
3359 cl->call = class_def->call;
3360 cl->exotic = class_def->exotic;
3361 return 0;
3362}
3363
3364int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
3365{
3366 int ret, len;
3367 JSAtom name;
3368
3369 len = strlen(class_def->class_name);
3370 name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3371 if (name == JS_ATOM_NULL) {
3372 name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
3373 if (name == JS_ATOM_NULL)
3374 return -1;
3375 }
3376 ret = JS_NewClass1(rt, class_id, class_def, name);
3377 JS_FreeAtomRT(rt, name);
3378 return ret;
3379}
3380
3381static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len)
3382{
3383 JSString *str;
3384
3385 if (len <= 0) {
3386 return JS_AtomToString(ctx, JS_ATOM_empty_string);
3387 }
3388 str = js_alloc_string(ctx, len, 0);
3389 if (!str)
3390 return JS_EXCEPTION;
3391 memcpy(str->u.str8, buf, len);
3392 str->u.str8[len] = '\0';
3393 return JS_MKPTR(JS_TAG_STRING, str);
3394}
3395
3396static JSValue js_new_string8(JSContext *ctx, const char *buf)
3397{
3398 return js_new_string8_len(ctx, buf, strlen(buf));
3399}
3400
3401static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len)
3402{
3403 JSString *str;
3404 str = js_alloc_string(ctx, len, 1);
3405 if (!str)
3406 return JS_EXCEPTION;
3407 memcpy(str->u.str16, buf, len * 2);
3408 return JS_MKPTR(JS_TAG_STRING, str);
3409}
3410
3411static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
3412{
3413 if (c < 0x100) {
3414 uint8_t ch8 = c;
3415 return js_new_string8_len(ctx, (const char *)&ch8, 1);
3416 } else {
3417 uint16_t ch16 = c;
3418 return js_new_string16_len(ctx, &ch16, 1);
3419 }
3420}
3421
3422static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
3423{
3424 int len = end - start;
3425 if (start == 0 && end == p->len) {
3426 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
3427 }
3428 if (p->is_wide_char && len > 0) {
3429 JSString *str;
3430 int i;
3431 uint16_t c = 0;
3432 for (i = start; i < end; i++) {
3433 c |= p->u.str16[i];
3434 }
3435 if (c > 0xFF)
3436 return js_new_string16_len(ctx, p->u.str16 + start, len);
3437
3438 str = js_alloc_string(ctx, len, 0);
3439 if (!str)
3440 return JS_EXCEPTION;
3441 for (i = 0; i < len; i++) {
3442 str->u.str8[i] = p->u.str16[start + i];
3443 }
3444 str->u.str8[len] = '\0';
3445 return JS_MKPTR(JS_TAG_STRING, str);
3446 } else {
3447 return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len);
3448 }
3449}
3450
3451typedef struct StringBuffer {
3452 JSContext *ctx;
3453 JSString *str;
3454 int len;
3455 int size;
3456 int is_wide_char;
3457 int error_status;
3458} StringBuffer;
3459
3460/* It is valid to call string_buffer_end() and all string_buffer functions even
3461 if string_buffer_init() or another string_buffer function returns an error.
3462 If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
3463 */
3464static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
3465 int is_wide)
3466{
3467 s->ctx = ctx;
3468 s->size = size;
3469 s->len = 0;
3470 s->is_wide_char = is_wide;
3471 s->error_status = 0;
3472 s->str = js_alloc_string(ctx, size, is_wide);
3473 if (unlikely(!s->str)) {
3474 s->size = 0;
3475 return s->error_status = -1;
3476 }
3477#ifdef DUMP_LEAKS
3478 /* the StringBuffer may reallocate the JSString, only link it at the end */
3479 list_del(&s->str->link);
3480#endif
3481 return 0;
3482}
3483
3484static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
3485{
3486 return string_buffer_init2(ctx, s, size, 0);
3487}
3488
3489static void string_buffer_free(StringBuffer *s)
3490{
3491 js_free(s->ctx, s->str);
3492 s->str = NULL;
3493}
3494
3495static int string_buffer_set_error(StringBuffer *s)
3496{
3497 js_free(s->ctx, s->str);
3498 s->str = NULL;
3499 s->size = 0;
3500 s->len = 0;
3501 return s->error_status = -1;
3502}
3503
3504static no_inline int string_buffer_widen(StringBuffer *s, int size)
3505{
3506 JSString *str;
3507 size_t slack;
3508 int i;
3509
3510 if (s->error_status)
3511 return -1;
3512
3513 str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
3514 if (!str)
3515 return string_buffer_set_error(s);
3516 size += slack >> 1;
3517 for(i = s->len; i-- > 0;) {
3518 str->u.str16[i] = str->u.str8[i];
3519 }
3520 s->is_wide_char = 1;
3521 s->size = size;
3522 s->str = str;
3523 return 0;
3524}
3525
3526static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
3527{
3528 JSString *new_str;
3529 int new_size;
3530 size_t new_size_bytes, slack;
3531
3532 if (s->error_status)
3533 return -1;
3534
3535 if (new_len > JS_STRING_LEN_MAX) {
3536 JS_ThrowInternalError(s->ctx, "string too long");
3537 return string_buffer_set_error(s);
3538 }
3539 new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
3540 if (!s->is_wide_char && c >= 0x100) {
3541 return string_buffer_widen(s, new_size);
3542 }
3543 new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
3544 new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
3545 if (!new_str)
3546 return string_buffer_set_error(s);
3547 new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
3548 s->size = new_size;
3549 s->str = new_str;
3550 return 0;
3551}
3552
3553static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
3554{
3555 if (unlikely(s->len >= s->size)) {
3556 if (string_buffer_realloc(s, s->len + 1, c))
3557 return -1;
3558 }
3559 if (s->is_wide_char) {
3560 s->str->u.str16[s->len++] = c;
3561 } else if (c < 0x100) {
3562 s->str->u.str8[s->len++] = c;
3563 } else {
3564 if (string_buffer_widen(s, s->size))
3565 return -1;
3566 s->str->u.str16[s->len++] = c;
3567 }
3568 return 0;
3569}
3570
3571/* 0 <= c <= 0xff */
3572static int string_buffer_putc8(StringBuffer *s, uint32_t c)
3573{
3574 if (unlikely(s->len >= s->size)) {
3575 if (string_buffer_realloc(s, s->len + 1, c))
3576 return -1;
3577 }
3578 if (s->is_wide_char) {
3579 s->str->u.str16[s->len++] = c;
3580 } else {
3581 s->str->u.str8[s->len++] = c;
3582 }
3583 return 0;
3584}
3585
3586/* 0 <= c <= 0xffff */
3587static int string_buffer_putc16(StringBuffer *s, uint32_t c)
3588{
3589 if (likely(s->len < s->size)) {
3590 if (s->is_wide_char) {
3591 s->str->u.str16[s->len++] = c;
3592 return 0;
3593 } else if (c < 0x100) {
3594 s->str->u.str8[s->len++] = c;
3595 return 0;
3596 }
3597 }
3598 return string_buffer_putc_slow(s, c);
3599}
3600
3601/* 0 <= c <= 0x10ffff */
3602static int string_buffer_putc(StringBuffer *s, uint32_t c)
3603{
3604 if (unlikely(c >= 0x10000)) {
3605 /* surrogate pair */
3606 if (string_buffer_putc16(s, get_hi_surrogate(c)))
3607 return -1;
3608 c = get_lo_surrogate(c);
3609 }
3610 return string_buffer_putc16(s, c);
3611}
3612
3613static int string_getc(const JSString *p, int *pidx)
3614{
3615 int idx, c, c1;
3616 idx = *pidx;
3617 if (p->is_wide_char) {
3618 c = p->u.str16[idx++];
3619 if (is_hi_surrogate(c) && idx < p->len) {
3620 c1 = p->u.str16[idx];
3621 if (is_lo_surrogate(c1)) {
3622 c = from_surrogate(c, c1);
3623 idx++;
3624 }
3625 }
3626 } else {
3627 c = p->u.str8[idx++];
3628 }
3629 *pidx = idx;
3630 return c;
3631}
3632
3633static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
3634{
3635 int i;
3636
3637 if (s->len + len > s->size) {
3638 if (string_buffer_realloc(s, s->len + len, 0))
3639 return -1;
3640 }
3641 if (s->is_wide_char) {
3642 for (i = 0; i < len; i++) {
3643 s->str->u.str16[s->len + i] = p[i];
3644 }
3645 s->len += len;
3646 } else {
3647 memcpy(&s->str->u.str8[s->len], p, len);
3648 s->len += len;
3649 }
3650 return 0;
3651}
3652
3653static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
3654{
3655 int c = 0, i;
3656
3657 for (i = 0; i < len; i++) {
3658 c |= p[i];
3659 }
3660 if (s->len + len > s->size) {
3661 if (string_buffer_realloc(s, s->len + len, c))
3662 return -1;
3663 } else if (!s->is_wide_char && c >= 0x100) {
3664 if (string_buffer_widen(s, s->size))
3665 return -1;
3666 }
3667 if (s->is_wide_char) {
3668 memcpy(&s->str->u.str16[s->len], p, len << 1);
3669 s->len += len;
3670 } else {
3671 for (i = 0; i < len; i++) {
3672 s->str->u.str8[s->len + i] = p[i];
3673 }
3674 s->len += len;
3675 }
3676 return 0;
3677}
3678
3679/* appending an ASCII string */
3680static int string_buffer_puts8(StringBuffer *s, const char *str)
3681{
3682 return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
3683}
3684
3685static int string_buffer_concat(StringBuffer *s, const JSString *p,
3686 uint32_t from, uint32_t to)
3687{
3688 if (to <= from)
3689 return 0;
3690 if (p->is_wide_char)
3691 return string_buffer_write16(s, p->u.str16 + from, to - from);
3692 else
3693 return string_buffer_write8(s, p->u.str8 + from, to - from);
3694}
3695
3696static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
3697{
3698 JSString *p;
3699 JSValue v1;
3700 int res;
3701
3702 if (s->error_status) {
3703 /* prevent exception overload */
3704 return -1;
3705 }
3706 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3707 if (JS_VALUE_GET_TAG(v) == JS_TAG_STRING_ROPE) {
3708 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(v);
3709 /* recursion is acceptable because the rope depth is bounded */
3710 if (string_buffer_concat_value(s, r->left))
3711 return -1;
3712 return string_buffer_concat_value(s, r->right);
3713 } else {
3714 v1 = JS_ToString(s->ctx, v);
3715 if (JS_IsException(v1))
3716 return string_buffer_set_error(s);
3717 p = JS_VALUE_GET_STRING(v1);
3718 res = string_buffer_concat(s, p, 0, p->len);
3719 JS_FreeValue(s->ctx, v1);
3720 return res;
3721 }
3722 }
3723 p = JS_VALUE_GET_STRING(v);
3724 return string_buffer_concat(s, p, 0, p->len);
3725}
3726
3727static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
3728{
3729 JSString *p;
3730 int res;
3731
3732 if (s->error_status) {
3733 /* prevent exception overload */
3734 JS_FreeValue(s->ctx, v);
3735 return -1;
3736 }
3737 if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
3738 v = JS_ToStringFree(s->ctx, v);
3739 if (JS_IsException(v))
3740 return string_buffer_set_error(s);
3741 }
3742 p = JS_VALUE_GET_STRING(v);
3743 res = string_buffer_concat(s, p, 0, p->len);
3744 JS_FreeValue(s->ctx, v);
3745 return res;
3746}
3747
3748static int string_buffer_fill(StringBuffer *s, int c, int count)
3749{
3750 /* XXX: optimize */
3751 if (s->len + count > s->size) {
3752 if (string_buffer_realloc(s, s->len + count, c))
3753 return -1;
3754 }
3755 while (count-- > 0) {
3756 if (string_buffer_putc16(s, c))
3757 return -1;
3758 }
3759 return 0;
3760}
3761
3762static JSValue string_buffer_end(StringBuffer *s)
3763{
3764 JSString *str;
3765 str = s->str;
3766 if (s->error_status)
3767 return JS_EXCEPTION;
3768 if (s->len == 0) {
3769 js_free(s->ctx, str);
3770 s->str = NULL;
3771 return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
3772 }
3773 if (s->len < s->size) {
3774 /* smaller size so js_realloc should not fail, but OK if it does */
3775 /* XXX: should add some slack to avoid unnecessary calls */
3776 /* XXX: might need to use malloc+free to ensure smaller size */
3777 str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
3778 (s->len << s->is_wide_char) + 1 - s->is_wide_char);
3779 if (str == NULL)
3780 str = s->str;
3781 s->str = str;
3782 }
3783 if (!s->is_wide_char)
3784 str->u.str8[s->len] = 0;
3785#ifdef DUMP_LEAKS
3786 list_add_tail(&str->link, &s->ctx->rt->string_list);
3787#endif
3788 str->is_wide_char = s->is_wide_char;
3789 str->len = s->len;
3790 s->str = NULL;
3791 return JS_MKPTR(JS_TAG_STRING, str);
3792}
3793
3794/* create a string from a UTF-8 buffer */
3795JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
3796{
3797 const uint8_t *p, *p_end, *p_start, *p_next;
3798 uint32_t c;
3799 StringBuffer b_s, *b = &b_s;
3800 size_t len1;
3801
3802 p_start = (const uint8_t *)buf;
3803 p_end = p_start + buf_len;
3804 len1 = count_ascii(p_start, buf_len);
3805 p = p_start + len1;
3806 if (len1 > JS_STRING_LEN_MAX)
3807 return JS_ThrowInternalError(ctx, "string too long");
3808 if (p == p_end) {
3809 /* ASCII string */
3810 return js_new_string8_len(ctx, buf, buf_len);
3811 } else {
3812 if (string_buffer_init(ctx, b, buf_len))
3813 goto fail;
3814 string_buffer_write8(b, p_start, len1);
3815 while (p < p_end) {
3816 if (*p < 128) {
3817 string_buffer_putc8(b, *p++);
3818 } else {
3819 /* parse utf-8 sequence, return 0xFFFFFFFF for error */
3820 c = unicode_from_utf8(p, p_end - p, &p_next);
3821 if (c < 0x10000) {
3822 p = p_next;
3823 } else if (c <= 0x10FFFF) {
3824 p = p_next;
3825 /* surrogate pair */
3826 string_buffer_putc16(b, get_hi_surrogate(c));
3827 c = get_lo_surrogate(c);
3828 } else {
3829 /* invalid char */
3830 c = 0xfffd;
3831 /* skip the invalid chars */
3832 /* XXX: seems incorrect. Why not just use c = *p++; ? */
3833 while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3834 p++;
3835 if (p < p_end) {
3836 p++;
3837 while (p < p_end && (*p >= 0x80 && *p < 0xc0))
3838 p++;
3839 }
3840 }
3841 string_buffer_putc16(b, c);
3842 }
3843 }
3844 }
3845 return string_buffer_end(b);
3846
3847 fail:
3848 string_buffer_free(b);
3849 return JS_EXCEPTION;
3850}
3851
3852static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
3853 JSValue str2, const char *str3)
3854{
3855 StringBuffer b_s, *b = &b_s;
3856 int len1, len3;
3857 JSString *p;
3858
3859 if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
3860 str2 = JS_ToStringFree(ctx, str2);
3861 if (JS_IsException(str2))
3862 goto fail;
3863 }
3864 p = JS_VALUE_GET_STRING(str2);
3865 len1 = strlen(str1);
3866 len3 = strlen(str3);
3867
3868 if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
3869 goto fail;
3870
3871 string_buffer_write8(b, (const uint8_t *)str1, len1);
3872 string_buffer_concat(b, p, 0, p->len);
3873 string_buffer_write8(b, (const uint8_t *)str3, len3);
3874
3875 JS_FreeValue(ctx, str2);
3876 return string_buffer_end(b);
3877
3878 fail:
3879 JS_FreeValue(ctx, str2);
3880 return JS_EXCEPTION;
3881}
3882
3883JSValue JS_NewAtomString(JSContext *ctx, const char *str)
3884{
3885 JSAtom atom = JS_NewAtom(ctx, str);
3886 if (atom == JS_ATOM_NULL)
3887 return JS_EXCEPTION;
3888 JSValue val = JS_AtomToString(ctx, atom);
3889 JS_FreeAtom(ctx, atom);
3890 return val;
3891}
3892
3893/* return (NULL, 0) if exception. */
3894/* return pointer into a JSString with a live ref_count */
3895/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
3896const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
3897{
3898 JSValue val;
3899 JSString *str, *str_new;
3900 int pos, len, c, c1;
3901 uint8_t *q;
3902
3903 if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
3904 val = JS_ToString(ctx, val1);
3905 if (JS_IsException(val))
3906 goto fail;
3907 } else {
3908 val = JS_DupValue(ctx, val1);
3909 }
3910
3911 str = JS_VALUE_GET_STRING(val);
3912 len = str->len;
3913 if (!str->is_wide_char) {
3914 const uint8_t *src = str->u.str8;
3915 int count;
3916
3917 /* count the number of non-ASCII characters */
3918 /* Scanning the whole string is required for ASCII strings,
3919 and computing the number of non-ASCII bytes is less expensive
3920 than testing each byte, hence this method is faster for ASCII
3921 strings, which is the most common case.
3922 */
3923 count = 0;
3924 for (pos = 0; pos < len; pos++) {
3925 count += src[pos] >> 7;
3926 }
3927 if (count == 0) {
3928 if (plen)
3929 *plen = len;
3930 return (const char *)src;
3931 }
3932 str_new = js_alloc_string(ctx, len + count, 0);
3933 if (!str_new)
3934 goto fail;
3935 q = str_new->u.str8;
3936 for (pos = 0; pos < len; pos++) {
3937 c = src[pos];
3938 if (c < 0x80) {
3939 *q++ = c;
3940 } else {
3941 *q++ = (c >> 6) | 0xc0;
3942 *q++ = (c & 0x3f) | 0x80;
3943 }
3944 }
3945 } else {
3946 const uint16_t *src = str->u.str16;
3947 /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
3948 produce 4 bytes but use 2 code points.
3949 */
3950 str_new = js_alloc_string(ctx, len * 3, 0);
3951 if (!str_new)
3952 goto fail;
3953 q = str_new->u.str8;
3954 pos = 0;
3955 while (pos < len) {
3956 c = src[pos++];
3957 if (c < 0x80) {
3958 *q++ = c;
3959 } else {
3960 if (is_hi_surrogate(c)) {
3961 if (pos < len && !cesu8) {
3962 c1 = src[pos];
3963 if (is_lo_surrogate(c1)) {
3964 pos++;
3965 c = from_surrogate(c, c1);
3966 } else {
3967 /* Keep unmatched surrogate code points */
3968 /* c = 0xfffd; */ /* error */
3969 }
3970 } else {
3971 /* Keep unmatched surrogate code points */
3972 /* c = 0xfffd; */ /* error */
3973 }
3974 }
3975 q += unicode_to_utf8(q, c);
3976 }
3977 }
3978 }
3979
3980 *q = '\0';
3981 str_new->len = q - str_new->u.str8;
3982 JS_FreeValue(ctx, val);
3983 if (plen)
3984 *plen = str_new->len;
3985 return (const char *)str_new->u.str8;
3986 fail:
3987 if (plen)
3988 *plen = 0;
3989 return NULL;
3990}
3991
3992void JS_FreeCString(JSContext *ctx, const char *ptr)
3993{
3994 JSString *p;
3995 if (!ptr)
3996 return;
3997 /* purposely removing constness */
3998 p = container_of(ptr, JSString, u);
3999 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
4000}
4001
4002static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
4003{
4004 int c, i;
4005 for(i = 0; i < len; i++) {
4006 c = src1[i] - src2[i];
4007 if (c != 0)
4008 return c;
4009 }
4010 return 0;
4011}
4012
4013static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
4014{
4015 int c, i;
4016 for(i = 0; i < len; i++) {
4017 c = src1[i] - src2[i];
4018 if (c != 0)
4019 return c;
4020 }
4021 return 0;
4022}
4023
4024static int js_string_memcmp(const JSString *p1, int pos1, const JSString *p2,
4025 int pos2, int len)
4026{
4027 int res;
4028
4029 if (likely(!p1->is_wide_char)) {
4030 if (likely(!p2->is_wide_char))
4031 res = memcmp(p1->u.str8 + pos1, p2->u.str8 + pos2, len);
4032 else
4033 res = -memcmp16_8(p2->u.str16 + pos2, p1->u.str8 + pos1, len);
4034 } else {
4035 if (!p2->is_wide_char)
4036 res = memcmp16_8(p1->u.str16 + pos1, p2->u.str8 + pos2, len);
4037 else
4038 res = memcmp16(p1->u.str16 + pos1, p2->u.str16 + pos2, len);
4039 }
4040 return res;
4041}
4042
4043/* return < 0, 0 or > 0 */
4044static int js_string_compare(JSContext *ctx,
4045 const JSString *p1, const JSString *p2)
4046{
4047 int res, len;
4048 len = min_int(p1->len, p2->len);
4049 res = js_string_memcmp(p1, 0, p2, 0, len);
4050 if (res == 0) {
4051 if (p1->len == p2->len)
4052 res = 0;
4053 else if (p1->len < p2->len)
4054 res = -1;
4055 else
4056 res = 1;
4057 }
4058 return res;
4059}
4060
4061static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
4062{
4063 if (p->is_wide_char) {
4064 memcpy(dst, p->u.str16 + offset, len * 2);
4065 } else {
4066 const uint8_t *src1 = p->u.str8 + offset;
4067 int i;
4068
4069 for(i = 0; i < len; i++)
4070 dst[i] = src1[i];
4071 }
4072}
4073
4074static JSValue JS_ConcatString1(JSContext *ctx,
4075 const JSString *p1, const JSString *p2)
4076{
4077 JSString *p;
4078 uint32_t len;
4079 int is_wide_char;
4080
4081 len = p1->len + p2->len;
4082 if (len > JS_STRING_LEN_MAX)
4083 return JS_ThrowInternalError(ctx, "string too long");
4084 is_wide_char = p1->is_wide_char | p2->is_wide_char;
4085 p = js_alloc_string(ctx, len, is_wide_char);
4086 if (!p)
4087 return JS_EXCEPTION;
4088 if (!is_wide_char) {
4089 memcpy(p->u.str8, p1->u.str8, p1->len);
4090 memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
4091 p->u.str8[len] = '\0';
4092 } else {
4093 copy_str16(p->u.str16, p1, 0, p1->len);
4094 copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
4095 }
4096 return JS_MKPTR(JS_TAG_STRING, p);
4097}
4098
4099static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) {
4100 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
4101 JSString *p2 = JS_VALUE_GET_STRING(op2);
4102 size_t size1;
4103
4104 if (p2->len == 0)
4105 return TRUE;
4106 if (p1->header.ref_count != 1)
4107 return FALSE;
4108 size1 = js_malloc_usable_size(ctx, p1);
4109 if (p1->is_wide_char) {
4110 if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) {
4111 if (p2->is_wide_char) {
4112 memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
4113 p1->len += p2->len;
4114 return TRUE;
4115 } else {
4116 size_t i;
4117 for (i = 0; i < p2->len; i++) {
4118 p1->u.str16[p1->len++] = p2->u.str8[i];
4119 }
4120 return TRUE;
4121 }
4122 }
4123 } else if (!p2->is_wide_char) {
4124 if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) {
4125 memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
4126 p1->len += p2->len;
4127 p1->u.str8[p1->len] = '\0';
4128 return TRUE;
4129 }
4130 }
4131 }
4132 return FALSE;
4133}
4134
4135static JSValue JS_ConcatString2(JSContext *ctx, JSValue op1, JSValue op2)
4136{
4137 JSValue ret;
4138 JSString *p1, *p2;
4139 p1 = JS_VALUE_GET_STRING(op1);
4140 if (JS_ConcatStringInPlace(ctx, p1, op2)) {
4141 JS_FreeValue(ctx, op2);
4142 return op1;
4143 }
4144 p2 = JS_VALUE_GET_STRING(op2);
4145 ret = JS_ConcatString1(ctx, p1, p2);
4146 JS_FreeValue(ctx, op1);
4147 JS_FreeValue(ctx, op2);
4148 return ret;
4149}
4150
4151/* Return the character at position 'idx'. 'val' must be a string or rope */
4152static int string_rope_get(JSValueConst val, uint32_t idx)
4153{
4154 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
4155 return string_get(JS_VALUE_GET_STRING(val), idx);
4156 } else {
4157 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
4158 uint32_t len;
4159 if (JS_VALUE_GET_TAG(r->left) == JS_TAG_STRING)
4160 len = JS_VALUE_GET_STRING(r->left)->len;
4161 else
4162 len = JS_VALUE_GET_STRING_ROPE(r->left)->len;
4163 if (idx < len)
4164 return string_rope_get(r->left, idx);
4165 else
4166 return string_rope_get(r->right, idx - len);
4167 }
4168}
4169
4170typedef struct {
4171 JSValueConst stack[JS_STRING_ROPE_MAX_DEPTH];
4172 int stack_len;
4173} JSStringRopeIter;
4174
4175static void string_rope_iter_init(JSStringRopeIter *s, JSValueConst val)
4176{
4177 s->stack_len = 0;
4178 s->stack[s->stack_len++] = val;
4179}
4180
4181/* iterate thru a rope and return the strings in order */
4182static JSString *string_rope_iter_next(JSStringRopeIter *s)
4183{
4184 JSValueConst val;
4185 JSStringRope *r;
4186
4187 if (s->stack_len == 0)
4188 return NULL;
4189 val = s->stack[--s->stack_len];
4190 for(;;) {
4191 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
4192 return JS_VALUE_GET_STRING(val);
4193 r = JS_VALUE_GET_STRING_ROPE(val);
4194 assert(s->stack_len < JS_STRING_ROPE_MAX_DEPTH);
4195 s->stack[s->stack_len++] = r->right;
4196 val = r->left;
4197 }
4198}
4199
4200static uint32_t string_rope_get_len(JSValueConst val)
4201{
4202 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING)
4203 return JS_VALUE_GET_STRING(val)->len;
4204 else
4205 return JS_VALUE_GET_STRING_ROPE(val)->len;
4206}
4207
4208static int js_string_rope_compare(JSContext *ctx, JSValueConst op1,
4209 JSValueConst op2, BOOL eq_only)
4210{
4211 uint32_t len1, len2, len, pos1, pos2, l;
4212 int res;
4213 JSStringRopeIter it1, it2;
4214 JSString *p1, *p2;
4215
4216 len1 = string_rope_get_len(op1);
4217 len2 = string_rope_get_len(op2);
4218 /* no need to go further for equality test if
4219 different length */
4220 if (eq_only && len1 != len2)
4221 return 1;
4222 len = min_uint32(len1, len2);
4223 string_rope_iter_init(&it1, op1);
4224 string_rope_iter_init(&it2, op2);
4225 p1 = string_rope_iter_next(&it1);
4226 p2 = string_rope_iter_next(&it2);
4227 pos1 = 0;
4228 pos2 = 0;
4229 while (len != 0) {
4230 l = min_uint32(p1->len - pos1, p2->len - pos2);
4231 l = min_uint32(l, len);
4232 res = js_string_memcmp(p1, pos1, p2, pos2, l);
4233 if (res != 0)
4234 return res;
4235 len -= l;
4236 pos1 += l;
4237 if (pos1 >= p1->len) {
4238 p1 = string_rope_iter_next(&it1);
4239 pos1 = 0;
4240 }
4241 pos2 += l;
4242 if (pos2 >= p2->len) {
4243 p2 = string_rope_iter_next(&it2);
4244 pos2 = 0;
4245 }
4246 }
4247
4248 if (len1 == len2)
4249 res = 0;
4250 else if (len1 < len2)
4251 res = -1;
4252 else
4253 res = 1;
4254 return res;
4255}
4256
4257/* 'rope' must be a rope. return a string and modify the rope so that
4258 it won't need to be linearized again. */
4259static JSValue js_linearize_string_rope(JSContext *ctx, JSValue rope)
4260{
4261 StringBuffer b_s, *b = &b_s;
4262 JSStringRope *r;
4263 JSValue ret;
4264
4265 r = JS_VALUE_GET_STRING_ROPE(rope);
4266
4267 /* check whether it is already linearized */
4268 if (JS_VALUE_GET_TAG(r->right) == JS_TAG_STRING &&
4269 JS_VALUE_GET_STRING(r->right)->len == 0) {
4270 ret = JS_DupValue(ctx, r->left);
4271 JS_FreeValue(ctx, rope);
4272 return ret;
4273 }
4274 if (string_buffer_init2(ctx, b, r->len, r->is_wide_char))
4275 goto fail;
4276 if (string_buffer_concat_value(b, rope))
4277 goto fail;
4278 ret = string_buffer_end(b);
4279 if (r->header.ref_count > 1) {
4280 /* update the rope so that it won't need to be linearized again */
4281 JS_FreeValue(ctx, r->left);
4282 JS_FreeValue(ctx, r->right);
4283 r->left = JS_DupValue(ctx, ret);
4284 r->right = JS_AtomToString(ctx, JS_ATOM_empty_string);
4285 }
4286 JS_FreeValue(ctx, rope);
4287 return ret;
4288 fail:
4289 JS_FreeValue(ctx, rope);
4290 return JS_EXCEPTION;
4291}
4292
4293static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope);
4294
4295/* op1 and op2 must be strings or string ropes */
4296static JSValue js_new_string_rope(JSContext *ctx, JSValue op1, JSValue op2)
4297{
4298 uint32_t len;
4299 int is_wide_char, depth;
4300 JSStringRope *r;
4301 JSValue res;
4302
4303 if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
4304 JSString *p1 = JS_VALUE_GET_STRING(op1);
4305 len = p1->len;
4306 is_wide_char = p1->is_wide_char;
4307 depth = 0;
4308 } else {
4309 JSStringRope *r1 = JS_VALUE_GET_STRING_ROPE(op1);
4310 len = r1->len;
4311 is_wide_char = r1->is_wide_char;
4312 depth = r1->depth;
4313 }
4314
4315 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
4316 JSString *p2 = JS_VALUE_GET_STRING(op2);
4317 len += p2->len;
4318 is_wide_char |= p2->is_wide_char;
4319 } else {
4320 JSStringRope *r2 = JS_VALUE_GET_STRING_ROPE(op2);
4321 len += r2->len;
4322 is_wide_char |= r2->is_wide_char;
4323 depth = max_int(depth, r2->depth);
4324 }
4325 if (len > JS_STRING_LEN_MAX) {
4326 JS_ThrowInternalError(ctx, "string too long");
4327 goto fail;
4328 }
4329 r = js_malloc(ctx, sizeof(*r));
4330 if (!r)
4331 goto fail;
4332 r->header.ref_count = 1;
4333 r->len = len;
4334 r->is_wide_char = is_wide_char;
4335 r->depth = depth + 1;
4336 r->left = op1;
4337 r->right = op2;
4338 res = JS_MKPTR(JS_TAG_STRING_ROPE, r);
4339 if (r->depth > JS_STRING_ROPE_MAX_DEPTH) {
4340 JSValue res2;
4341#ifdef DUMP_ROPE_REBALANCE
4342 printf("rebalance: initial depth=%d\n", r->depth);
4343#endif
4344 res2 = js_rebalancee_string_rope(ctx, res);
4345#ifdef DUMP_ROPE_REBALANCE
4346 if (JS_VALUE_GET_TAG(res2) == JS_TAG_STRING_ROPE)
4347 printf("rebalance: final depth=%d\n", JS_VALUE_GET_STRING_ROPE(res2)->depth);
4348#endif
4349 JS_FreeValue(ctx, res);
4350 return res2;
4351 } else {
4352 return res;
4353 }
4354 fail:
4355 JS_FreeValue(ctx, op1);
4356 JS_FreeValue(ctx, op2);
4357 return JS_EXCEPTION;
4358}
4359
4360#define ROPE_N_BUCKETS 44
4361
4362/* Fibonacii numbers starting from F_2 */
4363static const uint32_t rope_bucket_len[ROPE_N_BUCKETS] = {
4364 1, 2, 3, 5,
4365 8, 13, 21, 34,
4366 55, 89, 144, 233,
4367 377, 610, 987, 1597,
4368 2584, 4181, 6765, 10946,
4369 17711, 28657, 46368, 75025,
4370 121393, 196418, 317811, 514229,
4371 832040, 1346269, 2178309, 3524578,
4372 5702887, 9227465, 14930352, 24157817,
4373 39088169, 63245986, 102334155, 165580141,
4374 267914296, 433494437, 701408733, 1134903170, /* > JS_STRING_LEN_MAX */
4375};
4376
4377static int js_rebalancee_string_rope_rec(JSContext *ctx, JSValue *buckets,
4378 JSValueConst val)
4379{
4380 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
4381 JSString *p = JS_VALUE_GET_STRING(val);
4382 uint32_t len, i;
4383 JSValue a, b;
4384
4385 len = p->len;
4386 if (len == 0)
4387 return 0; /* nothing to do */
4388 /* find the bucket i so that rope_bucket_len[i] <= len <
4389 rope_bucket_len[i + 1] and concatenate the ropes in the
4390 buckets before */
4391 a = JS_NULL;
4392 i = 0;
4393 while (len >= rope_bucket_len[i + 1]) {
4394 b = buckets[i];
4395 if (!JS_IsNull(b)) {
4396 buckets[i] = JS_NULL;
4397 if (JS_IsNull(a)) {
4398 a = b;
4399 } else {
4400 a = js_new_string_rope(ctx, b, a);
4401 if (JS_IsException(a))
4402 return -1;
4403 }
4404 }
4405 i++;
4406 }
4407 if (!JS_IsNull(a)) {
4408 a = js_new_string_rope(ctx, a, JS_DupValue(ctx, val));
4409 if (JS_IsException(a))
4410 return -1;
4411 } else {
4412 a = JS_DupValue(ctx, val);
4413 }
4414 while (!JS_IsNull(buckets[i])) {
4415 a = js_new_string_rope(ctx, buckets[i], a);
4416 buckets[i] = JS_NULL;
4417 if (JS_IsException(a))
4418 return -1;
4419 i++;
4420 }
4421 buckets[i] = a;
4422 } else {
4423 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
4424 js_rebalancee_string_rope_rec(ctx, buckets, r->left);
4425 js_rebalancee_string_rope_rec(ctx, buckets, r->right);
4426 }
4427 return 0;
4428}
4429
4430/* Return a new rope which is balanced. Algorithm from "Ropes: an
4431 Alternative to Strings", Hans-J. Boehm, Russ Atkinson and Michael
4432 Plass. */
4433static JSValue js_rebalancee_string_rope(JSContext *ctx, JSValueConst rope)
4434{
4435 JSValue buckets[ROPE_N_BUCKETS], a, b;
4436 int i;
4437
4438 for(i = 0; i < ROPE_N_BUCKETS; i++)
4439 buckets[i] = JS_NULL;
4440 if (js_rebalancee_string_rope_rec(ctx, buckets, rope))
4441 goto fail;
4442 a = JS_NULL;
4443 for(i = 0; i < ROPE_N_BUCKETS; i++) {
4444 b = buckets[i];
4445 if (!JS_IsNull(b)) {
4446 buckets[i] = JS_NULL;
4447 if (JS_IsNull(a)) {
4448 a = b;
4449 } else {
4450 a = js_new_string_rope(ctx, b, a);
4451 if (JS_IsException(a))
4452 goto fail;
4453 }
4454 }
4455 }
4456 /* fail safe */
4457 if (JS_IsNull(a))
4458 return JS_AtomToString(ctx, JS_ATOM_empty_string);
4459 else
4460 return a;
4461 fail:
4462 for(i = 0; i < ROPE_N_BUCKETS; i++) {
4463 JS_FreeValue(ctx, buckets[i]);
4464 }
4465 return JS_EXCEPTION;
4466}
4467
4468/* op1 and op2 are converted to strings. For convenience, op1 or op2 =
4469 JS_EXCEPTION are accepted and return JS_EXCEPTION. */
4470static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
4471{
4472 JSString *p1, *p2;
4473
4474 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING &&
4475 JS_VALUE_GET_TAG(op1) != JS_TAG_STRING_ROPE)) {
4476 op1 = JS_ToStringFree(ctx, op1);
4477 if (JS_IsException(op1)) {
4478 JS_FreeValue(ctx, op2);
4479 return JS_EXCEPTION;
4480 }
4481 }
4482 if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING &&
4483 JS_VALUE_GET_TAG(op2) != JS_TAG_STRING_ROPE)) {
4484 op2 = JS_ToStringFree(ctx, op2);
4485 if (JS_IsException(op2)) {
4486 JS_FreeValue(ctx, op1);
4487 return JS_EXCEPTION;
4488 }
4489 }
4490
4491 /* normal concatenation for short strings */
4492 if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
4493 p2 = JS_VALUE_GET_STRING(op2);
4494 if (p2->len == 0) {
4495 JS_FreeValue(ctx, op2);
4496 return op1;
4497 }
4498 if (p2->len <= JS_STRING_ROPE_SHORT_LEN) {
4499 if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
4500 p1 = JS_VALUE_GET_STRING(op1);
4501 if (p1->len <= JS_STRING_ROPE_SHORT2_LEN) {
4502 return JS_ConcatString2(ctx, op1, op2);
4503 } else {
4504 return js_new_string_rope(ctx, op1, op2);
4505 }
4506 } else {
4507 JSStringRope *r1;
4508 r1 = JS_VALUE_GET_STRING_ROPE(op1);
4509 if (JS_VALUE_GET_TAG(r1->right) == JS_TAG_STRING &&
4510 JS_VALUE_GET_STRING(r1->right)->len <= JS_STRING_ROPE_SHORT_LEN) {
4511 JSValue val, ret;
4512 val = JS_ConcatString2(ctx, JS_DupValue(ctx, r1->right), op2);
4513 if (JS_IsException(val)) {
4514 JS_FreeValue(ctx, op1);
4515 return JS_EXCEPTION;
4516 }
4517 ret = js_new_string_rope(ctx, JS_DupValue(ctx, r1->left), val);
4518 JS_FreeValue(ctx, op1);
4519 return ret;
4520 }
4521 }
4522 }
4523 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING) {
4524 JSStringRope *r2;
4525 p1 = JS_VALUE_GET_STRING(op1);
4526 if (p1->len == 0) {
4527 JS_FreeValue(ctx, op1);
4528 return op2;
4529 }
4530 r2 = JS_VALUE_GET_STRING_ROPE(op2);
4531 if (JS_VALUE_GET_TAG(r2->left) == JS_TAG_STRING &&
4532 JS_VALUE_GET_STRING(r2->left)->len <= JS_STRING_ROPE_SHORT_LEN) {
4533 JSValue val, ret;
4534 val = JS_ConcatString2(ctx, op1, JS_DupValue(ctx, r2->left));
4535 if (JS_IsException(val)) {
4536 JS_FreeValue(ctx, op2);
4537 return JS_EXCEPTION;
4538 }
4539 ret = js_new_string_rope(ctx, val, JS_DupValue(ctx, r2->right));
4540 JS_FreeValue(ctx, op2);
4541 return ret;
4542 }
4543 }
4544 return js_new_string_rope(ctx, op1, op2);
4545}
4546
4547/* Shape support */
4548
4549static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
4550{
4551 return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
4552 prop_size * sizeof(JSShapeProperty);
4553}
4554
4555static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
4556{
4557 return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
4558}
4559
4560static inline uint32_t *prop_hash_end(JSShape *sh)
4561{
4562 return (uint32_t *)sh;
4563}
4564
4565static inline void *get_alloc_from_shape(JSShape *sh)
4566{
4567 return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
4568}
4569
4570static inline JSShapeProperty *get_shape_prop(JSShape *sh)
4571{
4572 return sh->prop;
4573}
4574
4575static int init_shape_hash(JSRuntime *rt)
4576{
4577 rt->shape_hash_bits = 4; /* 16 shapes */
4578 rt->shape_hash_size = 1 << rt->shape_hash_bits;
4579 rt->shape_hash_count = 0;
4580 rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4581 rt->shape_hash_size);
4582 if (!rt->shape_hash)
4583 return -1;
4584 return 0;
4585}
4586
4587/* same magic hash multiplier as the Linux kernel */
4588static uint32_t shape_hash(uint32_t h, uint32_t val)
4589{
4590 return (h + val) * 0x9e370001;
4591}
4592
4593/* truncate the shape hash to 'hash_bits' bits */
4594static uint32_t get_shape_hash(uint32_t h, int hash_bits)
4595{
4596 return h >> (32 - hash_bits);
4597}
4598
4599static uint32_t shape_initial_hash(JSObject *proto)
4600{
4601 uint32_t h;
4602 h = shape_hash(1, (uintptr_t)proto);
4603 if (sizeof(proto) > 4)
4604 h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
4605 return h;
4606}
4607
4608static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
4609{
4610 int new_shape_hash_size, i;
4611 uint32_t h;
4612 JSShape **new_shape_hash, *sh, *sh_next;
4613
4614 new_shape_hash_size = 1 << new_shape_hash_bits;
4615 new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
4616 new_shape_hash_size);
4617 if (!new_shape_hash)
4618 return -1;
4619 for(i = 0; i < rt->shape_hash_size; i++) {
4620 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
4621 sh_next = sh->shape_hash_next;
4622 h = get_shape_hash(sh->hash, new_shape_hash_bits);
4623 sh->shape_hash_next = new_shape_hash[h];
4624 new_shape_hash[h] = sh;
4625 }
4626 }
4627 js_free_rt(rt, rt->shape_hash);
4628 rt->shape_hash_bits = new_shape_hash_bits;
4629 rt->shape_hash_size = new_shape_hash_size;
4630 rt->shape_hash = new_shape_hash;
4631 return 0;
4632}
4633
4634static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
4635{
4636 uint32_t h;
4637 h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4638 sh->shape_hash_next = rt->shape_hash[h];
4639 rt->shape_hash[h] = sh;
4640 rt->shape_hash_count++;
4641}
4642
4643static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
4644{
4645 uint32_t h;
4646 JSShape **psh;
4647
4648 h = get_shape_hash(sh->hash, rt->shape_hash_bits);
4649 psh = &rt->shape_hash[h];
4650 while (*psh != sh)
4651 psh = &(*psh)->shape_hash_next;
4652 *psh = sh->shape_hash_next;
4653 rt->shape_hash_count--;
4654}
4655
4656/* create a new empty shape with prototype 'proto' */
4657static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
4658 int hash_size, int prop_size)
4659{
4660 JSRuntime *rt = ctx->rt;
4661 void *sh_alloc;
4662 JSShape *sh;
4663
4664 /* resize the shape hash table if necessary */
4665 if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
4666 resize_shape_hash(rt, rt->shape_hash_bits + 1);
4667 }
4668
4669 sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
4670 if (!sh_alloc)
4671 return NULL;
4672 sh = get_shape_from_alloc(sh_alloc, hash_size);
4673 sh->header.ref_count = 1;
4674 add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4675 if (proto)
4676 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
4677 sh->proto = proto;
4678 memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
4679 hash_size);
4680 sh->prop_hash_mask = hash_size - 1;
4681 sh->prop_size = prop_size;
4682 sh->prop_count = 0;
4683 sh->deleted_prop_count = 0;
4684
4685 /* insert in the hash table */
4686 sh->hash = shape_initial_hash(proto);
4687 sh->is_hashed = TRUE;
4688 sh->has_small_array_index = FALSE;
4689 js_shape_hash_link(ctx->rt, sh);
4690 return sh;
4691}
4692
4693static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
4694{
4695 return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
4696 JS_PROP_INITIAL_SIZE);
4697}
4698
4699/* The shape is cloned. The new shape is not inserted in the shape
4700 hash table */
4701static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
4702{
4703 JSShape *sh;
4704 void *sh_alloc, *sh_alloc1;
4705 size_t size;
4706 JSShapeProperty *pr;
4707 uint32_t i, hash_size;
4708
4709 hash_size = sh1->prop_hash_mask + 1;
4710 size = get_shape_size(hash_size, sh1->prop_size);
4711 sh_alloc = js_malloc(ctx, size);
4712 if (!sh_alloc)
4713 return NULL;
4714 sh_alloc1 = get_alloc_from_shape(sh1);
4715 memcpy(sh_alloc, sh_alloc1, size);
4716 sh = get_shape_from_alloc(sh_alloc, hash_size);
4717 sh->header.ref_count = 1;
4718 add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
4719 sh->is_hashed = FALSE;
4720 if (sh->proto) {
4721 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4722 }
4723 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
4724 JS_DupAtom(ctx, pr->atom);
4725 }
4726 return sh;
4727}
4728
4729static JSShape *js_dup_shape(JSShape *sh)
4730{
4731 sh->header.ref_count++;
4732 return sh;
4733}
4734
4735static void js_free_shape0(JSRuntime *rt, JSShape *sh)
4736{
4737 uint32_t i;
4738 JSShapeProperty *pr;
4739
4740 assert(sh->header.ref_count == 0);
4741 if (sh->is_hashed)
4742 js_shape_hash_unlink(rt, sh);
4743 if (sh->proto != NULL) {
4744 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
4745 }
4746 pr = get_shape_prop(sh);
4747 for(i = 0; i < sh->prop_count; i++) {
4748 JS_FreeAtomRT(rt, pr->atom);
4749 pr++;
4750 }
4751 remove_gc_object(&sh->header);
4752 js_free_rt(rt, get_alloc_from_shape(sh));
4753}
4754
4755static void js_free_shape(JSRuntime *rt, JSShape *sh)
4756{
4757 if (unlikely(--sh->header.ref_count <= 0)) {
4758 js_free_shape0(rt, sh);
4759 }
4760}
4761
4762static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
4763{
4764 if (sh)
4765 js_free_shape(rt, sh);
4766}
4767
4768/* make space to hold at least 'count' properties */
4769static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
4770 JSObject *p, uint32_t count)
4771{
4772 JSShape *sh;
4773 uint32_t new_size, new_hash_size, new_hash_mask, i;
4774 JSShapeProperty *pr;
4775 void *sh_alloc;
4776 intptr_t h;
4777 JSShape *old_sh;
4778
4779 sh = *psh;
4780 new_size = max_int(count, sh->prop_size * 3 / 2);
4781 /* Reallocate prop array first to avoid crash or size inconsistency
4782 in case of memory allocation failure */
4783 if (p) {
4784 JSProperty *new_prop;
4785 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4786 if (unlikely(!new_prop))
4787 return -1;
4788 p->prop = new_prop;
4789 }
4790 new_hash_size = sh->prop_hash_mask + 1;
4791 while (new_hash_size < new_size)
4792 new_hash_size = 2 * new_hash_size;
4793 /* resize the property shapes. Using js_realloc() is not possible in
4794 case the GC runs during the allocation */
4795 old_sh = sh;
4796 sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4797 if (!sh_alloc)
4798 return -1;
4799 sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4800 list_del(&old_sh->header.link);
4801 /* copy all the shape properties */
4802 memcpy(sh, old_sh,
4803 sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
4804 list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4805
4806 if (new_hash_size != (sh->prop_hash_mask + 1)) {
4807 /* resize the hash table and the properties */
4808 new_hash_mask = new_hash_size - 1;
4809 sh->prop_hash_mask = new_hash_mask;
4810 memset(prop_hash_end(sh) - new_hash_size, 0,
4811 sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4812 for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
4813 if (pr->atom != JS_ATOM_NULL) {
4814 h = ((uintptr_t)pr->atom & new_hash_mask);
4815 pr->hash_next = prop_hash_end(sh)[-h - 1];
4816 prop_hash_end(sh)[-h - 1] = i + 1;
4817 }
4818 }
4819 } else {
4820 /* just copy the previous hash table */
4821 memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size,
4822 sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4823 }
4824 js_free(ctx, get_alloc_from_shape(old_sh));
4825 *psh = sh;
4826 sh->prop_size = new_size;
4827 return 0;
4828}
4829
4830/* remove the deleted properties. */
4831static int compact_properties(JSContext *ctx, JSObject *p)
4832{
4833 JSShape *sh, *old_sh;
4834 void *sh_alloc;
4835 intptr_t h;
4836 uint32_t new_hash_size, i, j, new_hash_mask, new_size;
4837 JSShapeProperty *old_pr, *pr;
4838 JSProperty *prop, *new_prop;
4839
4840 sh = p->shape;
4841 assert(!sh->is_hashed);
4842
4843 new_size = max_int(JS_PROP_INITIAL_SIZE,
4844 sh->prop_count - sh->deleted_prop_count);
4845 assert(new_size <= sh->prop_size);
4846
4847 new_hash_size = sh->prop_hash_mask + 1;
4848 while ((new_hash_size / 2) >= new_size)
4849 new_hash_size = new_hash_size / 2;
4850 new_hash_mask = new_hash_size - 1;
4851
4852 /* resize the hash table and the properties */
4853 old_sh = sh;
4854 sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
4855 if (!sh_alloc)
4856 return -1;
4857 sh = get_shape_from_alloc(sh_alloc, new_hash_size);
4858 list_del(&old_sh->header.link);
4859 memcpy(sh, old_sh, sizeof(JSShape));
4860 list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
4861
4862 memset(prop_hash_end(sh) - new_hash_size, 0,
4863 sizeof(prop_hash_end(sh)[0]) * new_hash_size);
4864
4865 j = 0;
4866 old_pr = old_sh->prop;
4867 pr = sh->prop;
4868 prop = p->prop;
4869 for(i = 0; i < sh->prop_count; i++) {
4870 if (old_pr->atom != JS_ATOM_NULL) {
4871 pr->atom = old_pr->atom;
4872 pr->flags = old_pr->flags;
4873 h = ((uintptr_t)old_pr->atom & new_hash_mask);
4874 pr->hash_next = prop_hash_end(sh)[-h - 1];
4875 prop_hash_end(sh)[-h - 1] = j + 1;
4876 prop[j] = prop[i];
4877 j++;
4878 pr++;
4879 }
4880 old_pr++;
4881 }
4882 assert(j == (sh->prop_count - sh->deleted_prop_count));
4883 sh->prop_hash_mask = new_hash_mask;
4884 sh->prop_size = new_size;
4885 sh->deleted_prop_count = 0;
4886 sh->prop_count = j;
4887
4888 p->shape = sh;
4889 js_free(ctx, get_alloc_from_shape(old_sh));
4890
4891 /* reduce the size of the object properties */
4892 new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
4893 if (new_prop)
4894 p->prop = new_prop;
4895 return 0;
4896}
4897
4898static int add_shape_property(JSContext *ctx, JSShape **psh,
4899 JSObject *p, JSAtom atom, int prop_flags)
4900{
4901 JSRuntime *rt = ctx->rt;
4902 JSShape *sh = *psh;
4903 JSShapeProperty *pr, *prop;
4904 uint32_t hash_mask, new_shape_hash = 0;
4905 intptr_t h;
4906
4907 /* update the shape hash */
4908 if (sh->is_hashed) {
4909 js_shape_hash_unlink(rt, sh);
4910 new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
4911 }
4912
4913 if (unlikely(sh->prop_count >= sh->prop_size)) {
4914 if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
4915 /* in case of error, reinsert in the hash table.
4916 sh is still valid if resize_properties() failed */
4917 if (sh->is_hashed)
4918 js_shape_hash_link(rt, sh);
4919 return -1;
4920 }
4921 sh = *psh;
4922 }
4923 if (sh->is_hashed) {
4924 sh->hash = new_shape_hash;
4925 js_shape_hash_link(rt, sh);
4926 }
4927 /* Initialize the new shape property.
4928 The object property at p->prop[sh->prop_count] is uninitialized */
4929 prop = get_shape_prop(sh);
4930 pr = &prop[sh->prop_count++];
4931 pr->atom = JS_DupAtom(ctx, atom);
4932 pr->flags = prop_flags;
4933 sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
4934 /* add in hash table */
4935 hash_mask = sh->prop_hash_mask;
4936 h = atom & hash_mask;
4937 pr->hash_next = prop_hash_end(sh)[-h - 1];
4938 prop_hash_end(sh)[-h - 1] = sh->prop_count;
4939 return 0;
4940}
4941
4942/* find a hashed empty shape matching the prototype. Return NULL if
4943 not found */
4944static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
4945{
4946 JSShape *sh1;
4947 uint32_t h, h1;
4948
4949 h = shape_initial_hash(proto);
4950 h1 = get_shape_hash(h, rt->shape_hash_bits);
4951 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4952 if (sh1->hash == h &&
4953 sh1->proto == proto &&
4954 sh1->prop_count == 0) {
4955 return sh1;
4956 }
4957 }
4958 return NULL;
4959}
4960
4961/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
4962 not found */
4963static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
4964 JSAtom atom, int prop_flags)
4965{
4966 JSShape *sh1;
4967 uint32_t h, h1, i, n;
4968
4969 h = sh->hash;
4970 h = shape_hash(h, atom);
4971 h = shape_hash(h, prop_flags);
4972 h1 = get_shape_hash(h, rt->shape_hash_bits);
4973 for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
4974 /* we test the hash first so that the rest is done only if the
4975 shapes really match */
4976 if (sh1->hash == h &&
4977 sh1->proto == sh->proto &&
4978 sh1->prop_count == ((n = sh->prop_count) + 1)) {
4979 for(i = 0; i < n; i++) {
4980 if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
4981 unlikely(sh1->prop[i].flags != sh->prop[i].flags))
4982 goto next;
4983 }
4984 if (unlikely(sh1->prop[n].atom != atom) ||
4985 unlikely(sh1->prop[n].flags != prop_flags))
4986 goto next;
4987 return sh1;
4988 }
4989 next: ;
4990 }
4991 return NULL;
4992}
4993
4994static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
4995{
4996 char atom_buf[ATOM_GET_STR_BUF_SIZE];
4997 int j;
4998
4999 /* XXX: should output readable class prototype */
5000 printf("%5d %3d%c %14p %5d %5d", i,
5001 sh->header.ref_count, " *"[sh->is_hashed],
5002 (void *)sh->proto, sh->prop_size, sh->prop_count);
5003 for(j = 0; j < sh->prop_count; j++) {
5004 printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
5005 sh->prop[j].atom));
5006 }
5007 printf("\n");
5008}
5009
5010static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
5011{
5012 int i;
5013 JSShape *sh;
5014 struct list_head *el;
5015 JSObject *p;
5016 JSGCObjectHeader *gp;
5017
5018 printf("JSShapes: {\n");
5019 printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
5020 for(i = 0; i < rt->shape_hash_size; i++) {
5021 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
5022 JS_DumpShape(rt, i, sh);
5023 assert(sh->is_hashed);
5024 }
5025 }
5026 /* dump non-hashed shapes */
5027 list_for_each(el, &rt->gc_obj_list) {
5028 gp = list_entry(el, JSGCObjectHeader, link);
5029 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
5030 p = (JSObject *)gp;
5031 if (!p->shape->is_hashed) {
5032 JS_DumpShape(rt, -1, p->shape);
5033 }
5034 }
5035 }
5036 printf("}\n");
5037}
5038
5039static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
5040{
5041 JSObject *p;
5042
5043 js_trigger_gc(ctx->rt, sizeof(JSObject));
5044 p = js_malloc(ctx, sizeof(JSObject));
5045 if (unlikely(!p))
5046 goto fail;
5047 p->class_id = class_id;
5048 p->extensible = TRUE;
5049 p->free_mark = 0;
5050 p->is_exotic = 0;
5051 p->fast_array = 0;
5052 p->is_constructor = 0;
5053 p->is_uncatchable_error = 0;
5054 p->tmp_mark = 0;
5055 p->is_HTMLDDA = 0;
5056 p->weakref_count = 0;
5057 p->u.opaque = NULL;
5058 p->shape = sh;
5059 p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
5060 if (unlikely(!p->prop)) {
5061 js_free(ctx, p);
5062 fail:
5063 js_free_shape(ctx->rt, sh);
5064 return JS_EXCEPTION;
5065 }
5066
5067 switch(class_id) {
5068 case JS_CLASS_OBJECT:
5069 break;
5070 case JS_CLASS_ARRAY:
5071 {
5072 JSProperty *pr;
5073 p->is_exotic = 1;
5074 p->fast_array = 1;
5075 p->u.array.u.values = NULL;
5076 p->u.array.count = 0;
5077 p->u.array.u1.size = 0;
5078 /* the length property is always the first one */
5079 if (likely(sh == ctx->array_shape)) {
5080 pr = &p->prop[0];
5081 } else {
5082 /* only used for the first array */
5083 /* cannot fail */
5084 pr = add_property(ctx, p, JS_ATOM_length,
5085 JS_PROP_WRITABLE | JS_PROP_LENGTH);
5086 }
5087 pr->u.value = JS_NewInt32(ctx, 0);
5088 }
5089 break;
5090 case JS_CLASS_C_FUNCTION:
5091 p->prop[0].u.value = JS_UNDEFINED;
5092 break;
5093 case JS_CLASS_ARGUMENTS:
5094 case JS_CLASS_UINT8C_ARRAY:
5095 case JS_CLASS_INT8_ARRAY:
5096 case JS_CLASS_UINT8_ARRAY:
5097 case JS_CLASS_INT16_ARRAY:
5098 case JS_CLASS_UINT16_ARRAY:
5099 case JS_CLASS_INT32_ARRAY:
5100 case JS_CLASS_UINT32_ARRAY:
5101 case JS_CLASS_BIG_INT64_ARRAY:
5102 case JS_CLASS_BIG_UINT64_ARRAY:
5103 case JS_CLASS_FLOAT32_ARRAY:
5104 case JS_CLASS_FLOAT64_ARRAY:
5105 p->is_exotic = 1;
5106 p->fast_array = 1;
5107 p->u.array.u.ptr = NULL;
5108 p->u.array.count = 0;
5109 break;
5110 case JS_CLASS_DATAVIEW:
5111 p->u.array.u.ptr = NULL;
5112 p->u.array.count = 0;
5113 break;
5114 case JS_CLASS_NUMBER:
5115 case JS_CLASS_STRING:
5116 case JS_CLASS_BOOLEAN:
5117 case JS_CLASS_SYMBOL:
5118 case JS_CLASS_DATE:
5119 case JS_CLASS_BIG_INT:
5120 p->u.object_data = JS_UNDEFINED;
5121 goto set_exotic;
5122 case JS_CLASS_REGEXP:
5123 p->u.regexp.pattern = NULL;
5124 p->u.regexp.bytecode = NULL;
5125 goto set_exotic;
5126 default:
5127 set_exotic:
5128 if (ctx->rt->class_array[class_id].exotic) {
5129 p->is_exotic = 1;
5130 }
5131 break;
5132 }
5133 p->header.ref_count = 1;
5134 add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
5135 return JS_MKPTR(JS_TAG_OBJECT, p);
5136}
5137
5138static JSObject *get_proto_obj(JSValueConst proto_val)
5139{
5140 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
5141 return NULL;
5142 else
5143 return JS_VALUE_GET_OBJ(proto_val);
5144}
5145
5146/* WARNING: proto must be an object or JS_NULL */
5147JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
5148 JSClassID class_id)
5149{
5150 JSShape *sh;
5151 JSObject *proto;
5152
5153 proto = get_proto_obj(proto_val);
5154 sh = find_hashed_shape_proto(ctx->rt, proto);
5155 if (likely(sh)) {
5156 sh = js_dup_shape(sh);
5157 } else {
5158 sh = js_new_shape(ctx, proto);
5159 if (!sh)
5160 return JS_EXCEPTION;
5161 }
5162 return JS_NewObjectFromShape(ctx, sh, class_id);
5163}
5164
5165#if 0
5166static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
5167{
5168 JSObject *p;
5169
5170 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
5171 p = JS_VALUE_GET_OBJ(obj);
5172 switch(p->class_id) {
5173 case JS_CLASS_NUMBER:
5174 case JS_CLASS_STRING:
5175 case JS_CLASS_BOOLEAN:
5176 case JS_CLASS_SYMBOL:
5177 case JS_CLASS_DATE:
5178 case JS_CLASS_BIG_INT:
5179 return JS_DupValue(ctx, p->u.object_data);
5180 }
5181 }
5182 return JS_UNDEFINED;
5183}
5184#endif
5185
5186static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
5187{
5188 JSObject *p;
5189
5190 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
5191 p = JS_VALUE_GET_OBJ(obj);
5192 switch(p->class_id) {
5193 case JS_CLASS_NUMBER:
5194 case JS_CLASS_STRING:
5195 case JS_CLASS_BOOLEAN:
5196 case JS_CLASS_SYMBOL:
5197 case JS_CLASS_DATE:
5198 case JS_CLASS_BIG_INT:
5199 JS_FreeValue(ctx, p->u.object_data);
5200 p->u.object_data = val; /* for JS_CLASS_STRING, 'val' must
5201 be JS_TAG_STRING (and not a
5202 rope) */
5203 return 0;
5204 }
5205 }
5206 JS_FreeValue(ctx, val);
5207 if (!JS_IsException(obj))
5208 JS_ThrowTypeError(ctx, "invalid object type");
5209 return -1;
5210}
5211
5212JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
5213{
5214 return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
5215}
5216
5217JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
5218{
5219 return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
5220}
5221
5222JSValue JS_NewArray(JSContext *ctx)
5223{
5224 return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
5225 JS_CLASS_ARRAY);
5226}
5227
5228JSValue JS_NewObject(JSContext *ctx)
5229{
5230 /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
5231 return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
5232}
5233
5234static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
5235 JSAtom name, int len)
5236{
5237 /* ES6 feature non compatible with ES5.1: length is configurable */
5238 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
5239 JS_PROP_CONFIGURABLE);
5240 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
5241 JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
5242}
5243
5244static BOOL js_class_has_bytecode(JSClassID class_id)
5245{
5246 return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
5247 class_id == JS_CLASS_GENERATOR_FUNCTION ||
5248 class_id == JS_CLASS_ASYNC_FUNCTION ||
5249 class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
5250}
5251
5252/* return NULL without exception if not a function or no bytecode */
5253static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
5254{
5255 JSObject *p;
5256 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
5257 return NULL;
5258 p = JS_VALUE_GET_OBJ(val);
5259 if (!js_class_has_bytecode(p->class_id))
5260 return NULL;
5261 return p->u.func.function_bytecode;
5262}
5263
5264static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
5265 JSValueConst home_obj)
5266{
5267 JSObject *p, *p1;
5268 JSFunctionBytecode *b;
5269
5270 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
5271 return;
5272 p = JS_VALUE_GET_OBJ(func_obj);
5273 if (!js_class_has_bytecode(p->class_id))
5274 return;
5275 b = p->u.func.function_bytecode;
5276 if (b->need_home_object) {
5277 p1 = p->u.func.home_object;
5278 if (p1) {
5279 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
5280 }
5281 if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
5282 p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
5283 else
5284 p1 = NULL;
5285 p->u.func.home_object = p1;
5286 }
5287}
5288
5289static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
5290{
5291 JSValue name_str;
5292
5293 name_str = JS_AtomToString(ctx, name);
5294 if (JS_AtomSymbolHasDescription(ctx, name)) {
5295 name_str = JS_ConcatString3(ctx, "[", name_str, "]");
5296 }
5297 return name_str;
5298}
5299
5300/* Modify the name of a method according to the atom and
5301 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
5302 JS_PROP_HAS_SET. Also set the home object of the method.
5303 Return < 0 if exception. */
5304static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
5305 JSAtom name, int flags, JSValueConst home_obj)
5306{
5307 JSValue name_str;
5308
5309 name_str = js_get_function_name(ctx, name);
5310 if (flags & JS_PROP_HAS_GET) {
5311 name_str = JS_ConcatString3(ctx, "get ", name_str, "");
5312 } else if (flags & JS_PROP_HAS_SET) {
5313 name_str = JS_ConcatString3(ctx, "set ", name_str, "");
5314 }
5315 if (JS_IsException(name_str))
5316 return -1;
5317 if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
5318 JS_PROP_CONFIGURABLE) < 0)
5319 return -1;
5320 js_method_set_home_object(ctx, func_obj, home_obj);
5321 return 0;
5322}
5323
5324/* Note: at least 'length' arguments will be readable in 'argv' */
5325static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
5326 const char *name,
5327 int length, JSCFunctionEnum cproto, int magic,
5328 JSValueConst proto_val)
5329{
5330 JSValue func_obj;
5331 JSObject *p;
5332 JSAtom name_atom;
5333
5334 func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
5335 if (JS_IsException(func_obj))
5336 return func_obj;
5337 p = JS_VALUE_GET_OBJ(func_obj);
5338 p->u.cfunc.realm = JS_DupContext(ctx);
5339 p->u.cfunc.c_function.generic = func;
5340 p->u.cfunc.length = length;
5341 p->u.cfunc.cproto = cproto;
5342 p->u.cfunc.magic = magic;
5343 p->is_constructor = (cproto == JS_CFUNC_constructor ||
5344 cproto == JS_CFUNC_constructor_magic ||
5345 cproto == JS_CFUNC_constructor_or_func ||
5346 cproto == JS_CFUNC_constructor_or_func_magic);
5347 if (!name)
5348 name = "";
5349 name_atom = JS_NewAtom(ctx, name);
5350 js_function_set_properties(ctx, func_obj, name_atom, length);
5351 JS_FreeAtom(ctx, name_atom);
5352 return func_obj;
5353}
5354
5355/* Note: at least 'length' arguments will be readable in 'argv' */
5356JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
5357 const char *name,
5358 int length, JSCFunctionEnum cproto, int magic)
5359{
5360 return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
5361 ctx->function_proto);
5362}
5363
5364typedef struct JSCFunctionDataRecord {
5365 JSCFunctionData *func;
5366 uint8_t length;
5367 uint8_t data_len;
5368 uint16_t magic;
5369 JSValue data[0];
5370} JSCFunctionDataRecord;
5371
5372static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
5373{
5374 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5375 int i;
5376
5377 if (s) {
5378 for(i = 0; i < s->data_len; i++) {
5379 JS_FreeValueRT(rt, s->data[i]);
5380 }
5381 js_free_rt(rt, s);
5382 }
5383}
5384
5385static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
5386 JS_MarkFunc *mark_func)
5387{
5388 JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
5389 int i;
5390
5391 if (s) {
5392 for(i = 0; i < s->data_len; i++) {
5393 JS_MarkValue(rt, s->data[i], mark_func);
5394 }
5395 }
5396}
5397
5398static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
5399 JSValueConst this_val,
5400 int argc, JSValueConst *argv, int flags)
5401{
5402 JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
5403 JSValueConst *arg_buf;
5404 int i;
5405
5406 /* XXX: could add the function on the stack for debug */
5407 if (unlikely(argc < s->length)) {
5408 arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
5409 for(i = 0; i < argc; i++)
5410 arg_buf[i] = argv[i];
5411 for(i = argc; i < s->length; i++)
5412 arg_buf[i] = JS_UNDEFINED;
5413 } else {
5414 arg_buf = argv;
5415 }
5416
5417 return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
5418}
5419
5420JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
5421 int length, int magic, int data_len,
5422 JSValueConst *data)
5423{
5424 JSCFunctionDataRecord *s;
5425 JSValue func_obj;
5426 int i;
5427
5428 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
5429 JS_CLASS_C_FUNCTION_DATA);
5430 if (JS_IsException(func_obj))
5431 return func_obj;
5432 s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
5433 if (!s) {
5434 JS_FreeValue(ctx, func_obj);
5435 return JS_EXCEPTION;
5436 }
5437 s->func = func;
5438 s->length = length;
5439 s->data_len = data_len;
5440 s->magic = magic;
5441 for(i = 0; i < data_len; i++)
5442 s->data[i] = JS_DupValue(ctx, data[i]);
5443 JS_SetOpaque(func_obj, s);
5444 js_function_set_properties(ctx, func_obj,
5445 JS_ATOM_empty_string, length);
5446 return func_obj;
5447}
5448
5449static JSContext *js_autoinit_get_realm(JSProperty *pr)
5450{
5451 return (JSContext *)(pr->u.init.realm_and_id & ~3);
5452}
5453
5454static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
5455{
5456 return pr->u.init.realm_and_id & 3;
5457}
5458
5459static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
5460{
5461 JS_FreeContext(js_autoinit_get_realm(pr));
5462}
5463
5464static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
5465 JS_MarkFunc *mark_func)
5466{
5467 mark_func(rt, &js_autoinit_get_realm(pr)->header);
5468}
5469
5470static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
5471{
5472 if (unlikely(prop_flags & JS_PROP_TMASK)) {
5473 if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5474 if (pr->u.getset.getter)
5475 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
5476 if (pr->u.getset.setter)
5477 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
5478 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5479 free_var_ref(rt, pr->u.var_ref);
5480 } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5481 js_autoinit_free(rt, pr);
5482 }
5483 } else {
5484 JS_FreeValueRT(rt, pr->u.value);
5485 }
5486}
5487
5488static force_inline JSShapeProperty *find_own_property1(JSObject *p,
5489 JSAtom atom)
5490{
5491 JSShape *sh;
5492 JSShapeProperty *pr, *prop;
5493 intptr_t h;
5494 sh = p->shape;
5495 h = (uintptr_t)atom & sh->prop_hash_mask;
5496 h = prop_hash_end(sh)[-h - 1];
5497 prop = get_shape_prop(sh);
5498 while (h) {
5499 pr = &prop[h - 1];
5500 if (likely(pr->atom == atom)) {
5501 return pr;
5502 }
5503 h = pr->hash_next;
5504 }
5505 return NULL;
5506}
5507
5508static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
5509 JSObject *p,
5510 JSAtom atom)
5511{
5512 JSShape *sh;
5513 JSShapeProperty *pr, *prop;
5514 intptr_t h;
5515 sh = p->shape;
5516 h = (uintptr_t)atom & sh->prop_hash_mask;
5517 h = prop_hash_end(sh)[-h - 1];
5518 prop = get_shape_prop(sh);
5519 while (h) {
5520 pr = &prop[h - 1];
5521 if (likely(pr->atom == atom)) {
5522 *ppr = &p->prop[h - 1];
5523 /* the compiler should be able to assume that pr != NULL here */
5524 return pr;
5525 }
5526 h = pr->hash_next;
5527 }
5528 *ppr = NULL;
5529 return NULL;
5530}
5531
5532/* indicate that the object may be part of a function prototype cycle */
5533static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
5534{
5535}
5536
5537static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
5538{
5539 if (var_ref) {
5540 assert(var_ref->header.ref_count > 0);
5541 if (--var_ref->header.ref_count == 0) {
5542 if (var_ref->is_detached) {
5543 JS_FreeValueRT(rt, var_ref->value);
5544 } else {
5545 list_del(&var_ref->var_ref_link); /* still on the stack */
5546 if (var_ref->async_func)
5547 async_func_free(rt, var_ref->async_func);
5548 }
5549 remove_gc_object(&var_ref->header);
5550 js_free_rt(rt, var_ref);
5551 }
5552 }
5553}
5554
5555static void js_array_finalizer(JSRuntime *rt, JSValue val)
5556{
5557 JSObject *p = JS_VALUE_GET_OBJ(val);
5558 int i;
5559
5560 for(i = 0; i < p->u.array.count; i++) {
5561 JS_FreeValueRT(rt, p->u.array.u.values[i]);
5562 }
5563 js_free_rt(rt, p->u.array.u.values);
5564}
5565
5566static void js_array_mark(JSRuntime *rt, JSValueConst val,
5567 JS_MarkFunc *mark_func)
5568{
5569 JSObject *p = JS_VALUE_GET_OBJ(val);
5570 int i;
5571
5572 for(i = 0; i < p->u.array.count; i++) {
5573 JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
5574 }
5575}
5576
5577static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
5578{
5579 JSObject *p = JS_VALUE_GET_OBJ(val);
5580 JS_FreeValueRT(rt, p->u.object_data);
5581 p->u.object_data = JS_UNDEFINED;
5582}
5583
5584static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
5585 JS_MarkFunc *mark_func)
5586{
5587 JSObject *p = JS_VALUE_GET_OBJ(val);
5588 JS_MarkValue(rt, p->u.object_data, mark_func);
5589}
5590
5591static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
5592{
5593 JSObject *p = JS_VALUE_GET_OBJ(val);
5594
5595 if (p->u.cfunc.realm)
5596 JS_FreeContext(p->u.cfunc.realm);
5597}
5598
5599static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
5600 JS_MarkFunc *mark_func)
5601{
5602 JSObject *p = JS_VALUE_GET_OBJ(val);
5603
5604 if (p->u.cfunc.realm)
5605 mark_func(rt, &p->u.cfunc.realm->header);
5606}
5607
5608static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
5609{
5610 JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
5611 JSFunctionBytecode *b;
5612 JSVarRef **var_refs;
5613 int i;
5614
5615 p1 = p->u.func.home_object;
5616 if (p1) {
5617 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
5618 }
5619 b = p->u.func.function_bytecode;
5620 if (b) {
5621 var_refs = p->u.func.var_refs;
5622 if (var_refs) {
5623 for(i = 0; i < b->closure_var_count; i++)
5624 free_var_ref(rt, var_refs[i]);
5625 js_free_rt(rt, var_refs);
5626 }
5627 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
5628 }
5629}
5630
5631static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
5632 JS_MarkFunc *mark_func)
5633{
5634 JSObject *p = JS_VALUE_GET_OBJ(val);
5635 JSVarRef **var_refs = p->u.func.var_refs;
5636 JSFunctionBytecode *b = p->u.func.function_bytecode;
5637 int i;
5638
5639 if (p->u.func.home_object) {
5640 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
5641 mark_func);
5642 }
5643 if (b) {
5644 if (var_refs) {
5645 for(i = 0; i < b->closure_var_count; i++) {
5646 JSVarRef *var_ref = var_refs[i];
5647 if (var_ref) {
5648 mark_func(rt, &var_ref->header);
5649 }
5650 }
5651 }
5652 /* must mark the function bytecode because template objects may be
5653 part of a cycle */
5654 JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
5655 }
5656}
5657
5658static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
5659{
5660 JSObject *p = JS_VALUE_GET_OBJ(val);
5661 JSBoundFunction *bf = p->u.bound_function;
5662 int i;
5663
5664 JS_FreeValueRT(rt, bf->func_obj);
5665 JS_FreeValueRT(rt, bf->this_val);
5666 for(i = 0; i < bf->argc; i++) {
5667 JS_FreeValueRT(rt, bf->argv[i]);
5668 }
5669 js_free_rt(rt, bf);
5670}
5671
5672static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
5673 JS_MarkFunc *mark_func)
5674{
5675 JSObject *p = JS_VALUE_GET_OBJ(val);
5676 JSBoundFunction *bf = p->u.bound_function;
5677 int i;
5678
5679 JS_MarkValue(rt, bf->func_obj, mark_func);
5680 JS_MarkValue(rt, bf->this_val, mark_func);
5681 for(i = 0; i < bf->argc; i++)
5682 JS_MarkValue(rt, bf->argv[i], mark_func);
5683}
5684
5685static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
5686{
5687 JSObject *p = JS_VALUE_GET_OBJ(val);
5688 JSForInIterator *it = p->u.for_in_iterator;
5689 int i;
5690
5691 JS_FreeValueRT(rt, it->obj);
5692 if (!it->is_array) {
5693 for(i = 0; i < it->atom_count; i++) {
5694 JS_FreeAtomRT(rt, it->tab_atom[i].atom);
5695 }
5696 js_free_rt(rt, it->tab_atom);
5697 }
5698 js_free_rt(rt, it);
5699}
5700
5701static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
5702 JS_MarkFunc *mark_func)
5703{
5704 JSObject *p = JS_VALUE_GET_OBJ(val);
5705 JSForInIterator *it = p->u.for_in_iterator;
5706 JS_MarkValue(rt, it->obj, mark_func);
5707}
5708
5709static void free_object(JSRuntime *rt, JSObject *p)
5710{
5711 int i;
5712 JSClassFinalizer *finalizer;
5713 JSShape *sh;
5714 JSShapeProperty *pr;
5715
5716 p->free_mark = 1; /* used to tell the object is invalid when
5717 freeing cycles */
5718 /* free all the fields */
5719 sh = p->shape;
5720 pr = get_shape_prop(sh);
5721 for(i = 0; i < sh->prop_count; i++) {
5722 free_property(rt, &p->prop[i], pr->flags);
5723 pr++;
5724 }
5725 js_free_rt(rt, p->prop);
5726 /* as an optimization we destroy the shape immediately without
5727 putting it in gc_zero_ref_count_list */
5728 js_free_shape(rt, sh);
5729
5730 /* fail safe */
5731 p->shape = NULL;
5732 p->prop = NULL;
5733
5734 finalizer = rt->class_array[p->class_id].finalizer;
5735 if (finalizer)
5736 (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
5737
5738 /* fail safe */
5739 p->class_id = 0;
5740 p->u.opaque = NULL;
5741 p->u.func.var_refs = NULL;
5742 p->u.func.home_object = NULL;
5743
5744 remove_gc_object(&p->header);
5745 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) {
5746 if (p->header.ref_count == 0 && p->weakref_count == 0) {
5747 js_free_rt(rt, p);
5748 } else {
5749 /* keep the object structure because there are may be
5750 references to it */
5751 list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
5752 }
5753 } else {
5754 /* keep the object structure in case there are weak references to it */
5755 if (p->weakref_count == 0) {
5756 js_free_rt(rt, p);
5757 } else {
5758 p->header.mark = 0; /* reset the mark so that the weakref can be freed */
5759 }
5760 }
5761}
5762
5763static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
5764{
5765 switch(gp->gc_obj_type) {
5766 case JS_GC_OBJ_TYPE_JS_OBJECT:
5767 free_object(rt, (JSObject *)gp);
5768 break;
5769 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5770 free_function_bytecode(rt, (JSFunctionBytecode *)gp);
5771 break;
5772 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
5773 __async_func_free(rt, (JSAsyncFunctionState *)gp);
5774 break;
5775 default:
5776 abort();
5777 }
5778}
5779
5780static void free_zero_refcount(JSRuntime *rt)
5781{
5782 struct list_head *el;
5783 JSGCObjectHeader *p;
5784
5785 rt->gc_phase = JS_GC_PHASE_DECREF;
5786 for(;;) {
5787 el = rt->gc_zero_ref_count_list.next;
5788 if (el == &rt->gc_zero_ref_count_list)
5789 break;
5790 p = list_entry(el, JSGCObjectHeader, link);
5791 assert(p->ref_count == 0);
5792 free_gc_object(rt, p);
5793 }
5794 rt->gc_phase = JS_GC_PHASE_NONE;
5795}
5796
5797/* called with the ref_count of 'v' reaches zero. */
5798void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
5799{
5800 uint32_t tag = JS_VALUE_GET_TAG(v);
5801
5802#ifdef DUMP_FREE
5803 {
5804 printf("Freeing ");
5805 if (tag == JS_TAG_OBJECT) {
5806 JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
5807 } else {
5808 JS_DumpValueShort(rt, v);
5809 printf("\n");
5810 }
5811 }
5812#endif
5813
5814 switch(tag) {
5815 case JS_TAG_STRING:
5816 {
5817 JSString *p = JS_VALUE_GET_STRING(v);
5818 if (p->atom_type) {
5819 JS_FreeAtomStruct(rt, p);
5820 } else {
5821#ifdef DUMP_LEAKS
5822 list_del(&p->link);
5823#endif
5824 js_free_rt(rt, p);
5825 }
5826 }
5827 break;
5828 case JS_TAG_STRING_ROPE:
5829 /* Note: recursion is acceptable because the rope depth is bounded */
5830 {
5831 JSStringRope *p = JS_VALUE_GET_STRING_ROPE(v);
5832 JS_FreeValueRT(rt, p->left);
5833 JS_FreeValueRT(rt, p->right);
5834 js_free_rt(rt, p);
5835 }
5836 break;
5837 case JS_TAG_OBJECT:
5838 case JS_TAG_FUNCTION_BYTECODE:
5839 {
5840 JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
5841 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
5842 list_del(&p->link);
5843 list_add(&p->link, &rt->gc_zero_ref_count_list);
5844 p->mark = 1; /* indicate that the object is about to be freed */
5845 if (rt->gc_phase == JS_GC_PHASE_NONE) {
5846 free_zero_refcount(rt);
5847 }
5848 }
5849 }
5850 break;
5851 case JS_TAG_MODULE:
5852 abort(); /* never freed here */
5853 break;
5854 case JS_TAG_BIG_INT:
5855 {
5856 JSBigInt *p = JS_VALUE_GET_PTR(v);
5857 js_free_rt(rt, p);
5858 }
5859 break;
5860 case JS_TAG_SYMBOL:
5861 {
5862 JSAtomStruct *p = JS_VALUE_GET_PTR(v);
5863 JS_FreeAtomStruct(rt, p);
5864 }
5865 break;
5866 default:
5867 abort();
5868 }
5869}
5870
5871void __JS_FreeValue(JSContext *ctx, JSValue v)
5872{
5873 __JS_FreeValueRT(ctx->rt, v);
5874}
5875
5876/* garbage collection */
5877
5878static void gc_remove_weak_objects(JSRuntime *rt)
5879{
5880 struct list_head *el;
5881
5882 /* add the freed objects to rt->gc_zero_ref_count_list so that
5883 rt->weakref_list is not modified while we traverse it */
5884 rt->gc_phase = JS_GC_PHASE_DECREF;
5885
5886 list_for_each(el, &rt->weakref_list) {
5887 JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link);
5888 switch(wh->weakref_type) {
5889 case JS_WEAKREF_TYPE_MAP:
5890 map_delete_weakrefs(rt, wh);
5891 break;
5892 case JS_WEAKREF_TYPE_WEAKREF:
5893 weakref_delete_weakref(rt, wh);
5894 break;
5895 case JS_WEAKREF_TYPE_FINREC:
5896 finrec_delete_weakref(rt, wh);
5897 break;
5898 default:
5899 abort();
5900 }
5901 }
5902
5903 rt->gc_phase = JS_GC_PHASE_NONE;
5904 /* free the freed objects here. */
5905 free_zero_refcount(rt);
5906}
5907
5908static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
5909 JSGCObjectTypeEnum type)
5910{
5911 h->mark = 0;
5912 h->gc_obj_type = type;
5913 list_add_tail(&h->link, &rt->gc_obj_list);
5914}
5915
5916static void remove_gc_object(JSGCObjectHeader *h)
5917{
5918 list_del(&h->link);
5919}
5920
5921void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
5922{
5923 if (JS_VALUE_HAS_REF_COUNT(val)) {
5924 switch(JS_VALUE_GET_TAG(val)) {
5925 case JS_TAG_OBJECT:
5926 case JS_TAG_FUNCTION_BYTECODE:
5927 mark_func(rt, JS_VALUE_GET_PTR(val));
5928 break;
5929 default:
5930 break;
5931 }
5932 }
5933}
5934
5935static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
5936 JS_MarkFunc *mark_func)
5937{
5938 switch(gp->gc_obj_type) {
5939 case JS_GC_OBJ_TYPE_JS_OBJECT:
5940 {
5941 JSObject *p = (JSObject *)gp;
5942 JSShapeProperty *prs;
5943 JSShape *sh;
5944 int i;
5945 sh = p->shape;
5946 mark_func(rt, &sh->header);
5947 /* mark all the fields */
5948 prs = get_shape_prop(sh);
5949 for(i = 0; i < sh->prop_count; i++) {
5950 JSProperty *pr = &p->prop[i];
5951 if (prs->atom != JS_ATOM_NULL) {
5952 if (prs->flags & JS_PROP_TMASK) {
5953 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
5954 if (pr->u.getset.getter)
5955 mark_func(rt, &pr->u.getset.getter->header);
5956 if (pr->u.getset.setter)
5957 mark_func(rt, &pr->u.getset.setter->header);
5958 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
5959 /* Note: the tag does not matter
5960 provided it is a GC object */
5961 mark_func(rt, &pr->u.var_ref->header);
5962 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
5963 js_autoinit_mark(rt, pr, mark_func);
5964 }
5965 } else {
5966 JS_MarkValue(rt, pr->u.value, mark_func);
5967 }
5968 }
5969 prs++;
5970 }
5971
5972 if (p->class_id != JS_CLASS_OBJECT) {
5973 JSClassGCMark *gc_mark;
5974 gc_mark = rt->class_array[p->class_id].gc_mark;
5975 if (gc_mark)
5976 gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
5977 }
5978 }
5979 break;
5980 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
5981 /* the template objects can be part of a cycle */
5982 {
5983 JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
5984 int i;
5985 for(i = 0; i < b->cpool_count; i++) {
5986 JS_MarkValue(rt, b->cpool[i], mark_func);
5987 }
5988 if (b->realm)
5989 mark_func(rt, &b->realm->header);
5990 }
5991 break;
5992 case JS_GC_OBJ_TYPE_VAR_REF:
5993 {
5994 JSVarRef *var_ref = (JSVarRef *)gp;
5995 if (var_ref->is_detached) {
5996 JS_MarkValue(rt, *var_ref->pvalue, mark_func);
5997 } else if (var_ref->async_func) {
5998 mark_func(rt, &var_ref->async_func->header);
5999 }
6000 }
6001 break;
6002 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
6003 {
6004 JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp;
6005 JSStackFrame *sf = &s->frame;
6006 JSValue *sp;
6007
6008 if (!s->is_completed) {
6009 JS_MarkValue(rt, sf->cur_func, mark_func);
6010 JS_MarkValue(rt, s->this_val, mark_func);
6011 /* sf->cur_sp = NULL if the function is running */
6012 if (sf->cur_sp) {
6013 /* if the function is running, cur_sp is not known so we
6014 cannot mark the stack. Marking the variables is not needed
6015 because a running function cannot be part of a removable
6016 cycle */
6017 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
6018 JS_MarkValue(rt, *sp, mark_func);
6019 }
6020 }
6021 JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
6022 JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
6023 }
6024 break;
6025 case JS_GC_OBJ_TYPE_SHAPE:
6026 {
6027 JSShape *sh = (JSShape *)gp;
6028 if (sh->proto != NULL) {
6029 mark_func(rt, &sh->proto->header);
6030 }
6031 }
6032 break;
6033 case JS_GC_OBJ_TYPE_JS_CONTEXT:
6034 {
6035 JSContext *ctx = (JSContext *)gp;
6036 JS_MarkContext(rt, ctx, mark_func);
6037 }
6038 break;
6039 default:
6040 abort();
6041 }
6042}
6043
6044static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
6045{
6046 assert(p->ref_count > 0);
6047 p->ref_count--;
6048 if (p->ref_count == 0 && p->mark == 1) {
6049 list_del(&p->link);
6050 list_add_tail(&p->link, &rt->tmp_obj_list);
6051 }
6052}
6053
6054static void gc_decref(JSRuntime *rt)
6055{
6056 struct list_head *el, *el1;
6057 JSGCObjectHeader *p;
6058
6059 init_list_head(&rt->tmp_obj_list);
6060
6061 /* decrement the refcount of all the children of all the GC
6062 objects and move the GC objects with zero refcount to
6063 tmp_obj_list */
6064 list_for_each_safe(el, el1, &rt->gc_obj_list) {
6065 p = list_entry(el, JSGCObjectHeader, link);
6066 assert(p->mark == 0);
6067 mark_children(rt, p, gc_decref_child);
6068 p->mark = 1;
6069 if (p->ref_count == 0) {
6070 list_del(&p->link);
6071 list_add_tail(&p->link, &rt->tmp_obj_list);
6072 }
6073 }
6074}
6075
6076static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
6077{
6078 p->ref_count++;
6079 if (p->ref_count == 1) {
6080 /* ref_count was 0: remove from tmp_obj_list and add at the
6081 end of gc_obj_list */
6082 list_del(&p->link);
6083 list_add_tail(&p->link, &rt->gc_obj_list);
6084 p->mark = 0; /* reset the mark for the next GC call */
6085 }
6086}
6087
6088static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
6089{
6090 p->ref_count++;
6091}
6092
6093static void gc_scan(JSRuntime *rt)
6094{
6095 struct list_head *el;
6096 JSGCObjectHeader *p;
6097
6098 /* keep the objects with a refcount > 0 and their children. */
6099 list_for_each(el, &rt->gc_obj_list) {
6100 p = list_entry(el, JSGCObjectHeader, link);
6101 assert(p->ref_count > 0);
6102 p->mark = 0; /* reset the mark for the next GC call */
6103 mark_children(rt, p, gc_scan_incref_child);
6104 }
6105
6106 /* restore the refcount of the objects to be deleted. */
6107 list_for_each(el, &rt->tmp_obj_list) {
6108 p = list_entry(el, JSGCObjectHeader, link);
6109 mark_children(rt, p, gc_scan_incref_child2);
6110 }
6111}
6112
6113static void gc_free_cycles(JSRuntime *rt)
6114{
6115 struct list_head *el, *el1;
6116 JSGCObjectHeader *p;
6117#ifdef DUMP_GC_FREE
6118 BOOL header_done = FALSE;
6119#endif
6120
6121 rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
6122
6123 for(;;) {
6124 el = rt->tmp_obj_list.next;
6125 if (el == &rt->tmp_obj_list)
6126 break;
6127 p = list_entry(el, JSGCObjectHeader, link);
6128 /* Only need to free the GC object associated with JS values
6129 or async functions. The rest will be automatically removed
6130 because they must be referenced by them. */
6131 switch(p->gc_obj_type) {
6132 case JS_GC_OBJ_TYPE_JS_OBJECT:
6133 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
6134 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
6135#ifdef DUMP_GC_FREE
6136 if (!header_done) {
6137 printf("Freeing cycles:\n");
6138 JS_DumpObjectHeader(rt);
6139 header_done = TRUE;
6140 }
6141 JS_DumpGCObject(rt, p);
6142#endif
6143 free_gc_object(rt, p);
6144 break;
6145 default:
6146 list_del(&p->link);
6147 list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
6148 break;
6149 }
6150 }
6151 rt->gc_phase = JS_GC_PHASE_NONE;
6152
6153 list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
6154 p = list_entry(el, JSGCObjectHeader, link);
6155 assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
6156 p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
6157 p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
6158 if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
6159 ((JSObject *)p)->weakref_count != 0) {
6160 /* keep the object because there are weak references to it */
6161 p->mark = 0;
6162 } else {
6163 js_free_rt(rt, p);
6164 }
6165 }
6166
6167 init_list_head(&rt->gc_zero_ref_count_list);
6168}
6169
6170static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects)
6171{
6172 if (remove_weak_objects) {
6173 /* free the weakly referenced object or symbol structures, delete
6174 the associated Map/Set entries and queue the finalization
6175 registry callbacks. */
6176 gc_remove_weak_objects(rt);
6177 }
6178
6179 /* decrement the reference of the children of each object. mark =
6180 1 after this pass. */
6181 gc_decref(rt);
6182
6183 /* keep the GC objects with a non zero refcount and their childs */
6184 gc_scan(rt);
6185
6186 /* free the GC objects in a cycle */
6187 gc_free_cycles(rt);
6188}
6189
6190void JS_RunGC(JSRuntime *rt)
6191{
6192 JS_RunGCInternal(rt, TRUE);
6193}
6194
6195/* Return false if not an object or if the object has already been
6196 freed (zombie objects are visible in finalizers when freeing
6197 cycles). */
6198BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
6199{
6200 JSObject *p;
6201 if (!JS_IsObject(obj))
6202 return FALSE;
6203 p = JS_VALUE_GET_OBJ(obj);
6204 return !p->free_mark;
6205}
6206
6207/* Compute memory used by various object types */
6208/* XXX: poor man's approach to handling multiply referenced objects */
6209typedef struct JSMemoryUsage_helper {
6210 double memory_used_count;
6211 double str_count;
6212 double str_size;
6213 int64_t js_func_count;
6214 double js_func_size;
6215 int64_t js_func_code_size;
6216 int64_t js_func_pc2line_count;
6217 int64_t js_func_pc2line_size;
6218} JSMemoryUsage_helper;
6219
6220static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
6221
6222static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
6223{
6224 if (!str->atom_type) { /* atoms are handled separately */
6225 double s_ref_count = str->header.ref_count;
6226 hp->str_count += 1 / s_ref_count;
6227 hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
6228 1 - str->is_wide_char) / s_ref_count);
6229 }
6230}
6231
6232static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
6233{
6234 int memory_used_count, js_func_size, i;
6235
6236 memory_used_count = 0;
6237 js_func_size = offsetof(JSFunctionBytecode, debug);
6238 if (b->vardefs) {
6239 js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
6240 }
6241 if (b->cpool) {
6242 js_func_size += b->cpool_count * sizeof(*b->cpool);
6243 for (i = 0; i < b->cpool_count; i++) {
6244 JSValueConst val = b->cpool[i];
6245 compute_value_size(val, hp);
6246 }
6247 }
6248 if (b->closure_var) {
6249 js_func_size += b->closure_var_count * sizeof(*b->closure_var);
6250 }
6251 if (!b->read_only_bytecode && b->byte_code_buf) {
6252 hp->js_func_code_size += b->byte_code_len;
6253 }
6254 if (b->has_debug) {
6255 js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
6256 if (b->debug.source) {
6257 memory_used_count++;
6258 js_func_size += b->debug.source_len + 1;
6259 }
6260 if (b->debug.pc2line_len) {
6261 memory_used_count++;
6262 hp->js_func_pc2line_count += 1;
6263 hp->js_func_pc2line_size += b->debug.pc2line_len;
6264 }
6265 }
6266 hp->js_func_size += js_func_size;
6267 hp->js_func_count += 1;
6268 hp->memory_used_count += memory_used_count;
6269}
6270
6271static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
6272{
6273 switch(JS_VALUE_GET_TAG(val)) {
6274 case JS_TAG_STRING:
6275 compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
6276 break;
6277 case JS_TAG_BIG_INT:
6278 /* should track JSBigInt usage */
6279 break;
6280 }
6281}
6282
6283void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
6284{
6285 struct list_head *el, *el1;
6286 int i;
6287 JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
6288
6289 memset(s, 0, sizeof(*s));
6290 s->malloc_count = rt->malloc_state.malloc_count;
6291 s->malloc_size = rt->malloc_state.malloc_size;
6292 s->malloc_limit = rt->malloc_state.malloc_limit;
6293
6294 s->memory_used_count = 2; /* rt + rt->class_array */
6295 s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
6296
6297 list_for_each(el, &rt->context_list) {
6298 JSContext *ctx = list_entry(el, JSContext, link);
6299 JSShape *sh = ctx->array_shape;
6300 s->memory_used_count += 2; /* ctx + ctx->class_proto */
6301 s->memory_used_size += sizeof(JSContext) +
6302 sizeof(JSValue) * rt->class_count;
6303 s->binary_object_count += ctx->binary_object_count;
6304 s->binary_object_size += ctx->binary_object_size;
6305
6306 /* the hashed shapes are counted separately */
6307 if (sh && !sh->is_hashed) {
6308 int hash_size = sh->prop_hash_mask + 1;
6309 s->shape_count++;
6310 s->shape_size += get_shape_size(hash_size, sh->prop_size);
6311 }
6312 list_for_each(el1, &ctx->loaded_modules) {
6313 JSModuleDef *m = list_entry(el1, JSModuleDef, link);
6314 s->memory_used_count += 1;
6315 s->memory_used_size += sizeof(*m);
6316 if (m->req_module_entries) {
6317 s->memory_used_count += 1;
6318 s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
6319 }
6320 if (m->export_entries) {
6321 s->memory_used_count += 1;
6322 s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
6323 for (i = 0; i < m->export_entries_count; i++) {
6324 JSExportEntry *me = &m->export_entries[i];
6325 if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
6326 /* potential multiple count */
6327 s->memory_used_count += 1;
6328 compute_value_size(me->u.local.var_ref->value, hp);
6329 }
6330 }
6331 }
6332 if (m->star_export_entries) {
6333 s->memory_used_count += 1;
6334 s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
6335 }
6336 if (m->import_entries) {
6337 s->memory_used_count += 1;
6338 s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
6339 }
6340 compute_value_size(m->module_ns, hp);
6341 compute_value_size(m->func_obj, hp);
6342 }
6343 }
6344
6345 list_for_each(el, &rt->gc_obj_list) {
6346 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
6347 JSObject *p;
6348 JSShape *sh;
6349 JSShapeProperty *prs;
6350
6351 /* XXX: could count the other GC object types too */
6352 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
6353 compute_bytecode_size((JSFunctionBytecode *)gp, hp);
6354 continue;
6355 } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
6356 continue;
6357 }
6358 p = (JSObject *)gp;
6359 sh = p->shape;
6360 s->obj_count++;
6361 if (p->prop) {
6362 s->memory_used_count++;
6363 s->prop_size += sh->prop_size * sizeof(*p->prop);
6364 s->prop_count += sh->prop_count;
6365 prs = get_shape_prop(sh);
6366 for(i = 0; i < sh->prop_count; i++) {
6367 JSProperty *pr = &p->prop[i];
6368 if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
6369 compute_value_size(pr->u.value, hp);
6370 }
6371 prs++;
6372 }
6373 }
6374 /* the hashed shapes are counted separately */
6375 if (!sh->is_hashed) {
6376 int hash_size = sh->prop_hash_mask + 1;
6377 s->shape_count++;
6378 s->shape_size += get_shape_size(hash_size, sh->prop_size);
6379 }
6380
6381 switch(p->class_id) {
6382 case JS_CLASS_ARRAY: /* u.array | length */
6383 case JS_CLASS_ARGUMENTS: /* u.array | length */
6384 s->array_count++;
6385 if (p->fast_array) {
6386 s->fast_array_count++;
6387 if (p->u.array.u.values) {
6388 s->memory_used_count++;
6389 s->memory_used_size += p->u.array.count *
6390 sizeof(*p->u.array.u.values);
6391 s->fast_array_elements += p->u.array.count;
6392 for (i = 0; i < p->u.array.count; i++) {
6393 compute_value_size(p->u.array.u.values[i], hp);
6394 }
6395 }
6396 }
6397 break;
6398 case JS_CLASS_NUMBER: /* u.object_data */
6399 case JS_CLASS_STRING: /* u.object_data */
6400 case JS_CLASS_BOOLEAN: /* u.object_data */
6401 case JS_CLASS_SYMBOL: /* u.object_data */
6402 case JS_CLASS_DATE: /* u.object_data */
6403 case JS_CLASS_BIG_INT: /* u.object_data */
6404 compute_value_size(p->u.object_data, hp);
6405 break;
6406 case JS_CLASS_C_FUNCTION: /* u.cfunc */
6407 s->c_func_count++;
6408 break;
6409 case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
6410 {
6411 JSFunctionBytecode *b = p->u.func.function_bytecode;
6412 JSVarRef **var_refs = p->u.func.var_refs;
6413 /* home_object: object will be accounted for in list scan */
6414 if (var_refs) {
6415 s->memory_used_count++;
6416 s->js_func_size += b->closure_var_count * sizeof(*var_refs);
6417 for (i = 0; i < b->closure_var_count; i++) {
6418 if (var_refs[i]) {
6419 double ref_count = var_refs[i]->header.ref_count;
6420 s->memory_used_count += 1 / ref_count;
6421 s->js_func_size += sizeof(*var_refs[i]) / ref_count;
6422 /* handle non object closed values */
6423 if (var_refs[i]->pvalue == &var_refs[i]->value) {
6424 /* potential multiple count */
6425 compute_value_size(var_refs[i]->value, hp);
6426 }
6427 }
6428 }
6429 }
6430 }
6431 break;
6432 case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */
6433 {
6434 JSBoundFunction *bf = p->u.bound_function;
6435 /* func_obj and this_val are objects */
6436 for (i = 0; i < bf->argc; i++) {
6437 compute_value_size(bf->argv[i], hp);
6438 }
6439 s->memory_used_count += 1;
6440 s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
6441 }
6442 break;
6443 case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */
6444 {
6445 JSCFunctionDataRecord *fd = p->u.c_function_data_record;
6446 if (fd) {
6447 for (i = 0; i < fd->data_len; i++) {
6448 compute_value_size(fd->data[i], hp);
6449 }
6450 s->memory_used_count += 1;
6451 s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
6452 }
6453 }
6454 break;
6455 case JS_CLASS_REGEXP: /* u.regexp */
6456 compute_jsstring_size(p->u.regexp.pattern, hp);
6457 compute_jsstring_size(p->u.regexp.bytecode, hp);
6458 break;
6459
6460 case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
6461 {
6462 JSForInIterator *it = p->u.for_in_iterator;
6463 if (it) {
6464 compute_value_size(it->obj, hp);
6465 s->memory_used_count += 1;
6466 s->memory_used_size += sizeof(*it);
6467 }
6468 }
6469 break;
6470 case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */
6471 case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
6472 {
6473 JSArrayBuffer *abuf = p->u.array_buffer;
6474 if (abuf) {
6475 s->memory_used_count += 1;
6476 s->memory_used_size += sizeof(*abuf);
6477 if (abuf->data) {
6478 s->memory_used_count += 1;
6479 s->memory_used_size += abuf->byte_length;
6480 }
6481 }
6482 }
6483 break;
6484 case JS_CLASS_GENERATOR: /* u.generator_data */
6485 case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */
6486 case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */
6487 case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */
6488 case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */
6489 case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */
6490 case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */
6491 case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */
6492 case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */
6493 case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */
6494 case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
6495 case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
6496 case JS_CLASS_DATAVIEW: /* u.typed_array */
6497 case JS_CLASS_MAP: /* u.map_state */
6498 case JS_CLASS_SET: /* u.map_state */
6499 case JS_CLASS_WEAKMAP: /* u.map_state */
6500 case JS_CLASS_WEAKSET: /* u.map_state */
6501 case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
6502 case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */
6503 case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */
6504 case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */
6505 case JS_CLASS_PROXY: /* u.proxy_data */
6506 case JS_CLASS_PROMISE: /* u.promise_data */
6507 case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */
6508 case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */
6509 case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */
6510 case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */
6511 case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */
6512 case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */
6513 /* TODO */
6514 default:
6515 /* XXX: class definition should have an opaque block size */
6516 if (p->u.opaque) {
6517 s->memory_used_count += 1;
6518 }
6519 break;
6520 }
6521 }
6522 s->obj_size += s->obj_count * sizeof(JSObject);
6523
6524 /* hashed shapes */
6525 s->memory_used_count++; /* rt->shape_hash */
6526 s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
6527 for(i = 0; i < rt->shape_hash_size; i++) {
6528 JSShape *sh;
6529 for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
6530 int hash_size = sh->prop_hash_mask + 1;
6531 s->shape_count++;
6532 s->shape_size += get_shape_size(hash_size, sh->prop_size);
6533 }
6534 }
6535
6536 /* atoms */
6537 s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
6538 s->atom_count = rt->atom_count;
6539 s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
6540 sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
6541 for(i = 0; i < rt->atom_size; i++) {
6542 JSAtomStruct *p = rt->atom_array[i];
6543 if (!atom_is_free(p)) {
6544 s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
6545 1 - p->is_wide_char);
6546 }
6547 }
6548 s->str_count = round(mem.str_count);
6549 s->str_size = round(mem.str_size);
6550 s->js_func_count = mem.js_func_count;
6551 s->js_func_size = round(mem.js_func_size);
6552 s->js_func_code_size = mem.js_func_code_size;
6553 s->js_func_pc2line_count = mem.js_func_pc2line_count;
6554 s->js_func_pc2line_size = mem.js_func_pc2line_size;
6555 s->memory_used_count += round(mem.memory_used_count) +
6556 s->atom_count + s->str_count +
6557 s->obj_count + s->shape_count +
6558 s->js_func_count + s->js_func_pc2line_count;
6559 s->memory_used_size += s->atom_size + s->str_size +
6560 s->obj_size + s->prop_size + s->shape_size +
6561 s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
6562}
6563
6564void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
6565{
6566 fprintf(fp, "QuickJS memory usage -- " CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
6567 (int)sizeof(void *) * 8, s->malloc_limit);
6568#if 1
6569 if (rt) {
6570 static const struct {
6571 const char *name;
6572 size_t size;
6573 } object_types[] = {
6574 { "JSRuntime", sizeof(JSRuntime) },
6575 { "JSContext", sizeof(JSContext) },
6576 { "JSObject", sizeof(JSObject) },
6577 { "JSString", sizeof(JSString) },
6578 { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
6579 };
6580 int i, usage_size_ok = 0;
6581 for(i = 0; i < countof(object_types); i++) {
6582 unsigned int size = object_types[i].size;
6583 void *p = js_malloc_rt(rt, size);
6584 if (p) {
6585 unsigned int size1 = js_malloc_usable_size_rt(rt, p);
6586 if (size1 >= size) {
6587 usage_size_ok = 1;
6588 fprintf(fp, " %3u + %-2u %s\n",
6589 size, size1 - size, object_types[i].name);
6590 }
6591 js_free_rt(rt, p);
6592 }
6593 }
6594 if (!usage_size_ok) {
6595 fprintf(fp, " malloc_usable_size unavailable\n");
6596 }
6597 {
6598 int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
6599 int class_id;
6600 struct list_head *el;
6601 list_for_each(el, &rt->gc_obj_list) {
6602 JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
6603 JSObject *p;
6604 if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
6605 p = (JSObject *)gp;
6606 obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
6607 }
6608 }
6609 fprintf(fp, "\n" "JSObject classes\n");
6610 if (obj_classes[0])
6611 fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
6612 for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
6613 if (obj_classes[class_id] && class_id < rt->class_count) {
6614 char buf[ATOM_GET_STR_BUF_SIZE];
6615 fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
6616 JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name));
6617 }
6618 }
6619 if (obj_classes[JS_CLASS_INIT_COUNT])
6620 fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
6621 }
6622 fprintf(fp, "\n");
6623 }
6624#endif
6625 fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
6626
6627 if (s->malloc_count) {
6628 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
6629 "memory allocated", s->malloc_count, s->malloc_size,
6630 (double)s->malloc_size / s->malloc_count);
6631 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
6632 "memory used", s->memory_used_count, s->memory_used_size,
6633 MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
6634 s->memory_used_count));
6635 }
6636 if (s->atom_count) {
6637 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
6638 "atoms", s->atom_count, s->atom_size,
6639 (double)s->atom_size / s->atom_count);
6640 }
6641 if (s->str_count) {
6642 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
6643 "strings", s->str_count, s->str_size,
6644 (double)s->str_size / s->str_count);
6645 }
6646 if (s->obj_count) {
6647 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
6648 "objects", s->obj_count, s->obj_size,
6649 (double)s->obj_size / s->obj_count);
6650 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
6651 " properties", s->prop_count, s->prop_size,
6652 (double)s->prop_count / s->obj_count);
6653 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
6654 " shapes", s->shape_count, s->shape_size,
6655 (double)s->shape_size / s->shape_count);
6656 }
6657 if (s->js_func_count) {
6658 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6659 "bytecode functions", s->js_func_count, s->js_func_size);
6660 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
6661 " bytecode", s->js_func_count, s->js_func_code_size,
6662 (double)s->js_func_code_size / s->js_func_count);
6663 if (s->js_func_pc2line_count) {
6664 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
6665 " pc2line", s->js_func_pc2line_count,
6666 s->js_func_pc2line_size,
6667 (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
6668 }
6669 }
6670 if (s->c_func_count) {
6671 fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
6672 }
6673 if (s->array_count) {
6674 fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
6675 if (s->fast_array_count) {
6676 fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
6677 fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
6678 " elements", s->fast_array_elements,
6679 s->fast_array_elements * (int)sizeof(JSValue),
6680 (double)s->fast_array_elements / s->fast_array_count);
6681 }
6682 }
6683 if (s->binary_object_count) {
6684 fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
6685 "binary objects", s->binary_object_count, s->binary_object_size);
6686 }
6687}
6688
6689JSValue JS_GetGlobalObject(JSContext *ctx)
6690{
6691 return JS_DupValue(ctx, ctx->global_obj);
6692}
6693
6694/* WARNING: obj is freed */
6695JSValue JS_Throw(JSContext *ctx, JSValue obj)
6696{
6697 JSRuntime *rt = ctx->rt;
6698 JS_FreeValue(ctx, rt->current_exception);
6699 rt->current_exception = obj;
6700 return JS_EXCEPTION;
6701}
6702
6703/* return the pending exception (cannot be called twice). */
6704JSValue JS_GetException(JSContext *ctx)
6705{
6706 JSValue val;
6707 JSRuntime *rt = ctx->rt;
6708 val = rt->current_exception;
6709 rt->current_exception = JS_UNINITIALIZED;
6710 return val;
6711}
6712
6713JS_BOOL JS_HasException(JSContext *ctx)
6714{
6715 return !JS_IsUninitialized(ctx->rt->current_exception);
6716}
6717
6718static void dbuf_put_leb128(DynBuf *s, uint32_t v)
6719{
6720 uint32_t a;
6721 for(;;) {
6722 a = v & 0x7f;
6723 v >>= 7;
6724 if (v != 0) {
6725 dbuf_putc(s, a | 0x80);
6726 } else {
6727 dbuf_putc(s, a);
6728 break;
6729 }
6730 }
6731}
6732
6733static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
6734{
6735 uint32_t v = v1;
6736 dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
6737}
6738
6739static int get_leb128(uint32_t *pval, const uint8_t *buf,
6740 const uint8_t *buf_end)
6741{
6742 const uint8_t *ptr = buf;
6743 uint32_t v, a, i;
6744 v = 0;
6745 for(i = 0; i < 5; i++) {
6746 if (unlikely(ptr >= buf_end))
6747 break;
6748 a = *ptr++;
6749 v |= (a & 0x7f) << (i * 7);
6750 if (!(a & 0x80)) {
6751 *pval = v;
6752 return ptr - buf;
6753 }
6754 }
6755 *pval = 0;
6756 return -1;
6757}
6758
6759static int get_sleb128(int32_t *pval, const uint8_t *buf,
6760 const uint8_t *buf_end)
6761{
6762 int ret;
6763 uint32_t val;
6764 ret = get_leb128(&val, buf, buf_end);
6765 if (ret < 0) {
6766 *pval = 0;
6767 return -1;
6768 }
6769 *pval = (val >> 1) ^ -(val & 1);
6770 return ret;
6771}
6772
6773/* use pc_value = -1 to get the position of the function definition */
6774static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
6775 uint32_t pc_value, int *pcol_num)
6776{
6777 const uint8_t *p_end, *p;
6778 int new_line_num, line_num, pc, v, ret, new_col_num, col_num;
6779 uint32_t val;
6780 unsigned int op;
6781
6782 if (!b->has_debug || !b->debug.pc2line_buf)
6783 goto fail; /* function was stripped */
6784
6785 p = b->debug.pc2line_buf;
6786 p_end = p + b->debug.pc2line_len;
6787
6788 /* get the function line and column numbers */
6789 ret = get_leb128(&val, p, p_end);
6790 if (ret < 0)
6791 goto fail;
6792 p += ret;
6793 line_num = val + 1;
6794
6795 ret = get_leb128(&val, p, p_end);
6796 if (ret < 0)
6797 goto fail;
6798 p += ret;
6799 col_num = val + 1;
6800
6801 if (pc_value != -1) {
6802 pc = 0;
6803 while (p < p_end) {
6804 op = *p++;
6805 if (op == 0) {
6806 ret = get_leb128(&val, p, p_end);
6807 if (ret < 0)
6808 goto fail;
6809 pc += val;
6810 p += ret;
6811 ret = get_sleb128(&v, p, p_end);
6812 if (ret < 0)
6813 goto fail;
6814 p += ret;
6815 new_line_num = line_num + v;
6816 } else {
6817 op -= PC2LINE_OP_FIRST;
6818 pc += (op / PC2LINE_RANGE);
6819 new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
6820 }
6821 ret = get_sleb128(&v, p, p_end);
6822 if (ret < 0)
6823 goto fail;
6824 p += ret;
6825 new_col_num = col_num + v;
6826
6827 if (pc_value < pc)
6828 goto done;
6829 line_num = new_line_num;
6830 col_num = new_col_num;
6831 }
6832 }
6833 done:
6834 *pcol_num = col_num;
6835 return line_num;
6836 fail:
6837 *pcol_num = 0;
6838 return 0;
6839}
6840
6841/* in order to avoid executing arbitrary code during the stack trace
6842 generation, we only look at simple 'name' properties containing a
6843 string. */
6844static const char *get_func_name(JSContext *ctx, JSValueConst func)
6845{
6846 JSProperty *pr;
6847 JSShapeProperty *prs;
6848 JSValueConst val;
6849
6850 if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
6851 return NULL;
6852 prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
6853 if (!prs)
6854 return NULL;
6855 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
6856 return NULL;
6857 val = pr->u.value;
6858 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
6859 return NULL;
6860 return JS_ToCString(ctx, val);
6861}
6862
6863#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
6864
6865/* if filename != NULL, an additional level is added with the filename
6866 and line number information (used for parse error). */
6867static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
6868 const char *filename, int line_num, int col_num,
6869 int backtrace_flags)
6870{
6871 JSStackFrame *sf;
6872 JSValue str;
6873 DynBuf dbuf;
6874 const char *func_name_str;
6875 const char *str1;
6876 JSObject *p;
6877
6878 js_dbuf_init(ctx, &dbuf);
6879 if (filename) {
6880 dbuf_printf(&dbuf, " at %s", filename);
6881 if (line_num != -1)
6882 dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
6883 dbuf_putc(&dbuf, '\n');
6884 str = JS_NewString(ctx, filename);
6885 /* Note: SpiderMonkey does that, could update once there is a standard */
6886 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
6887 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6888 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
6889 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6890 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_columnNumber, JS_NewInt32(ctx, col_num),
6891 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6892 }
6893 for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
6894 if (sf->js_mode & JS_MODE_BACKTRACE_BARRIER)
6895 break;
6896 if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
6897 backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
6898 continue;
6899 }
6900 func_name_str = get_func_name(ctx, sf->cur_func);
6901 if (!func_name_str || func_name_str[0] == '\0')
6902 str1 = "<anonymous>";
6903 else
6904 str1 = func_name_str;
6905 dbuf_printf(&dbuf, " at %s", str1);
6906 JS_FreeCString(ctx, func_name_str);
6907
6908 p = JS_VALUE_GET_OBJ(sf->cur_func);
6909 if (js_class_has_bytecode(p->class_id)) {
6910 JSFunctionBytecode *b;
6911 const char *atom_str;
6912 int line_num1, col_num1;
6913
6914 b = p->u.func.function_bytecode;
6915 if (b->has_debug) {
6916 line_num1 = find_line_num(ctx, b,
6917 sf->cur_pc - b->byte_code_buf - 1, &col_num1);
6918 atom_str = JS_AtomToCString(ctx, b->debug.filename);
6919 dbuf_printf(&dbuf, " (%s",
6920 atom_str ? atom_str : "<null>");
6921 JS_FreeCString(ctx, atom_str);
6922 if (line_num1 != 0)
6923 dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
6924 dbuf_putc(&dbuf, ')');
6925 }
6926 } else {
6927 dbuf_printf(&dbuf, " (native)");
6928 }
6929 dbuf_putc(&dbuf, '\n');
6930 }
6931 dbuf_putc(&dbuf, '\0');
6932 if (dbuf_error(&dbuf))
6933 str = JS_NULL;
6934 else
6935 str = JS_NewString(ctx, (char *)dbuf.buf);
6936 dbuf_free(&dbuf);
6937 JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
6938 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6939}
6940
6941/* Note: it is important that no exception is returned by this function */
6942static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
6943{
6944 JSObject *p;
6945 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
6946 return FALSE;
6947 p = JS_VALUE_GET_OBJ(obj);
6948 if (p->class_id != JS_CLASS_ERROR)
6949 return FALSE;
6950 if (find_own_property1(p, JS_ATOM_stack))
6951 return FALSE;
6952 return TRUE;
6953}
6954
6955JSValue JS_NewError(JSContext *ctx)
6956{
6957 return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
6958}
6959
6960static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
6961 const char *fmt, va_list ap, BOOL add_backtrace)
6962{
6963 char buf[256];
6964 JSValue obj, ret;
6965
6966 vsnprintf(buf, sizeof(buf), fmt, ap);
6967 obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
6968 JS_CLASS_ERROR);
6969 if (unlikely(JS_IsException(obj))) {
6970 /* out of memory: throw JS_NULL to avoid recursing */
6971 obj = JS_NULL;
6972 } else {
6973 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
6974 JS_NewString(ctx, buf),
6975 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
6976 }
6977 if (add_backtrace) {
6978 build_backtrace(ctx, obj, NULL, 0, 0, 0);
6979 }
6980 ret = JS_Throw(ctx, obj);
6981 return ret;
6982}
6983
6984static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
6985 const char *fmt, va_list ap)
6986{
6987 JSRuntime *rt = ctx->rt;
6988 JSStackFrame *sf;
6989 BOOL add_backtrace;
6990
6991 /* the backtrace is added later if called from a bytecode function */
6992 sf = rt->current_stack_frame;
6993 add_backtrace = !rt->in_out_of_memory &&
6994 (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
6995 return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
6996}
6997
6998JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
6999{
7000 JSValue val;
7001 va_list ap;
7002
7003 va_start(ap, fmt);
7004 val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
7005 va_end(ap);
7006 return val;
7007}
7008
7009JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
7010{
7011 JSValue val;
7012 va_list ap;
7013
7014 va_start(ap, fmt);
7015 val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
7016 va_end(ap);
7017 return val;
7018}
7019
7020static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
7021{
7022 va_list ap;
7023
7024 if ((flags & JS_PROP_THROW) ||
7025 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
7026 va_start(ap, fmt);
7027 JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
7028 va_end(ap);
7029 return -1;
7030 } else {
7031 return FALSE;
7032 }
7033}
7034
7035/* never use it directly */
7036static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
7037{
7038 char buf[ATOM_GET_STR_BUF_SIZE];
7039 return JS_ThrowTypeError(ctx, fmt,
7040 JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
7041}
7042
7043/* never use it directly */
7044static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
7045{
7046 char buf[ATOM_GET_STR_BUF_SIZE];
7047 return JS_ThrowSyntaxError(ctx, fmt,
7048 JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
7049}
7050
7051/* %s is replaced by 'atom'. The macro is used so that gcc can check
7052 the format string. */
7053#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
7054#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
7055
7056static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
7057{
7058 if ((flags & JS_PROP_THROW) ||
7059 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
7060 JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
7061 return -1;
7062 } else {
7063 return FALSE;
7064 }
7065}
7066
7067JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
7068{
7069 JSValue val;
7070 va_list ap;
7071
7072 va_start(ap, fmt);
7073 val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
7074 va_end(ap);
7075 return val;
7076}
7077
7078JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
7079{
7080 JSValue val;
7081 va_list ap;
7082
7083 va_start(ap, fmt);
7084 val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
7085 va_end(ap);
7086 return val;
7087}
7088
7089JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
7090{
7091 JSValue val;
7092 va_list ap;
7093
7094 va_start(ap, fmt);
7095 val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
7096 va_end(ap);
7097 return val;
7098}
7099
7100JSValue JS_ThrowOutOfMemory(JSContext *ctx)
7101{
7102 JSRuntime *rt = ctx->rt;
7103 if (!rt->in_out_of_memory) {
7104 rt->in_out_of_memory = TRUE;
7105 JS_ThrowInternalError(ctx, "out of memory");
7106 rt->in_out_of_memory = FALSE;
7107 }
7108 return JS_EXCEPTION;
7109}
7110
7111static JSValue JS_ThrowStackOverflow(JSContext *ctx)
7112{
7113 return JS_ThrowInternalError(ctx, "stack overflow");
7114}
7115
7116static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
7117{
7118 return JS_ThrowTypeError(ctx, "not an object");
7119}
7120
7121static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
7122{
7123 return JS_ThrowTypeError(ctx, "not a symbol");
7124}
7125
7126static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
7127{
7128 char buf[ATOM_GET_STR_BUF_SIZE];
7129 return JS_ThrowReferenceError(ctx, "'%s' is not defined",
7130 JS_AtomGetStr(ctx, buf, sizeof(buf), name));
7131}
7132
7133static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
7134{
7135 char buf[ATOM_GET_STR_BUF_SIZE];
7136 return JS_ThrowReferenceError(ctx, "%s is not initialized",
7137 name == JS_ATOM_NULL ? "lexical variable" :
7138 JS_AtomGetStr(ctx, buf, sizeof(buf), name));
7139}
7140
7141static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
7142 JSFunctionBytecode *b,
7143 int idx, BOOL is_ref)
7144{
7145 JSAtom atom = JS_ATOM_NULL;
7146 if (is_ref) {
7147 atom = b->closure_var[idx].var_name;
7148 } else {
7149 /* not present if the function is stripped and contains no eval() */
7150 if (b->vardefs)
7151 atom = b->vardefs[b->arg_count + idx].var_name;
7152 }
7153 return JS_ThrowReferenceErrorUninitialized(ctx, atom);
7154}
7155
7156static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
7157{
7158 JSRuntime *rt = ctx->rt;
7159 JSAtom name;
7160 name = rt->class_array[class_id].class_name;
7161 return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
7162}
7163
7164static void JS_ThrowInterrupted(JSContext *ctx)
7165{
7166 JS_ThrowInternalError(ctx, "interrupted");
7167 JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
7168}
7169
7170static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
7171{
7172 JSRuntime *rt = ctx->rt;
7173 ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
7174 if (rt->interrupt_handler) {
7175 if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
7176 JS_ThrowInterrupted(ctx);
7177 return -1;
7178 }
7179 }
7180 return 0;
7181}
7182
7183static inline __exception int js_poll_interrupts(JSContext *ctx)
7184{
7185 if (unlikely(--ctx->interrupt_counter <= 0)) {
7186 return __js_poll_interrupts(ctx);
7187 } else {
7188 return 0;
7189 }
7190}
7191
7192/* Return -1 (exception) or TRUE/FALSE. 'throw_flag' = FALSE indicates
7193 that it is called from Reflect.setPrototypeOf(). */
7194static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
7195 JSValueConst proto_val,
7196 BOOL throw_flag)
7197{
7198 JSObject *proto, *p, *p1;
7199 JSShape *sh;
7200
7201 if (throw_flag) {
7202 if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
7203 JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
7204 goto not_obj;
7205 } else {
7206 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
7207 goto not_obj;
7208 }
7209 p = JS_VALUE_GET_OBJ(obj);
7210 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
7211 if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
7212 not_obj:
7213 JS_ThrowTypeErrorNotAnObject(ctx);
7214 return -1;
7215 }
7216 proto = NULL;
7217 } else {
7218 proto = JS_VALUE_GET_OBJ(proto_val);
7219 }
7220
7221 if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
7222 return TRUE;
7223
7224 if (unlikely(p->is_exotic)) {
7225 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7226 int ret;
7227 if (em && em->set_prototype) {
7228 ret = em->set_prototype(ctx, obj, proto_val);
7229 if (ret == 0 && throw_flag) {
7230 JS_ThrowTypeError(ctx, "proxy: bad prototype");
7231 return -1;
7232 } else {
7233 return ret;
7234 }
7235 }
7236 }
7237
7238 sh = p->shape;
7239 if (sh->proto == proto)
7240 return TRUE;
7241 if (!p->extensible) {
7242 if (throw_flag) {
7243 JS_ThrowTypeError(ctx, "object is not extensible");
7244 return -1;
7245 } else {
7246 return FALSE;
7247 }
7248 }
7249 if (proto) {
7250 /* check if there is a cycle */
7251 p1 = proto;
7252 do {
7253 if (p1 == p) {
7254 if (throw_flag) {
7255 JS_ThrowTypeError(ctx, "circular prototype chain");
7256 return -1;
7257 } else {
7258 return FALSE;
7259 }
7260 }
7261 /* Note: for Proxy objects, proto is NULL */
7262 p1 = p1->shape->proto;
7263 } while (p1 != NULL);
7264 JS_DupValue(ctx, proto_val);
7265 }
7266
7267 if (js_shape_prepare_update(ctx, p, NULL))
7268 return -1;
7269 sh = p->shape;
7270 if (sh->proto)
7271 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
7272 sh->proto = proto;
7273 return TRUE;
7274}
7275
7276/* return -1 (exception) or TRUE/FALSE */
7277int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
7278{
7279 return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
7280}
7281
7282/* Only works for primitive types, otherwise return JS_NULL. */
7283static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
7284{
7285 switch(JS_VALUE_GET_NORM_TAG(val)) {
7286 case JS_TAG_SHORT_BIG_INT:
7287 case JS_TAG_BIG_INT:
7288 val = ctx->class_proto[JS_CLASS_BIG_INT];
7289 break;
7290 case JS_TAG_INT:
7291 case JS_TAG_FLOAT64:
7292 val = ctx->class_proto[JS_CLASS_NUMBER];
7293 break;
7294 case JS_TAG_BOOL:
7295 val = ctx->class_proto[JS_CLASS_BOOLEAN];
7296 break;
7297 case JS_TAG_STRING:
7298 case JS_TAG_STRING_ROPE:
7299 val = ctx->class_proto[JS_CLASS_STRING];
7300 break;
7301 case JS_TAG_SYMBOL:
7302 val = ctx->class_proto[JS_CLASS_SYMBOL];
7303 break;
7304 case JS_TAG_OBJECT:
7305 case JS_TAG_NULL:
7306 case JS_TAG_UNDEFINED:
7307 default:
7308 val = JS_NULL;
7309 break;
7310 }
7311 return val;
7312}
7313
7314/* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */
7315JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
7316{
7317 JSValue val;
7318 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
7319 JSObject *p;
7320 p = JS_VALUE_GET_OBJ(obj);
7321 if (unlikely(p->is_exotic)) {
7322 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7323 if (em && em->get_prototype) {
7324 return em->get_prototype(ctx, obj);
7325 }
7326 }
7327 p = p->shape->proto;
7328 if (!p)
7329 val = JS_NULL;
7330 else
7331 val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7332 } else {
7333 val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
7334 }
7335 return val;
7336}
7337
7338static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
7339{
7340 JSValue obj1;
7341 obj1 = JS_GetPrototype(ctx, obj);
7342 JS_FreeValue(ctx, obj);
7343 return obj1;
7344}
7345
7346/* return TRUE, FALSE or (-1) in case of exception */
7347static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
7348 JSValueConst obj)
7349{
7350 JSValue obj_proto;
7351 JSObject *proto;
7352 const JSObject *p, *proto1;
7353 BOOL ret;
7354
7355 if (!JS_IsFunction(ctx, obj))
7356 return FALSE;
7357 p = JS_VALUE_GET_OBJ(obj);
7358 if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
7359 JSBoundFunction *s = p->u.bound_function;
7360 return JS_IsInstanceOf(ctx, val, s->func_obj);
7361 }
7362
7363 /* Only explicitly boxed values are instances of constructors */
7364 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
7365 return FALSE;
7366 obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
7367 if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
7368 if (!JS_IsException(obj_proto))
7369 JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
7370 ret = -1;
7371 goto done;
7372 }
7373 proto = JS_VALUE_GET_OBJ(obj_proto);
7374 p = JS_VALUE_GET_OBJ(val);
7375 for(;;) {
7376 proto1 = p->shape->proto;
7377 if (!proto1) {
7378 /* slow case if exotic object in the prototype chain */
7379 if (unlikely(p->is_exotic && !p->fast_array)) {
7380 JSValue obj1;
7381 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
7382 for(;;) {
7383 obj1 = JS_GetPrototypeFree(ctx, obj1);
7384 if (JS_IsException(obj1)) {
7385 ret = -1;
7386 break;
7387 }
7388 if (JS_IsNull(obj1)) {
7389 ret = FALSE;
7390 break;
7391 }
7392 if (proto == JS_VALUE_GET_OBJ(obj1)) {
7393 JS_FreeValue(ctx, obj1);
7394 ret = TRUE;
7395 break;
7396 }
7397 /* must check for timeout to avoid infinite loop */
7398 if (js_poll_interrupts(ctx)) {
7399 JS_FreeValue(ctx, obj1);
7400 ret = -1;
7401 break;
7402 }
7403 }
7404 } else {
7405 ret = FALSE;
7406 }
7407 break;
7408 }
7409 p = proto1;
7410 if (proto == p) {
7411 ret = TRUE;
7412 break;
7413 }
7414 }
7415done:
7416 JS_FreeValue(ctx, obj_proto);
7417 return ret;
7418}
7419
7420/* return TRUE, FALSE or (-1) in case of exception */
7421int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
7422{
7423 JSValue method;
7424
7425 if (!JS_IsObject(obj))
7426 goto fail;
7427 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
7428 if (JS_IsException(method))
7429 return -1;
7430 if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
7431 JSValue ret;
7432 ret = JS_CallFree(ctx, method, obj, 1, &val);
7433 return JS_ToBoolFree(ctx, ret);
7434 }
7435
7436 /* legacy case */
7437 if (!JS_IsFunction(ctx, obj)) {
7438 fail:
7439 JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
7440 return -1;
7441 }
7442 return JS_OrdinaryIsInstanceOf(ctx, val, obj);
7443}
7444
7445/* return the value associated to the autoinit property or an exception */
7446typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
7447
7448static JSAutoInitFunc *js_autoinit_func_table[] = {
7449 js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
7450 js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
7451 JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
7452};
7453
7454/* warning: 'prs' is reallocated after it */
7455static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
7456 JSProperty *pr, JSShapeProperty *prs)
7457{
7458 JSValue val;
7459 JSContext *realm;
7460 JSAutoInitFunc *func;
7461 JSAutoInitIDEnum id;
7462
7463 if (js_shape_prepare_update(ctx, p, &prs))
7464 return -1;
7465
7466 realm = js_autoinit_get_realm(pr);
7467 id = js_autoinit_get_id(pr);
7468 func = js_autoinit_func_table[id];
7469 /* 'func' shall not modify the object properties 'pr' */
7470 val = func(realm, p, prop, pr->u.init.opaque);
7471 js_autoinit_free(ctx->rt, pr);
7472 prs->flags &= ~JS_PROP_TMASK;
7473 pr->u.value = JS_UNDEFINED;
7474 if (JS_IsException(val))
7475 return -1;
7476 if (id == JS_AUTOINIT_ID_MODULE_NS &&
7477 JS_VALUE_GET_TAG(val) == JS_TAG_STRING) {
7478 /* WARNING: a varref is returned as a string ! */
7479 prs->flags |= JS_PROP_VARREF;
7480 pr->u.var_ref = JS_VALUE_GET_PTR(val);
7481 pr->u.var_ref->header.ref_count++;
7482 } else {
7483 pr->u.value = val;
7484 }
7485 return 0;
7486}
7487
7488JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
7489 JSAtom prop, JSValueConst this_obj,
7490 BOOL throw_ref_error)
7491{
7492 JSObject *p;
7493 JSProperty *pr;
7494 JSShapeProperty *prs;
7495 uint32_t tag;
7496
7497 tag = JS_VALUE_GET_TAG(obj);
7498 if (unlikely(tag != JS_TAG_OBJECT)) {
7499 switch(tag) {
7500 case JS_TAG_NULL:
7501 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
7502 case JS_TAG_UNDEFINED:
7503 return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
7504 case JS_TAG_EXCEPTION:
7505 return JS_EXCEPTION;
7506 case JS_TAG_STRING:
7507 {
7508 JSString *p1 = JS_VALUE_GET_STRING(obj);
7509 if (__JS_AtomIsTaggedInt(prop)) {
7510 uint32_t idx;
7511 idx = __JS_AtomToUInt32(prop);
7512 if (idx < p1->len) {
7513 return js_new_string_char(ctx, string_get(p1, idx));
7514 }
7515 } else if (prop == JS_ATOM_length) {
7516 return JS_NewInt32(ctx, p1->len);
7517 }
7518 }
7519 break;
7520 case JS_TAG_STRING_ROPE:
7521 {
7522 JSStringRope *p1 = JS_VALUE_GET_STRING_ROPE(obj);
7523 if (__JS_AtomIsTaggedInt(prop)) {
7524 uint32_t idx;
7525 idx = __JS_AtomToUInt32(prop);
7526 if (idx < p1->len) {
7527 return js_new_string_char(ctx, string_rope_get(obj, idx));
7528 }
7529 } else if (prop == JS_ATOM_length) {
7530 return JS_NewInt32(ctx, p1->len);
7531 }
7532 }
7533 break;
7534 default:
7535 break;
7536 }
7537 /* cannot raise an exception */
7538 p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
7539 if (!p)
7540 return JS_UNDEFINED;
7541 } else {
7542 p = JS_VALUE_GET_OBJ(obj);
7543 }
7544
7545 for(;;) {
7546 prs = find_own_property(&pr, p, prop);
7547 if (prs) {
7548 /* found */
7549 if (unlikely(prs->flags & JS_PROP_TMASK)) {
7550 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
7551 if (unlikely(!pr->u.getset.getter)) {
7552 return JS_UNDEFINED;
7553 } else {
7554 JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
7555 /* Note: the field could be removed in the getter */
7556 func = JS_DupValue(ctx, func);
7557 return JS_CallFree(ctx, func, this_obj, 0, NULL);
7558 }
7559 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
7560 JSValue val = *pr->u.var_ref->pvalue;
7561 if (unlikely(JS_IsUninitialized(val)))
7562 return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7563 return JS_DupValue(ctx, val);
7564 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
7565 /* Instantiate property and retry */
7566 if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
7567 return JS_EXCEPTION;
7568 continue;
7569 }
7570 } else {
7571 return JS_DupValue(ctx, pr->u.value);
7572 }
7573 }
7574 if (unlikely(p->is_exotic)) {
7575 /* exotic behaviors */
7576 if (p->fast_array) {
7577 if (__JS_AtomIsTaggedInt(prop)) {
7578 uint32_t idx = __JS_AtomToUInt32(prop);
7579 if (idx < p->u.array.count) {
7580 /* we avoid duplicating the code */
7581 return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
7582 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7583 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7584 return JS_UNDEFINED;
7585 }
7586 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
7587 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
7588 int ret;
7589 ret = JS_AtomIsNumericIndex(ctx, prop);
7590 if (ret != 0) {
7591 if (ret < 0)
7592 return JS_EXCEPTION;
7593 return JS_UNDEFINED;
7594 }
7595 }
7596 } else {
7597 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7598 if (em) {
7599 if (em->get_property) {
7600 JSValue obj1, retval;
7601 /* XXX: should pass throw_ref_error */
7602 /* Note: if 'p' is a prototype, it can be
7603 freed in the called function */
7604 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7605 retval = em->get_property(ctx, obj1, prop, this_obj);
7606 JS_FreeValue(ctx, obj1);
7607 return retval;
7608 }
7609 if (em->get_own_property) {
7610 JSPropertyDescriptor desc;
7611 int ret;
7612 JSValue obj1;
7613
7614 /* Note: if 'p' is a prototype, it can be
7615 freed in the called function */
7616 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7617 ret = em->get_own_property(ctx, &desc, obj1, prop);
7618 JS_FreeValue(ctx, obj1);
7619 if (ret < 0)
7620 return JS_EXCEPTION;
7621 if (ret) {
7622 if (desc.flags & JS_PROP_GETSET) {
7623 JS_FreeValue(ctx, desc.setter);
7624 return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
7625 } else {
7626 return desc.value;
7627 }
7628 }
7629 }
7630 }
7631 }
7632 }
7633 p = p->shape->proto;
7634 if (!p)
7635 break;
7636 }
7637 if (unlikely(throw_ref_error)) {
7638 return JS_ThrowReferenceErrorNotDefined(ctx, prop);
7639 } else {
7640 return JS_UNDEFINED;
7641 }
7642}
7643
7644static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
7645{
7646 return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
7647 atom);
7648}
7649
7650/* Private fields can be added even on non extensible objects or
7651 Proxies */
7652static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
7653 JSValueConst name, JSValue val)
7654{
7655 JSObject *p;
7656 JSShapeProperty *prs;
7657 JSProperty *pr;
7658 JSAtom prop;
7659
7660 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7661 JS_ThrowTypeErrorNotAnObject(ctx);
7662 goto fail;
7663 }
7664 /* safety check */
7665 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7666 JS_ThrowTypeErrorNotASymbol(ctx);
7667 goto fail;
7668 }
7669 prop = js_symbol_to_atom(ctx, (JSValue)name);
7670 p = JS_VALUE_GET_OBJ(obj);
7671 prs = find_own_property(&pr, p, prop);
7672 if (prs) {
7673 JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
7674 prop);
7675 goto fail;
7676 }
7677 pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
7678 if (unlikely(!pr)) {
7679 fail:
7680 JS_FreeValue(ctx, val);
7681 return -1;
7682 }
7683 pr->u.value = val;
7684 return 0;
7685}
7686
7687static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
7688 JSValueConst name)
7689{
7690 JSObject *p;
7691 JSShapeProperty *prs;
7692 JSProperty *pr;
7693 JSAtom prop;
7694
7695 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
7696 return JS_ThrowTypeErrorNotAnObject(ctx);
7697 /* safety check */
7698 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
7699 return JS_ThrowTypeErrorNotASymbol(ctx);
7700 prop = js_symbol_to_atom(ctx, (JSValue)name);
7701 p = JS_VALUE_GET_OBJ(obj);
7702 prs = find_own_property(&pr, p, prop);
7703 if (!prs) {
7704 JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7705 return JS_EXCEPTION;
7706 }
7707 return JS_DupValue(ctx, pr->u.value);
7708}
7709
7710static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
7711 JSValueConst name, JSValue val)
7712{
7713 JSObject *p;
7714 JSShapeProperty *prs;
7715 JSProperty *pr;
7716 JSAtom prop;
7717
7718 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7719 JS_ThrowTypeErrorNotAnObject(ctx);
7720 goto fail;
7721 }
7722 /* safety check */
7723 if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
7724 JS_ThrowTypeErrorNotASymbol(ctx);
7725 goto fail;
7726 }
7727 prop = js_symbol_to_atom(ctx, (JSValue)name);
7728 p = JS_VALUE_GET_OBJ(obj);
7729 prs = find_own_property(&pr, p, prop);
7730 if (!prs) {
7731 JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
7732 fail:
7733 JS_FreeValue(ctx, val);
7734 return -1;
7735 }
7736 set_value(ctx, &pr->u.value, val);
7737 return 0;
7738}
7739
7740/* add a private brand field to 'home_obj' if not already present and
7741 if obj is != null add a private brand to it */
7742static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
7743{
7744 JSObject *p, *p1;
7745 JSShapeProperty *prs;
7746 JSProperty *pr;
7747 JSValue brand;
7748 JSAtom brand_atom;
7749
7750 if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
7751 JS_ThrowTypeErrorNotAnObject(ctx);
7752 return -1;
7753 }
7754 p = JS_VALUE_GET_OBJ(home_obj);
7755 prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
7756 if (!prs) {
7757 /* if the brand is not present, add it */
7758 brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
7759 if (JS_IsException(brand))
7760 return -1;
7761 pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
7762 if (!pr) {
7763 JS_FreeValue(ctx, brand);
7764 return -1;
7765 }
7766 pr->u.value = JS_DupValue(ctx, brand);
7767 } else {
7768 brand = JS_DupValue(ctx, pr->u.value);
7769 }
7770 brand_atom = js_symbol_to_atom(ctx, brand);
7771
7772 if (JS_IsObject(obj)) {
7773 p1 = JS_VALUE_GET_OBJ(obj);
7774 prs = find_own_property(&pr, p1, brand_atom);
7775 if (unlikely(prs)) {
7776 JS_FreeAtom(ctx, brand_atom);
7777 JS_ThrowTypeError(ctx, "private method is already present");
7778 return -1;
7779 }
7780 pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
7781 JS_FreeAtom(ctx, brand_atom);
7782 if (!pr)
7783 return -1;
7784 pr->u.value = JS_UNDEFINED;
7785 } else {
7786 JS_FreeAtom(ctx, brand_atom);
7787 }
7788 return 0;
7789}
7790
7791/* return a boolean telling if the brand of the home object of 'func'
7792 is present on 'obj' or -1 in case of exception */
7793static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
7794{
7795 JSObject *p, *p1, *home_obj;
7796 JSShapeProperty *prs;
7797 JSProperty *pr;
7798 JSValueConst brand;
7799
7800 /* get the home object of 'func' */
7801 if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT))
7802 goto not_obj;
7803 p1 = JS_VALUE_GET_OBJ(func);
7804 if (!js_class_has_bytecode(p1->class_id))
7805 goto not_obj;
7806 home_obj = p1->u.func.home_object;
7807 if (!home_obj)
7808 goto not_obj;
7809 prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
7810 if (!prs) {
7811 JS_ThrowTypeError(ctx, "expecting <brand> private field");
7812 return -1;
7813 }
7814 brand = pr->u.value;
7815 /* safety check */
7816 if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
7817 goto not_obj;
7818
7819 /* get the brand array of 'obj' */
7820 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
7821 not_obj:
7822 JS_ThrowTypeErrorNotAnObject(ctx);
7823 return -1;
7824 }
7825 p = JS_VALUE_GET_OBJ(obj);
7826 prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
7827 return (prs != NULL);
7828}
7829
7830static uint32_t js_string_obj_get_length(JSContext *ctx,
7831 JSValueConst obj)
7832{
7833 JSObject *p;
7834 uint32_t len = 0;
7835
7836 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
7837 p = JS_VALUE_GET_OBJ(obj);
7838 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
7839 JSString *p1 = JS_VALUE_GET_STRING(p->u.object_data);
7840 len = p1->len;
7841 }
7842 return len;
7843}
7844
7845static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
7846{
7847 JSContext *ctx = opaque;
7848 JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
7849 JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
7850 uint32_t v1, v2;
7851 BOOL atom1_is_integer, atom2_is_integer;
7852
7853 atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
7854 atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
7855 assert(atom1_is_integer && atom2_is_integer);
7856 if (v1 < v2)
7857 return -1;
7858 else if (v1 == v2)
7859 return 0;
7860 else
7861 return 1;
7862}
7863
7864static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
7865{
7866 uint32_t i;
7867 if (tab) {
7868 for(i = 0; i < len; i++)
7869 JS_FreeAtom(ctx, tab[i].atom);
7870 js_free(ctx, tab);
7871 }
7872}
7873
7874/* return < 0 in case if exception, 0 if OK. ptab and its atoms must
7875 be freed by the user. */
7876static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
7877 JSPropertyEnum **ptab,
7878 uint32_t *plen,
7879 JSObject *p, int flags)
7880{
7881 int i, j;
7882 JSShape *sh;
7883 JSShapeProperty *prs;
7884 JSPropertyEnum *tab_atom, *tab_exotic;
7885 JSAtom atom;
7886 uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
7887 uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
7888 BOOL is_enumerable, num_sorted;
7889 uint32_t num_key;
7890 JSAtomKindEnum kind;
7891
7892 /* clear pointer for consistency in case of failure */
7893 *ptab = NULL;
7894 *plen = 0;
7895
7896 /* compute the number of returned properties */
7897 num_keys_count = 0;
7898 str_keys_count = 0;
7899 sym_keys_count = 0;
7900 exotic_keys_count = 0;
7901 exotic_count = 0;
7902 tab_exotic = NULL;
7903 sh = p->shape;
7904 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
7905 atom = prs->atom;
7906 if (atom != JS_ATOM_NULL) {
7907 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
7908 kind = JS_AtomGetKind(ctx, atom);
7909 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
7910 ((flags >> kind) & 1) != 0) {
7911 /* need to raise an exception in case of the module
7912 name space (implicit GetOwnProperty) */
7913 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
7914 (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
7915 JSVarRef *var_ref = p->prop[i].u.var_ref;
7916 if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
7917 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
7918 return -1;
7919 }
7920 }
7921 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
7922 num_keys_count++;
7923 } else if (kind == JS_ATOM_KIND_STRING) {
7924 str_keys_count++;
7925 } else {
7926 sym_keys_count++;
7927 }
7928 }
7929 }
7930 }
7931
7932 if (p->is_exotic) {
7933 if (p->fast_array) {
7934 if (flags & JS_GPN_STRING_MASK) {
7935 num_keys_count += p->u.array.count;
7936 }
7937 } else if (p->class_id == JS_CLASS_STRING) {
7938 if (flags & JS_GPN_STRING_MASK) {
7939 num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
7940 }
7941 } else {
7942 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
7943 if (em && em->get_own_property_names) {
7944 if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
7945 JS_MKPTR(JS_TAG_OBJECT, p)))
7946 return -1;
7947 for(i = 0; i < exotic_count; i++) {
7948 atom = tab_exotic[i].atom;
7949 kind = JS_AtomGetKind(ctx, atom);
7950 if (((flags >> kind) & 1) != 0) {
7951 is_enumerable = FALSE;
7952 if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
7953 JSPropertyDescriptor desc;
7954 int res;
7955 /* set the "is_enumerable" field if necessary */
7956 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
7957 if (res < 0) {
7958 js_free_prop_enum(ctx, tab_exotic, exotic_count);
7959 return -1;
7960 }
7961 if (res) {
7962 is_enumerable =
7963 ((desc.flags & JS_PROP_ENUMERABLE) != 0);
7964 js_free_desc(ctx, &desc);
7965 }
7966 tab_exotic[i].is_enumerable = is_enumerable;
7967 }
7968 if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
7969 exotic_keys_count++;
7970 }
7971 }
7972 }
7973 }
7974 }
7975 }
7976
7977 /* fill them */
7978
7979 atom_count = num_keys_count + str_keys_count;
7980 if (atom_count < str_keys_count)
7981 goto add_overflow;
7982 atom_count += sym_keys_count;
7983 if (atom_count < sym_keys_count)
7984 goto add_overflow;
7985 atom_count += exotic_keys_count;
7986 if (atom_count < exotic_keys_count || atom_count > INT32_MAX) {
7987 add_overflow:
7988 JS_ThrowOutOfMemory(ctx);
7989 js_free_prop_enum(ctx, tab_exotic, exotic_count);
7990 return -1;
7991 }
7992 /* XXX: need generic way to test for js_malloc(ctx, a * b) overflow */
7993
7994 /* avoid allocating 0 bytes */
7995 tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
7996 if (!tab_atom) {
7997 js_free_prop_enum(ctx, tab_exotic, exotic_count);
7998 return -1;
7999 }
8000
8001 num_index = 0;
8002 str_index = num_keys_count;
8003 sym_index = str_index + str_keys_count;
8004
8005 num_sorted = TRUE;
8006 sh = p->shape;
8007 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
8008 atom = prs->atom;
8009 if (atom != JS_ATOM_NULL) {
8010 is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
8011 kind = JS_AtomGetKind(ctx, atom);
8012 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
8013 ((flags >> kind) & 1) != 0) {
8014 if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
8015 j = num_index++;
8016 num_sorted = FALSE;
8017 } else if (kind == JS_ATOM_KIND_STRING) {
8018 j = str_index++;
8019 } else {
8020 j = sym_index++;
8021 }
8022 tab_atom[j].atom = JS_DupAtom(ctx, atom);
8023 tab_atom[j].is_enumerable = is_enumerable;
8024 }
8025 }
8026 }
8027
8028 if (p->is_exotic) {
8029 int len;
8030 if (p->fast_array) {
8031 if (flags & JS_GPN_STRING_MASK) {
8032 len = p->u.array.count;
8033 goto add_array_keys;
8034 }
8035 } else if (p->class_id == JS_CLASS_STRING) {
8036 if (flags & JS_GPN_STRING_MASK) {
8037 len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8038 add_array_keys:
8039 for(i = 0; i < len; i++) {
8040 tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
8041 if (tab_atom[num_index].atom == JS_ATOM_NULL) {
8042 js_free_prop_enum(ctx, tab_atom, num_index);
8043 return -1;
8044 }
8045 tab_atom[num_index].is_enumerable = TRUE;
8046 num_index++;
8047 }
8048 }
8049 } else {
8050 /* Note: exotic keys are not reordered and comes after the object own properties. */
8051 for(i = 0; i < exotic_count; i++) {
8052 atom = tab_exotic[i].atom;
8053 is_enumerable = tab_exotic[i].is_enumerable;
8054 kind = JS_AtomGetKind(ctx, atom);
8055 if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
8056 ((flags >> kind) & 1) != 0) {
8057 tab_atom[sym_index].atom = atom;
8058 tab_atom[sym_index].is_enumerable = is_enumerable;
8059 sym_index++;
8060 } else {
8061 JS_FreeAtom(ctx, atom);
8062 }
8063 }
8064 js_free(ctx, tab_exotic);
8065 }
8066 }
8067
8068 assert(num_index == num_keys_count);
8069 assert(str_index == num_keys_count + str_keys_count);
8070 assert(sym_index == atom_count);
8071
8072 if (num_keys_count != 0 && !num_sorted) {
8073 rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
8074 ctx);
8075 }
8076 *ptab = tab_atom;
8077 *plen = atom_count;
8078 return 0;
8079}
8080
8081int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
8082 uint32_t *plen, JSValueConst obj, int flags)
8083{
8084 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
8085 JS_ThrowTypeErrorNotAnObject(ctx);
8086 return -1;
8087 }
8088 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
8089 JS_VALUE_GET_OBJ(obj), flags);
8090}
8091
8092/* Return -1 if exception,
8093 FALSE if the property does not exist, TRUE if it exists. If TRUE is
8094 returned, the property descriptor 'desc' is filled present. */
8095static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
8096 JSObject *p, JSAtom prop)
8097{
8098 JSShapeProperty *prs;
8099 JSProperty *pr;
8100
8101retry:
8102 prs = find_own_property(&pr, p, prop);
8103 if (prs) {
8104 if (desc) {
8105 desc->flags = prs->flags & JS_PROP_C_W_E;
8106 desc->getter = JS_UNDEFINED;
8107 desc->setter = JS_UNDEFINED;
8108 desc->value = JS_UNDEFINED;
8109 if (unlikely(prs->flags & JS_PROP_TMASK)) {
8110 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8111 desc->flags |= JS_PROP_GETSET;
8112 if (pr->u.getset.getter)
8113 desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
8114 if (pr->u.getset.setter)
8115 desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
8116 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
8117 JSValue val = *pr->u.var_ref->pvalue;
8118 if (unlikely(JS_IsUninitialized(val))) {
8119 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
8120 return -1;
8121 }
8122 desc->value = JS_DupValue(ctx, val);
8123 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8124 /* Instantiate property and retry */
8125 if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
8126 return -1;
8127 goto retry;
8128 }
8129 } else {
8130 desc->value = JS_DupValue(ctx, pr->u.value);
8131 }
8132 } else {
8133 /* for consistency, send the exception even if desc is NULL */
8134 if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
8135 if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
8136 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
8137 return -1;
8138 }
8139 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8140 /* nothing to do: delay instantiation until actual value and/or attributes are read */
8141 }
8142 }
8143 return TRUE;
8144 }
8145 if (p->is_exotic) {
8146 if (p->fast_array) {
8147 /* specific case for fast arrays */
8148 if (__JS_AtomIsTaggedInt(prop)) {
8149 uint32_t idx;
8150 idx = __JS_AtomToUInt32(prop);
8151 if (idx < p->u.array.count) {
8152 if (desc) {
8153 desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
8154 JS_PROP_CONFIGURABLE;
8155 desc->getter = JS_UNDEFINED;
8156 desc->setter = JS_UNDEFINED;
8157 desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
8158 }
8159 return TRUE;
8160 }
8161 }
8162 } else {
8163 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8164 if (em && em->get_own_property) {
8165 return em->get_own_property(ctx, desc,
8166 JS_MKPTR(JS_TAG_OBJECT, p), prop);
8167 }
8168 }
8169 }
8170 return FALSE;
8171}
8172
8173int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
8174 JSValueConst obj, JSAtom prop)
8175{
8176 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
8177 JS_ThrowTypeErrorNotAnObject(ctx);
8178 return -1;
8179 }
8180 return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
8181}
8182
8183/* return -1 if exception (exotic object only) or TRUE/FALSE */
8184int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
8185{
8186 JSObject *p;
8187
8188 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
8189 return FALSE;
8190 p = JS_VALUE_GET_OBJ(obj);
8191 if (unlikely(p->is_exotic)) {
8192 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8193 if (em && em->is_extensible) {
8194 return em->is_extensible(ctx, obj);
8195 }
8196 }
8197 return p->extensible;
8198}
8199
8200/* return -1 if exception (exotic object only) or TRUE/FALSE */
8201int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
8202{
8203 JSObject *p;
8204
8205 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
8206 return FALSE;
8207 p = JS_VALUE_GET_OBJ(obj);
8208 if (unlikely(p->is_exotic)) {
8209 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8210 if (em && em->prevent_extensions) {
8211 return em->prevent_extensions(ctx, obj);
8212 }
8213 }
8214 p->extensible = FALSE;
8215 return TRUE;
8216}
8217
8218/* return -1 if exception otherwise TRUE or FALSE */
8219int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
8220{
8221 JSObject *p;
8222 int ret;
8223 JSValue obj1;
8224
8225 if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
8226 return FALSE;
8227 p = JS_VALUE_GET_OBJ(obj);
8228 for(;;) {
8229 if (p->is_exotic) {
8230 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8231 if (em && em->has_property) {
8232 /* has_property can free the prototype */
8233 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8234 ret = em->has_property(ctx, obj1, prop);
8235 JS_FreeValue(ctx, obj1);
8236 return ret;
8237 }
8238 }
8239 /* JS_GetOwnPropertyInternal can free the prototype */
8240 JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8241 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
8242 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
8243 if (ret != 0)
8244 return ret;
8245 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
8246 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8247 ret = JS_AtomIsNumericIndex(ctx, prop);
8248 if (ret != 0) {
8249 if (ret < 0)
8250 return -1;
8251 return FALSE;
8252 }
8253 }
8254 p = p->shape->proto;
8255 if (!p)
8256 break;
8257 }
8258 return FALSE;
8259}
8260
8261/* val must be a symbol */
8262static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
8263{
8264 JSAtomStruct *p = JS_VALUE_GET_PTR(val);
8265 return js_get_atom_index(ctx->rt, p);
8266}
8267
8268/* return JS_ATOM_NULL in case of exception */
8269JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
8270{
8271 JSAtom atom;
8272 uint32_t tag;
8273 tag = JS_VALUE_GET_TAG(val);
8274 if (tag == JS_TAG_INT &&
8275 (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
8276 /* fast path for integer values */
8277 atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
8278 } else if (tag == JS_TAG_SYMBOL) {
8279 JSAtomStruct *p = JS_VALUE_GET_PTR(val);
8280 atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
8281 } else {
8282 JSValue str;
8283 str = JS_ToPropertyKey(ctx, val);
8284 if (JS_IsException(str))
8285 return JS_ATOM_NULL;
8286 if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
8287 atom = js_symbol_to_atom(ctx, str);
8288 } else {
8289 atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
8290 }
8291 }
8292 return atom;
8293}
8294
8295static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
8296 JSValue prop)
8297{
8298 JSAtom atom;
8299 JSValue ret;
8300
8301 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
8302 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
8303 JSObject *p;
8304 uint32_t idx;
8305 /* fast path for array access */
8306 p = JS_VALUE_GET_OBJ(this_obj);
8307 idx = JS_VALUE_GET_INT(prop);
8308 switch(p->class_id) {
8309 case JS_CLASS_ARRAY:
8310 case JS_CLASS_ARGUMENTS:
8311 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8312 return JS_DupValue(ctx, p->u.array.u.values[idx]);
8313 case JS_CLASS_INT8_ARRAY:
8314 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8315 return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
8316 case JS_CLASS_UINT8C_ARRAY:
8317 case JS_CLASS_UINT8_ARRAY:
8318 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8319 return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
8320 case JS_CLASS_INT16_ARRAY:
8321 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8322 return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
8323 case JS_CLASS_UINT16_ARRAY:
8324 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8325 return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
8326 case JS_CLASS_INT32_ARRAY:
8327 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8328 return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
8329 case JS_CLASS_UINT32_ARRAY:
8330 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8331 return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
8332 case JS_CLASS_BIG_INT64_ARRAY:
8333 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8334 return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
8335 case JS_CLASS_BIG_UINT64_ARRAY:
8336 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8337 return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
8338 case JS_CLASS_FLOAT32_ARRAY:
8339 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8340 return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
8341 case JS_CLASS_FLOAT64_ARRAY:
8342 if (unlikely(idx >= p->u.array.count)) goto slow_path;
8343 return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
8344 default:
8345 goto slow_path;
8346 }
8347 } else {
8348 slow_path:
8349 /* ToObject() must be done before ToPropertyKey() */
8350 if (JS_IsNull(this_obj) || JS_IsUndefined(this_obj)) {
8351 JS_FreeValue(ctx, prop);
8352 return JS_ThrowTypeError(ctx, "cannot read property of %s", JS_IsNull(this_obj) ? "null" : "undefined");
8353 }
8354 atom = JS_ValueToAtom(ctx, prop);
8355 JS_FreeValue(ctx, prop);
8356 if (unlikely(atom == JS_ATOM_NULL))
8357 return JS_EXCEPTION;
8358 ret = JS_GetProperty(ctx, this_obj, atom);
8359 JS_FreeAtom(ctx, atom);
8360 return ret;
8361 }
8362}
8363
8364JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
8365 uint32_t idx)
8366{
8367 return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
8368}
8369
8370/* Check if an object has a generalized numeric property. Return value:
8371 -1 for exception,
8372 TRUE if property exists, stored into *pval,
8373 FALSE if proprty does not exist.
8374 */
8375static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
8376{
8377 JSValue val = JS_UNDEFINED;
8378 JSAtom prop;
8379 int present;
8380
8381 if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
8382 /* fast path */
8383 present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
8384 if (present > 0) {
8385 val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
8386 if (unlikely(JS_IsException(val)))
8387 present = -1;
8388 }
8389 } else {
8390 prop = JS_NewAtomInt64(ctx, idx);
8391 present = -1;
8392 if (likely(prop != JS_ATOM_NULL)) {
8393 present = JS_HasProperty(ctx, obj, prop);
8394 if (present > 0) {
8395 val = JS_GetProperty(ctx, obj, prop);
8396 if (unlikely(JS_IsException(val)))
8397 present = -1;
8398 }
8399 JS_FreeAtom(ctx, prop);
8400 }
8401 }
8402 *pval = val;
8403 return present;
8404}
8405
8406static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
8407{
8408 JSAtom prop;
8409 JSValue val;
8410
8411 if ((uint64_t)idx <= INT32_MAX) {
8412 /* fast path for fast arrays */
8413 return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
8414 }
8415 prop = JS_NewAtomInt64(ctx, idx);
8416 if (prop == JS_ATOM_NULL)
8417 return JS_EXCEPTION;
8418
8419 val = JS_GetProperty(ctx, obj, prop);
8420 JS_FreeAtom(ctx, prop);
8421 return val;
8422}
8423
8424JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
8425 const char *prop)
8426{
8427 JSAtom atom;
8428 JSValue ret;
8429 atom = JS_NewAtom(ctx, prop);
8430 ret = JS_GetProperty(ctx, this_obj, atom);
8431 JS_FreeAtom(ctx, atom);
8432 return ret;
8433}
8434
8435/* Note: the property value is not initialized. Return NULL if memory
8436 error. */
8437static JSProperty *add_property(JSContext *ctx,
8438 JSObject *p, JSAtom prop, int prop_flags)
8439{
8440 JSShape *sh, *new_sh;
8441
8442 sh = p->shape;
8443 if (sh->is_hashed) {
8444 /* try to find an existing shape */
8445 new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
8446 if (new_sh) {
8447 /* matching shape found: use it */
8448 /* the property array may need to be resized */
8449 if (new_sh->prop_size != sh->prop_size) {
8450 JSProperty *new_prop;
8451 new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
8452 new_sh->prop_size);
8453 if (!new_prop)
8454 return NULL;
8455 p->prop = new_prop;
8456 }
8457 p->shape = js_dup_shape(new_sh);
8458 js_free_shape(ctx->rt, sh);
8459 return &p->prop[new_sh->prop_count - 1];
8460 } else if (sh->header.ref_count != 1) {
8461 /* if the shape is shared, clone it */
8462 new_sh = js_clone_shape(ctx, sh);
8463 if (!new_sh)
8464 return NULL;
8465 /* hash the cloned shape */
8466 new_sh->is_hashed = TRUE;
8467 js_shape_hash_link(ctx->rt, new_sh);
8468 js_free_shape(ctx->rt, p->shape);
8469 p->shape = new_sh;
8470 }
8471 }
8472 assert(p->shape->header.ref_count == 1);
8473 if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
8474 return NULL;
8475 return &p->prop[p->shape->prop_count - 1];
8476}
8477
8478/* can be called on Array or Arguments objects. return < 0 if
8479 memory alloc error. */
8480static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
8481 JSObject *p)
8482{
8483 JSProperty *pr;
8484 JSShape *sh;
8485 JSValue *tab;
8486 uint32_t i, len, new_count;
8487
8488 if (js_shape_prepare_update(ctx, p, NULL))
8489 return -1;
8490 len = p->u.array.count;
8491 /* resize the properties once to simplify the error handling */
8492 sh = p->shape;
8493 new_count = sh->prop_count + len;
8494 if (new_count > sh->prop_size) {
8495 if (resize_properties(ctx, &p->shape, p, new_count))
8496 return -1;
8497 }
8498
8499 tab = p->u.array.u.values;
8500 for(i = 0; i < len; i++) {
8501 /* add_property cannot fail here but
8502 __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
8503 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
8504 pr->u.value = *tab++;
8505 }
8506 js_free(ctx, p->u.array.u.values);
8507 p->u.array.count = 0;
8508 p->u.array.u.values = NULL; /* fail safe */
8509 p->u.array.u1.size = 0;
8510 p->fast_array = 0;
8511 return 0;
8512}
8513
8514static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
8515{
8516 JSShape *sh;
8517 JSShapeProperty *pr, *lpr, *prop;
8518 JSProperty *pr1;
8519 uint32_t lpr_idx;
8520 intptr_t h, h1;
8521
8522 redo:
8523 sh = p->shape;
8524 h1 = atom & sh->prop_hash_mask;
8525 h = prop_hash_end(sh)[-h1 - 1];
8526 prop = get_shape_prop(sh);
8527 lpr = NULL;
8528 lpr_idx = 0; /* prevent warning */
8529 while (h != 0) {
8530 pr = &prop[h - 1];
8531 if (likely(pr->atom == atom)) {
8532 /* found ! */
8533 if (!(pr->flags & JS_PROP_CONFIGURABLE))
8534 return FALSE;
8535 /* realloc the shape if needed */
8536 if (lpr)
8537 lpr_idx = lpr - get_shape_prop(sh);
8538 if (js_shape_prepare_update(ctx, p, &pr))
8539 return -1;
8540 sh = p->shape;
8541 /* remove property */
8542 if (lpr) {
8543 lpr = get_shape_prop(sh) + lpr_idx;
8544 lpr->hash_next = pr->hash_next;
8545 } else {
8546 prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
8547 }
8548 sh->deleted_prop_count++;
8549 /* free the entry */
8550 pr1 = &p->prop[h - 1];
8551 free_property(ctx->rt, pr1, pr->flags);
8552 JS_FreeAtom(ctx, pr->atom);
8553 /* put default values */
8554 pr->flags = 0;
8555 pr->atom = JS_ATOM_NULL;
8556 pr1->u.value = JS_UNDEFINED;
8557
8558 /* compact the properties if too many deleted properties */
8559 if (sh->deleted_prop_count >= 8 &&
8560 sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
8561 compact_properties(ctx, p);
8562 }
8563 return TRUE;
8564 }
8565 lpr = pr;
8566 h = pr->hash_next;
8567 }
8568
8569 if (p->is_exotic) {
8570 if (p->fast_array) {
8571 uint32_t idx;
8572 if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
8573 idx < p->u.array.count) {
8574 if (p->class_id == JS_CLASS_ARRAY ||
8575 p->class_id == JS_CLASS_ARGUMENTS) {
8576 /* Special case deleting the last element of a fast Array */
8577 if (idx == p->u.array.count - 1) {
8578 JS_FreeValue(ctx, p->u.array.u.values[idx]);
8579 p->u.array.count = idx;
8580 return TRUE;
8581 }
8582 if (convert_fast_array_to_array(ctx, p))
8583 return -1;
8584 goto redo;
8585 } else {
8586 return FALSE;
8587 }
8588 }
8589 } else {
8590 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
8591 if (em && em->delete_property) {
8592 return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
8593 }
8594 }
8595 }
8596 /* not found */
8597 return TRUE;
8598}
8599
8600static int call_setter(JSContext *ctx, JSObject *setter,
8601 JSValueConst this_obj, JSValue val, int flags)
8602{
8603 JSValue ret, func;
8604 if (likely(setter)) {
8605 func = JS_MKPTR(JS_TAG_OBJECT, setter);
8606 /* Note: the field could be removed in the setter */
8607 func = JS_DupValue(ctx, func);
8608 ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
8609 JS_FreeValue(ctx, val);
8610 if (JS_IsException(ret))
8611 return -1;
8612 JS_FreeValue(ctx, ret);
8613 return TRUE;
8614 } else {
8615 JS_FreeValue(ctx, val);
8616 if ((flags & JS_PROP_THROW) ||
8617 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
8618 JS_ThrowTypeError(ctx, "no setter for property");
8619 return -1;
8620 }
8621 return FALSE;
8622 }
8623}
8624
8625/* set the array length and remove the array elements if necessary. */
8626static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
8627 int flags)
8628{
8629 uint32_t len, idx, cur_len;
8630 int i, ret;
8631
8632 /* Note: this call can reallocate the properties of 'p' */
8633 ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
8634 if (ret)
8635 return -1;
8636 /* JS_ToArrayLengthFree() must be done before the read-only test */
8637 if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
8638 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8639
8640 if (likely(p->fast_array)) {
8641 uint32_t old_len = p->u.array.count;
8642 if (len < old_len) {
8643 for(i = len; i < old_len; i++) {
8644 JS_FreeValue(ctx, p->u.array.u.values[i]);
8645 }
8646 p->u.array.count = len;
8647 }
8648 p->prop[0].u.value = JS_NewUint32(ctx, len);
8649 } else {
8650 /* Note: length is always a uint32 because the object is an
8651 array */
8652 JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
8653 if (len < cur_len) {
8654 uint32_t d;
8655 JSShape *sh;
8656 JSShapeProperty *pr;
8657
8658 d = cur_len - len;
8659 sh = p->shape;
8660 if (d <= sh->prop_count) {
8661 JSAtom atom;
8662
8663 /* faster to iterate */
8664 while (cur_len > len) {
8665 atom = JS_NewAtomUInt32(ctx, cur_len - 1);
8666 ret = delete_property(ctx, p, atom);
8667 JS_FreeAtom(ctx, atom);
8668 if (unlikely(!ret)) {
8669 /* unlikely case: property is not
8670 configurable */
8671 break;
8672 }
8673 cur_len--;
8674 }
8675 } else {
8676 /* faster to iterate thru all the properties. Need two
8677 passes in case one of the property is not
8678 configurable */
8679 cur_len = len;
8680 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8681 i++, pr++) {
8682 if (pr->atom != JS_ATOM_NULL &&
8683 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8684 if (idx >= cur_len &&
8685 !(pr->flags & JS_PROP_CONFIGURABLE)) {
8686 cur_len = idx + 1;
8687 }
8688 }
8689 }
8690
8691 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
8692 i++, pr++) {
8693 if (pr->atom != JS_ATOM_NULL &&
8694 JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
8695 if (idx >= cur_len) {
8696 /* remove the property */
8697 delete_property(ctx, p, pr->atom);
8698 /* WARNING: the shape may have been modified */
8699 sh = p->shape;
8700 pr = get_shape_prop(sh) + i;
8701 }
8702 }
8703 }
8704 }
8705 } else {
8706 cur_len = len;
8707 }
8708 set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
8709 if (unlikely(cur_len > len)) {
8710 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
8711 }
8712 }
8713 return TRUE;
8714}
8715
8716/* return -1 if exception */
8717static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
8718{
8719 uint32_t new_size;
8720 size_t slack;
8721 JSValue *new_array_prop;
8722 /* XXX: potential arithmetic overflow */
8723 new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
8724 new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
8725 if (!new_array_prop)
8726 return -1;
8727 new_size += slack / sizeof(*new_array_prop);
8728 p->u.array.u.values = new_array_prop;
8729 p->u.array.u1.size = new_size;
8730 return 0;
8731}
8732
8733/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
8734 TRUE and p->extensible = TRUE */
8735static int add_fast_array_element(JSContext *ctx, JSObject *p,
8736 JSValue val, int flags)
8737{
8738 uint32_t new_len, array_len;
8739 /* extend the array by one */
8740 /* XXX: convert to slow array if new_len > 2^31-1 elements */
8741 new_len = p->u.array.count + 1;
8742 /* update the length if necessary. We assume that if the length is
8743 not an integer, then if it >= 2^31. */
8744 if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
8745 array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
8746 if (new_len > array_len) {
8747 if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
8748 JS_FreeValue(ctx, val);
8749 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
8750 }
8751 p->prop[0].u.value = JS_NewInt32(ctx, new_len);
8752 }
8753 }
8754 if (unlikely(new_len > p->u.array.u1.size)) {
8755 if (expand_fast_array(ctx, p, new_len)) {
8756 JS_FreeValue(ctx, val);
8757 return -1;
8758 }
8759 }
8760 p->u.array.u.values[new_len - 1] = val;
8761 p->u.array.count = new_len;
8762 return TRUE;
8763}
8764
8765/* Allocate a new fast array. Its 'length' property is set to zero. It
8766 maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit
8767 integer. WARNING: the content of the array is not initialized. */
8768static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len)
8769{
8770 JSValue arr;
8771 JSObject *p;
8772
8773 if (len > INT32_MAX)
8774 return JS_ThrowRangeError(ctx, "invalid array length");
8775 arr = JS_NewArray(ctx);
8776 if (JS_IsException(arr))
8777 return arr;
8778 if (len > 0) {
8779 p = JS_VALUE_GET_OBJ(arr);
8780 if (expand_fast_array(ctx, p, len) < 0) {
8781 JS_FreeValue(ctx, arr);
8782 return JS_EXCEPTION;
8783 }
8784 p->u.array.count = len;
8785 }
8786 return arr;
8787}
8788
8789static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
8790{
8791 JS_FreeValue(ctx, desc->getter);
8792 JS_FreeValue(ctx, desc->setter);
8793 JS_FreeValue(ctx, desc->value);
8794}
8795
8796/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
8797 freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
8798 JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
8799 the new property is not added and an error is raised. 'this_obj' is
8800 the receiver. If obj != this_obj, then obj must be an object
8801 (Reflect.set case). */
8802int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj,
8803 JSAtom prop, JSValue val, JSValueConst this_obj, int flags)
8804{
8805 JSObject *p, *p1;
8806 JSShapeProperty *prs;
8807 JSProperty *pr;
8808 uint32_t tag;
8809 JSPropertyDescriptor desc;
8810 int ret;
8811#if 0
8812 printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
8813#endif
8814 tag = JS_VALUE_GET_TAG(this_obj);
8815 if (unlikely(tag != JS_TAG_OBJECT)) {
8816 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
8817 p = NULL;
8818 p1 = JS_VALUE_GET_OBJ(obj);
8819 goto prototype_lookup;
8820 } else {
8821 switch(tag) {
8822 case JS_TAG_NULL:
8823 JS_FreeValue(ctx, val);
8824 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
8825 return -1;
8826 case JS_TAG_UNDEFINED:
8827 JS_FreeValue(ctx, val);
8828 JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
8829 return -1;
8830 default:
8831 /* even on a primitive type we can have setters on the prototype */
8832 p = NULL;
8833 p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
8834 goto prototype_lookup;
8835 }
8836 }
8837 } else {
8838 p = JS_VALUE_GET_OBJ(this_obj);
8839 p1 = JS_VALUE_GET_OBJ(obj);
8840 if (unlikely(p != p1))
8841 goto retry2;
8842 }
8843
8844 /* fast path if obj == this_obj */
8845 retry:
8846 prs = find_own_property(&pr, p1, prop);
8847 if (prs) {
8848 if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
8849 JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
8850 /* fast case */
8851 set_value(ctx, &pr->u.value, val);
8852 return TRUE;
8853 } else if (prs->flags & JS_PROP_LENGTH) {
8854 assert(p->class_id == JS_CLASS_ARRAY);
8855 assert(prop == JS_ATOM_length);
8856 return set_array_length(ctx, p, val, flags);
8857 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8858 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8859 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
8860 /* JS_PROP_WRITABLE is always true for variable
8861 references, but they are write protected in module name
8862 spaces. */
8863 if (p->class_id == JS_CLASS_MODULE_NS)
8864 goto read_only_prop;
8865 set_value(ctx, pr->u.var_ref->pvalue, val);
8866 return TRUE;
8867 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8868 /* Instantiate property and retry (potentially useless) */
8869 if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
8870 JS_FreeValue(ctx, val);
8871 return -1;
8872 }
8873 goto retry;
8874 } else {
8875 goto read_only_prop;
8876 }
8877 }
8878
8879 for(;;) {
8880 if (p1->is_exotic) {
8881 if (p1->fast_array) {
8882 if (__JS_AtomIsTaggedInt(prop)) {
8883 uint32_t idx = __JS_AtomToUInt32(prop);
8884 if (idx < p1->u.array.count) {
8885 if (unlikely(p == p1))
8886 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
8887 else
8888 break;
8889 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8890 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8891 goto typed_array_oob;
8892 }
8893 } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
8894 p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
8895 ret = JS_AtomIsNumericIndex(ctx, prop);
8896 if (ret != 0) {
8897 if (ret < 0) {
8898 JS_FreeValue(ctx, val);
8899 return -1;
8900 }
8901 typed_array_oob:
8902 if (p == p1) {
8903 /* must convert the argument even if out of bound access */
8904 if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY ||
8905 p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
8906 int64_t v;
8907 if (JS_ToBigInt64Free(ctx, &v, val))
8908 return -1;
8909 } else {
8910 val = JS_ToNumberFree(ctx, val);
8911 JS_FreeValue(ctx, val);
8912 if (JS_IsException(val))
8913 return -1;
8914 }
8915 } else {
8916 JS_FreeValue(ctx, val);
8917 }
8918 return TRUE;
8919 }
8920 }
8921 } else {
8922 const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
8923 if (em) {
8924 JSValue obj1;
8925 if (em->set_property) {
8926 /* set_property can free the prototype */
8927 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8928 ret = em->set_property(ctx, obj1, prop,
8929 val, this_obj, flags);
8930 JS_FreeValue(ctx, obj1);
8931 JS_FreeValue(ctx, val);
8932 return ret;
8933 }
8934 if (em->get_own_property) {
8935 /* get_own_property can free the prototype */
8936 obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
8937 ret = em->get_own_property(ctx, &desc,
8938 obj1, prop);
8939 JS_FreeValue(ctx, obj1);
8940 if (ret < 0) {
8941 JS_FreeValue(ctx, val);
8942 return ret;
8943 }
8944 if (ret) {
8945 if (desc.flags & JS_PROP_GETSET) {
8946 JSObject *setter;
8947 if (JS_IsUndefined(desc.setter))
8948 setter = NULL;
8949 else
8950 setter = JS_VALUE_GET_OBJ(desc.setter);
8951 ret = call_setter(ctx, setter, this_obj, val, flags);
8952 JS_FreeValue(ctx, desc.getter);
8953 JS_FreeValue(ctx, desc.setter);
8954 return ret;
8955 } else {
8956 JS_FreeValue(ctx, desc.value);
8957 if (!(desc.flags & JS_PROP_WRITABLE))
8958 goto read_only_prop;
8959 if (likely(p == p1)) {
8960 ret = JS_DefineProperty(ctx, this_obj, prop, val,
8961 JS_UNDEFINED, JS_UNDEFINED,
8962 JS_PROP_HAS_VALUE);
8963 JS_FreeValue(ctx, val);
8964 return ret;
8965 } else {
8966 break;
8967 }
8968 }
8969 }
8970 }
8971 }
8972 }
8973 }
8974 p1 = p1->shape->proto;
8975 prototype_lookup:
8976 if (!p1)
8977 break;
8978
8979 retry2:
8980 prs = find_own_property(&pr, p1, prop);
8981 if (prs) {
8982 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
8983 return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
8984 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
8985 /* Instantiate property and retry (potentially useless) */
8986 if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
8987 return -1;
8988 goto retry2;
8989 } else if (!(prs->flags & JS_PROP_WRITABLE)) {
8990 goto read_only_prop;
8991 } else {
8992 break;
8993 }
8994 }
8995 }
8996
8997 if (unlikely(flags & JS_PROP_NO_ADD)) {
8998 JS_FreeValue(ctx, val);
8999 JS_ThrowReferenceErrorNotDefined(ctx, prop);
9000 return -1;
9001 }
9002
9003 if (unlikely(!p)) {
9004 JS_FreeValue(ctx, val);
9005 return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
9006 }
9007
9008 if (unlikely(!p->extensible)) {
9009 JS_FreeValue(ctx, val);
9010 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
9011 }
9012
9013 if (likely(p == JS_VALUE_GET_OBJ(obj))) {
9014 if (p->is_exotic) {
9015 if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
9016 __JS_AtomIsTaggedInt(prop)) {
9017 uint32_t idx = __JS_AtomToUInt32(prop);
9018 if (idx == p->u.array.count) {
9019 /* fast case */
9020 return add_fast_array_element(ctx, p, val, flags);
9021 } else {
9022 goto generic_create_prop;
9023 }
9024 } else {
9025 goto generic_create_prop;
9026 }
9027 } else {
9028 pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
9029 if (unlikely(!pr)) {
9030 JS_FreeValue(ctx, val);
9031 return -1;
9032 }
9033 pr->u.value = val;
9034 return TRUE;
9035 }
9036 } else {
9037 /* generic case: modify the property in this_obj if it already exists */
9038 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
9039 if (ret < 0) {
9040 JS_FreeValue(ctx, val);
9041 return ret;
9042 }
9043 if (ret) {
9044 if (desc.flags & JS_PROP_GETSET) {
9045 JS_FreeValue(ctx, desc.getter);
9046 JS_FreeValue(ctx, desc.setter);
9047 JS_FreeValue(ctx, val);
9048 return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
9049 } else {
9050 JS_FreeValue(ctx, desc.value);
9051 if (!(desc.flags & JS_PROP_WRITABLE) ||
9052 p->class_id == JS_CLASS_MODULE_NS) {
9053 read_only_prop:
9054 JS_FreeValue(ctx, val);
9055 return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
9056 }
9057 }
9058 ret = JS_DefineProperty(ctx, this_obj, prop, val,
9059 JS_UNDEFINED, JS_UNDEFINED,
9060 JS_PROP_HAS_VALUE);
9061 JS_FreeValue(ctx, val);
9062 return ret;
9063 } else {
9064 generic_create_prop:
9065 ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
9066 flags |
9067 JS_PROP_HAS_VALUE |
9068 JS_PROP_HAS_ENUMERABLE |
9069 JS_PROP_HAS_WRITABLE |
9070 JS_PROP_HAS_CONFIGURABLE |
9071 JS_PROP_C_W_E);
9072 JS_FreeValue(ctx, val);
9073 return ret;
9074 }
9075 }
9076}
9077
9078/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
9079static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
9080 JSValue prop, JSValue val, int flags)
9081{
9082 if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
9083 JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
9084 JSObject *p;
9085 uint32_t idx;
9086 double d;
9087 int32_t v;
9088
9089 /* fast path for array access */
9090 p = JS_VALUE_GET_OBJ(this_obj);
9091 idx = JS_VALUE_GET_INT(prop);
9092 switch(p->class_id) {
9093 case JS_CLASS_ARRAY:
9094 if (unlikely(idx >= (uint32_t)p->u.array.count)) {
9095 JSObject *p1;
9096 JSShape *sh1;
9097
9098 /* fast path to add an element to the array */
9099 if (idx != (uint32_t)p->u.array.count ||
9100 !p->fast_array || !p->extensible)
9101 goto slow_path;
9102 /* check if prototype chain has a numeric property */
9103 p1 = p->shape->proto;
9104 while (p1 != NULL) {
9105 sh1 = p1->shape;
9106 if (p1->class_id == JS_CLASS_ARRAY) {
9107 if (unlikely(!p1->fast_array))
9108 goto slow_path;
9109 } else if (p1->class_id == JS_CLASS_OBJECT) {
9110 if (unlikely(sh1->has_small_array_index))
9111 goto slow_path;
9112 } else {
9113 goto slow_path;
9114 }
9115 p1 = sh1->proto;
9116 }
9117 /* add element */
9118 return add_fast_array_element(ctx, p, val, flags);
9119 }
9120 set_value(ctx, &p->u.array.u.values[idx], val);
9121 break;
9122 case JS_CLASS_ARGUMENTS:
9123 if (unlikely(idx >= (uint32_t)p->u.array.count))
9124 goto slow_path;
9125 set_value(ctx, &p->u.array.u.values[idx], val);
9126 break;
9127 case JS_CLASS_UINT8C_ARRAY:
9128 if (JS_ToUint8ClampFree(ctx, &v, val))
9129 return -1;
9130 /* Note: the conversion can detach the typed array, so the
9131 array bound check must be done after */
9132 if (unlikely(idx >= (uint32_t)p->u.array.count))
9133 goto ta_out_of_bound;
9134 p->u.array.u.uint8_ptr[idx] = v;
9135 break;
9136 case JS_CLASS_INT8_ARRAY:
9137 case JS_CLASS_UINT8_ARRAY:
9138 if (JS_ToInt32Free(ctx, &v, val))
9139 return -1;
9140 if (unlikely(idx >= (uint32_t)p->u.array.count))
9141 goto ta_out_of_bound;
9142 p->u.array.u.uint8_ptr[idx] = v;
9143 break;
9144 case JS_CLASS_INT16_ARRAY:
9145 case JS_CLASS_UINT16_ARRAY:
9146 if (JS_ToInt32Free(ctx, &v, val))
9147 return -1;
9148 if (unlikely(idx >= (uint32_t)p->u.array.count))
9149 goto ta_out_of_bound;
9150 p->u.array.u.uint16_ptr[idx] = v;
9151 break;
9152 case JS_CLASS_INT32_ARRAY:
9153 case JS_CLASS_UINT32_ARRAY:
9154 if (JS_ToInt32Free(ctx, &v, val))
9155 return -1;
9156 if (unlikely(idx >= (uint32_t)p->u.array.count))
9157 goto ta_out_of_bound;
9158 p->u.array.u.uint32_ptr[idx] = v;
9159 break;
9160 case JS_CLASS_BIG_INT64_ARRAY:
9161 case JS_CLASS_BIG_UINT64_ARRAY:
9162 /* XXX: need specific conversion function */
9163 {
9164 int64_t v;
9165 if (JS_ToBigInt64Free(ctx, &v, val))
9166 return -1;
9167 if (unlikely(idx >= (uint32_t)p->u.array.count))
9168 goto ta_out_of_bound;
9169 p->u.array.u.uint64_ptr[idx] = v;
9170 }
9171 break;
9172 case JS_CLASS_FLOAT32_ARRAY:
9173 if (JS_ToFloat64Free(ctx, &d, val))
9174 return -1;
9175 if (unlikely(idx >= (uint32_t)p->u.array.count))
9176 goto ta_out_of_bound;
9177 p->u.array.u.float_ptr[idx] = d;
9178 break;
9179 case JS_CLASS_FLOAT64_ARRAY:
9180 if (JS_ToFloat64Free(ctx, &d, val))
9181 return -1;
9182 if (unlikely(idx >= (uint32_t)p->u.array.count)) {
9183 ta_out_of_bound:
9184 return TRUE;
9185 }
9186 p->u.array.u.double_ptr[idx] = d;
9187 break;
9188 default:
9189 goto slow_path;
9190 }
9191 return TRUE;
9192 } else {
9193 JSAtom atom;
9194 int ret;
9195 slow_path:
9196 atom = JS_ValueToAtom(ctx, prop);
9197 JS_FreeValue(ctx, prop);
9198 if (unlikely(atom == JS_ATOM_NULL)) {
9199 JS_FreeValue(ctx, val);
9200 return -1;
9201 }
9202 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags);
9203 JS_FreeAtom(ctx, atom);
9204 return ret;
9205 }
9206}
9207
9208int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
9209 uint32_t idx, JSValue val)
9210{
9211 return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
9212 JS_PROP_THROW);
9213}
9214
9215int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
9216 int64_t idx, JSValue val)
9217{
9218 JSAtom prop;
9219 int res;
9220
9221 if ((uint64_t)idx <= INT32_MAX) {
9222 /* fast path for fast arrays */
9223 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
9224 JS_PROP_THROW);
9225 }
9226 prop = JS_NewAtomInt64(ctx, idx);
9227 if (prop == JS_ATOM_NULL) {
9228 JS_FreeValue(ctx, val);
9229 return -1;
9230 }
9231 res = JS_SetProperty(ctx, this_obj, prop, val);
9232 JS_FreeAtom(ctx, prop);
9233 return res;
9234}
9235
9236int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
9237 const char *prop, JSValue val)
9238{
9239 JSAtom atom;
9240 int ret;
9241 atom = JS_NewAtom(ctx, prop);
9242 ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW);
9243 JS_FreeAtom(ctx, atom);
9244 return ret;
9245}
9246
9247/* compute the property flags. For each flag: (JS_PROP_HAS_x forces
9248 it, otherwise def_flags is used)
9249 Note: makes assumption about the bit pattern of the flags
9250*/
9251static int get_prop_flags(int flags, int def_flags)
9252{
9253 int mask;
9254 mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
9255 return (flags & mask) | (def_flags & ~mask);
9256}
9257
9258static int JS_CreateProperty(JSContext *ctx, JSObject *p,
9259 JSAtom prop, JSValueConst val,
9260 JSValueConst getter, JSValueConst setter,
9261 int flags)
9262{
9263 JSProperty *pr;
9264 int ret, prop_flags;
9265
9266 /* add a new property or modify an existing exotic one */
9267 if (p->is_exotic) {
9268 if (p->class_id == JS_CLASS_ARRAY) {
9269 uint32_t idx, len;
9270
9271 if (p->fast_array) {
9272 if (__JS_AtomIsTaggedInt(prop)) {
9273 idx = __JS_AtomToUInt32(prop);
9274 if (idx == p->u.array.count) {
9275 if (!p->extensible)
9276 goto not_extensible;
9277 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
9278 goto convert_to_array;
9279 prop_flags = get_prop_flags(flags, 0);
9280 if (prop_flags != JS_PROP_C_W_E)
9281 goto convert_to_array;
9282 return add_fast_array_element(ctx, p,
9283 JS_DupValue(ctx, val), flags);
9284 } else {
9285 goto convert_to_array;
9286 }
9287 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
9288 /* convert the fast array to normal array */
9289 convert_to_array:
9290 if (convert_fast_array_to_array(ctx, p))
9291 return -1;
9292 goto generic_array;
9293 }
9294 } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
9295 JSProperty *plen;
9296 JSShapeProperty *pslen;
9297 generic_array:
9298 /* update the length field */
9299 plen = &p->prop[0];
9300 JS_ToUint32(ctx, &len, plen->u.value);
9301 if ((idx + 1) > len) {
9302 pslen = get_shape_prop(p->shape);
9303 if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
9304 return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
9305 /* XXX: should update the length after defining
9306 the property */
9307 len = idx + 1;
9308 set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
9309 }
9310 }
9311 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
9312 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
9313 ret = JS_AtomIsNumericIndex(ctx, prop);
9314 if (ret != 0) {
9315 if (ret < 0)
9316 return -1;
9317 return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
9318 }
9319 } else if (!(flags & JS_PROP_NO_EXOTIC)) {
9320 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
9321 if (em) {
9322 if (em->define_own_property) {
9323 return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
9324 prop, val, getter, setter, flags);
9325 }
9326 ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
9327 if (ret < 0)
9328 return -1;
9329 if (!ret)
9330 goto not_extensible;
9331 }
9332 }
9333 }
9334
9335 if (!p->extensible) {
9336 not_extensible:
9337 return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
9338 }
9339
9340 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9341 prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
9342 JS_PROP_GETSET;
9343 } else {
9344 prop_flags = flags & JS_PROP_C_W_E;
9345 }
9346 pr = add_property(ctx, p, prop, prop_flags);
9347 if (unlikely(!pr))
9348 return -1;
9349 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9350 pr->u.getset.getter = NULL;
9351 if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
9352 pr->u.getset.getter =
9353 JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
9354 }
9355 pr->u.getset.setter = NULL;
9356 if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
9357 pr->u.getset.setter =
9358 JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
9359 }
9360 } else {
9361 if (flags & JS_PROP_HAS_VALUE) {
9362 pr->u.value = JS_DupValue(ctx, val);
9363 } else {
9364 pr->u.value = JS_UNDEFINED;
9365 }
9366 }
9367 return TRUE;
9368}
9369
9370/* return FALSE if not OK */
9371static BOOL check_define_prop_flags(int prop_flags, int flags)
9372{
9373 BOOL has_accessor, is_getset;
9374
9375 if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
9376 if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
9377 (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
9378 return FALSE;
9379 }
9380 if ((flags & JS_PROP_HAS_ENUMERABLE) &&
9381 (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
9382 return FALSE;
9383 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
9384 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9385 has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
9386 is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
9387 if (has_accessor != is_getset)
9388 return FALSE;
9389 if (!is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
9390 /* not writable: cannot set the writable bit */
9391 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
9392 (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
9393 return FALSE;
9394 }
9395 }
9396 }
9397 return TRUE;
9398}
9399
9400/* ensure that the shape can be safely modified */
9401static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
9402 JSShapeProperty **pprs)
9403{
9404 JSShape *sh;
9405 uint32_t idx = 0; /* prevent warning */
9406
9407 sh = p->shape;
9408 if (sh->is_hashed) {
9409 if (sh->header.ref_count != 1) {
9410 if (pprs)
9411 idx = *pprs - get_shape_prop(sh);
9412 /* clone the shape (the resulting one is no longer hashed) */
9413 sh = js_clone_shape(ctx, sh);
9414 if (!sh)
9415 return -1;
9416 js_free_shape(ctx->rt, p->shape);
9417 p->shape = sh;
9418 if (pprs)
9419 *pprs = get_shape_prop(sh) + idx;
9420 } else {
9421 js_shape_hash_unlink(ctx->rt, sh);
9422 sh->is_hashed = FALSE;
9423 }
9424 }
9425 return 0;
9426}
9427
9428static int js_update_property_flags(JSContext *ctx, JSObject *p,
9429 JSShapeProperty **pprs, int flags)
9430{
9431 if (flags != (*pprs)->flags) {
9432 if (js_shape_prepare_update(ctx, p, pprs))
9433 return -1;
9434 (*pprs)->flags = flags;
9435 }
9436 return 0;
9437}
9438
9439/* allowed flags:
9440 JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
9441 JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
9442 JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
9443 JS_PROP_THROW, JS_PROP_NO_EXOTIC.
9444 If JS_PROP_THROW is set, return an exception instead of FALSE.
9445 if JS_PROP_NO_EXOTIC is set, do not call the exotic
9446 define_own_property callback.
9447 return -1 (exception), FALSE or TRUE.
9448*/
9449int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
9450 JSAtom prop, JSValueConst val,
9451 JSValueConst getter, JSValueConst setter, int flags)
9452{
9453 JSObject *p;
9454 JSShapeProperty *prs;
9455 JSProperty *pr;
9456 int mask, res;
9457
9458 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
9459 JS_ThrowTypeErrorNotAnObject(ctx);
9460 return -1;
9461 }
9462 p = JS_VALUE_GET_OBJ(this_obj);
9463
9464 redo_prop_update:
9465 prs = find_own_property(&pr, p, prop);
9466 if (prs) {
9467 /* the range of the Array length property is always tested before */
9468 if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
9469 uint32_t array_length;
9470 if (JS_ToArrayLengthFree(ctx, &array_length,
9471 JS_DupValue(ctx, val), FALSE)) {
9472 return -1;
9473 }
9474 /* this code relies on the fact that Uint32 are never allocated */
9475 val = (JSValueConst)JS_NewUint32(ctx, array_length);
9476 /* prs may have been modified */
9477 prs = find_own_property(&pr, p, prop);
9478 assert(prs != NULL);
9479 }
9480 /* property already exists */
9481 if (!check_define_prop_flags(prs->flags, flags)) {
9482 not_configurable:
9483 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
9484 }
9485
9486 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
9487 /* Instantiate property and retry */
9488 if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
9489 return -1;
9490 goto redo_prop_update;
9491 }
9492
9493 if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
9494 JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9495 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9496 JSObject *new_getter, *new_setter;
9497
9498 if (JS_IsFunction(ctx, getter)) {
9499 new_getter = JS_VALUE_GET_OBJ(getter);
9500 } else {
9501 new_getter = NULL;
9502 }
9503 if (JS_IsFunction(ctx, setter)) {
9504 new_setter = JS_VALUE_GET_OBJ(setter);
9505 } else {
9506 new_setter = NULL;
9507 }
9508
9509 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
9510 if (js_shape_prepare_update(ctx, p, &prs))
9511 return -1;
9512 /* convert to getset */
9513 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9514 free_var_ref(ctx->rt, pr->u.var_ref);
9515 } else {
9516 JS_FreeValue(ctx, pr->u.value);
9517 }
9518 prs->flags = (prs->flags &
9519 (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
9520 JS_PROP_GETSET;
9521 pr->u.getset.getter = NULL;
9522 pr->u.getset.setter = NULL;
9523 } else {
9524 if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
9525 if ((flags & JS_PROP_HAS_GET) &&
9526 new_getter != pr->u.getset.getter) {
9527 goto not_configurable;
9528 }
9529 if ((flags & JS_PROP_HAS_SET) &&
9530 new_setter != pr->u.getset.setter) {
9531 goto not_configurable;
9532 }
9533 }
9534 }
9535 if (flags & JS_PROP_HAS_GET) {
9536 if (pr->u.getset.getter)
9537 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9538 if (new_getter)
9539 JS_DupValue(ctx, getter);
9540 pr->u.getset.getter = new_getter;
9541 }
9542 if (flags & JS_PROP_HAS_SET) {
9543 if (pr->u.getset.setter)
9544 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9545 if (new_setter)
9546 JS_DupValue(ctx, setter);
9547 pr->u.getset.setter = new_setter;
9548 }
9549 } else {
9550 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
9551 /* convert to data descriptor */
9552 if (js_shape_prepare_update(ctx, p, &prs))
9553 return -1;
9554 if (pr->u.getset.getter)
9555 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
9556 if (pr->u.getset.setter)
9557 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
9558 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9559 pr->u.value = JS_UNDEFINED;
9560 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9561 /* Note: JS_PROP_VARREF is always writable */
9562 } else {
9563 if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
9564 (flags & JS_PROP_HAS_VALUE)) {
9565 if (!js_same_value(ctx, val, pr->u.value)) {
9566 goto not_configurable;
9567 } else {
9568 return TRUE;
9569 }
9570 }
9571 }
9572 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
9573 if (flags & JS_PROP_HAS_VALUE) {
9574 if (p->class_id == JS_CLASS_MODULE_NS) {
9575 /* JS_PROP_WRITABLE is always true for variable
9576 references, but they are write protected in module name
9577 spaces. */
9578 if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
9579 goto not_configurable;
9580 } else {
9581 /* update the reference */
9582 set_value(ctx, pr->u.var_ref->pvalue,
9583 JS_DupValue(ctx, val));
9584 }
9585 }
9586 /* if writable is set to false, no longer a
9587 reference (for mapped arguments) */
9588 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
9589 JSValue val1;
9590 if (p->class_id == JS_CLASS_MODULE_NS) {
9591 return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false");
9592 }
9593 if (js_shape_prepare_update(ctx, p, &prs))
9594 return -1;
9595 val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
9596 free_var_ref(ctx->rt, pr->u.var_ref);
9597 pr->u.value = val1;
9598 prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
9599 }
9600 } else if (prs->flags & JS_PROP_LENGTH) {
9601 if (flags & JS_PROP_HAS_VALUE) {
9602 /* Note: no JS code is executable because
9603 'val' is guaranted to be a Uint32 */
9604 res = set_array_length(ctx, p, JS_DupValue(ctx, val),
9605 flags);
9606 } else {
9607 res = TRUE;
9608 }
9609 /* still need to reset the writable flag if
9610 needed. The JS_PROP_LENGTH is kept because the
9611 Uint32 test is still done if the length
9612 property is read-only. */
9613 if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
9614 JS_PROP_HAS_WRITABLE) {
9615 prs = get_shape_prop(p->shape);
9616 if (js_update_property_flags(ctx, p, &prs,
9617 prs->flags & ~JS_PROP_WRITABLE))
9618 return -1;
9619 }
9620 return res;
9621 } else {
9622 if (flags & JS_PROP_HAS_VALUE) {
9623 JS_FreeValue(ctx, pr->u.value);
9624 pr->u.value = JS_DupValue(ctx, val);
9625 }
9626 if (flags & JS_PROP_HAS_WRITABLE) {
9627 if (js_update_property_flags(ctx, p, &prs,
9628 (prs->flags & ~JS_PROP_WRITABLE) |
9629 (flags & JS_PROP_WRITABLE)))
9630 return -1;
9631 }
9632 }
9633 }
9634 }
9635 mask = 0;
9636 if (flags & JS_PROP_HAS_CONFIGURABLE)
9637 mask |= JS_PROP_CONFIGURABLE;
9638 if (flags & JS_PROP_HAS_ENUMERABLE)
9639 mask |= JS_PROP_ENUMERABLE;
9640 if (js_update_property_flags(ctx, p, &prs,
9641 (prs->flags & ~mask) | (flags & mask)))
9642 return -1;
9643 return TRUE;
9644 }
9645
9646 /* handle modification of fast array elements */
9647 if (p->fast_array) {
9648 uint32_t idx;
9649 uint32_t prop_flags;
9650 if (p->class_id == JS_CLASS_ARRAY) {
9651 if (__JS_AtomIsTaggedInt(prop)) {
9652 idx = __JS_AtomToUInt32(prop);
9653 if (idx < p->u.array.count) {
9654 prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
9655 if (prop_flags != JS_PROP_C_W_E)
9656 goto convert_to_slow_array;
9657 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
9658 convert_to_slow_array:
9659 if (convert_fast_array_to_array(ctx, p))
9660 return -1;
9661 else
9662 goto redo_prop_update;
9663 }
9664 if (flags & JS_PROP_HAS_VALUE) {
9665 set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
9666 }
9667 return TRUE;
9668 }
9669 }
9670 } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
9671 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
9672 JSValue num;
9673 int ret;
9674
9675 if (!__JS_AtomIsTaggedInt(prop)) {
9676 /* slow path with to handle all numeric indexes */
9677 num = JS_AtomIsNumericIndex1(ctx, prop);
9678 if (JS_IsUndefined(num))
9679 goto typed_array_done;
9680 if (JS_IsException(num))
9681 return -1;
9682 ret = JS_NumberIsInteger(ctx, num);
9683 if (ret < 0) {
9684 JS_FreeValue(ctx, num);
9685 return -1;
9686 }
9687 if (!ret) {
9688 JS_FreeValue(ctx, num);
9689 return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
9690 }
9691 ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
9692 JS_FreeValue(ctx, num);
9693 if (ret) {
9694 return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
9695 }
9696 if (!__JS_AtomIsTaggedInt(prop))
9697 goto typed_array_oob;
9698 }
9699 idx = __JS_AtomToUInt32(prop);
9700 /* if the typed array is detached, p->u.array.count = 0 */
9701 if (idx >= p->u.array.count) {
9702 typed_array_oob:
9703 return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
9704 }
9705 prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
9706 if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
9707 prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
9708 return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
9709 }
9710 if (flags & JS_PROP_HAS_VALUE) {
9711 return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
9712 }
9713 return TRUE;
9714 typed_array_done: ;
9715 }
9716 }
9717
9718 return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
9719}
9720
9721static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
9722 JSAtom prop, JSAutoInitIDEnum id,
9723 void *opaque, int flags)
9724{
9725 JSObject *p;
9726 JSProperty *pr;
9727
9728 if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
9729 return FALSE;
9730
9731 p = JS_VALUE_GET_OBJ(this_obj);
9732
9733 if (find_own_property(&pr, p, prop)) {
9734 /* property already exists */
9735 abort();
9736 return FALSE;
9737 }
9738
9739 /* Specialized CreateProperty */
9740 pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
9741 if (unlikely(!pr))
9742 return -1;
9743 pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
9744 assert((pr->u.init.realm_and_id & 3) == 0);
9745 assert(id <= 3);
9746 pr->u.init.realm_and_id |= id;
9747 pr->u.init.opaque = opaque;
9748 return TRUE;
9749}
9750
9751/* shortcut to add or redefine a new property value */
9752int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
9753 JSAtom prop, JSValue val, int flags)
9754{
9755 int ret;
9756 ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
9757 flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
9758 JS_FreeValue(ctx, val);
9759 return ret;
9760}
9761
9762int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
9763 JSValue prop, JSValue val, int flags)
9764{
9765 JSAtom atom;
9766 int ret;
9767 atom = JS_ValueToAtom(ctx, prop);
9768 JS_FreeValue(ctx, prop);
9769 if (unlikely(atom == JS_ATOM_NULL)) {
9770 JS_FreeValue(ctx, val);
9771 return -1;
9772 }
9773 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9774 JS_FreeAtom(ctx, atom);
9775 return ret;
9776}
9777
9778int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
9779 uint32_t idx, JSValue val, int flags)
9780{
9781 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
9782 val, flags);
9783}
9784
9785int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
9786 int64_t idx, JSValue val, int flags)
9787{
9788 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9789 val, flags);
9790}
9791
9792int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
9793 const char *prop, JSValue val, int flags)
9794{
9795 JSAtom atom;
9796 int ret;
9797 atom = JS_NewAtom(ctx, prop);
9798 ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
9799 JS_FreeAtom(ctx, atom);
9800 return ret;
9801}
9802
9803/* shortcut to add getter & setter */
9804int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
9805 JSAtom prop, JSValue getter, JSValue setter,
9806 int flags)
9807{
9808 int ret;
9809 ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
9810 flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
9811 JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
9812 JS_FreeValue(ctx, getter);
9813 JS_FreeValue(ctx, setter);
9814 return ret;
9815}
9816
9817static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
9818 int64_t idx, JSValue val, int flags)
9819{
9820 return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
9821 val, flags | JS_PROP_CONFIGURABLE |
9822 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
9823}
9824
9825
9826/* return TRUE if 'obj' has a non empty 'name' string */
9827static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
9828{
9829 JSProperty *pr;
9830 JSShapeProperty *prs;
9831 JSValueConst val;
9832 JSString *p;
9833
9834 prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
9835 if (!prs)
9836 return FALSE;
9837 if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
9838 return TRUE;
9839 val = pr->u.value;
9840 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
9841 return TRUE;
9842 p = JS_VALUE_GET_STRING(val);
9843 return (p->len != 0);
9844}
9845
9846static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
9847 JSAtom name, int flags)
9848{
9849 if (name != JS_ATOM_NULL
9850 && JS_IsObject(obj)
9851 && !js_object_has_name(ctx, obj)
9852 && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
9853 return -1;
9854 }
9855 return 0;
9856}
9857
9858static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
9859 JSValueConst str, int flags)
9860{
9861 if (JS_IsObject(obj) &&
9862 !js_object_has_name(ctx, obj)) {
9863 JSAtom prop;
9864 JSValue name_str;
9865 prop = JS_ValueToAtom(ctx, str);
9866 if (prop == JS_ATOM_NULL)
9867 return -1;
9868 name_str = js_get_function_name(ctx, prop);
9869 JS_FreeAtom(ctx, prop);
9870 if (JS_IsException(name_str))
9871 return -1;
9872 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
9873 return -1;
9874 }
9875 return 0;
9876}
9877
9878#define DEFINE_GLOBAL_LEX_VAR (1 << 7)
9879#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
9880
9881static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
9882{
9883 return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
9884}
9885
9886/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
9887/* XXX: could support exotic global object. */
9888static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
9889{
9890 JSObject *p;
9891 JSShapeProperty *prs;
9892
9893 p = JS_VALUE_GET_OBJ(ctx->global_obj);
9894 prs = find_own_property1(p, prop);
9895 /* XXX: should handle JS_PROP_AUTOINIT */
9896 if (flags & DEFINE_GLOBAL_LEX_VAR) {
9897 if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
9898 goto fail_redeclaration;
9899 } else {
9900 if (!prs && !p->extensible)
9901 goto define_error;
9902 if (flags & DEFINE_GLOBAL_FUNC_VAR) {
9903 if (prs) {
9904 if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
9905 ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
9906 ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
9907 (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
9908 define_error:
9909 JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
9910 prop);
9911 return -1;
9912 }
9913 }
9914 }
9915 }
9916 /* check if there already is a lexical declaration */
9917 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9918 prs = find_own_property1(p, prop);
9919 if (prs) {
9920 fail_redeclaration:
9921 JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
9922 return -1;
9923 }
9924 return 0;
9925}
9926
9927/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
9928 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
9929/* XXX: could support exotic global object. */
9930static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
9931{
9932 JSObject *p;
9933 JSShapeProperty *prs;
9934 JSProperty *pr;
9935 JSValue val;
9936 int flags;
9937
9938 if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
9939 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9940 flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
9941 JS_PROP_CONFIGURABLE;
9942 val = JS_UNINITIALIZED;
9943 } else {
9944 p = JS_VALUE_GET_OBJ(ctx->global_obj);
9945 flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
9946 (def_flags & JS_PROP_CONFIGURABLE);
9947 val = JS_UNDEFINED;
9948 }
9949 prs = find_own_property1(p, prop);
9950 if (prs)
9951 return 0;
9952 if (!p->extensible)
9953 return 0;
9954 pr = add_property(ctx, p, prop, flags);
9955 if (unlikely(!pr))
9956 return -1;
9957 pr->u.value = val;
9958 return 0;
9959}
9960
9961/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
9962/* XXX: could support exotic global object. */
9963static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
9964 JSValueConst func, int def_flags)
9965{
9966
9967 JSObject *p;
9968 JSShapeProperty *prs;
9969 int flags;
9970
9971 p = JS_VALUE_GET_OBJ(ctx->global_obj);
9972 prs = find_own_property1(p, prop);
9973 flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
9974 if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
9975 flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
9976 JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
9977 }
9978 if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
9979 JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
9980 return -1;
9981 return 0;
9982}
9983
9984static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
9985 BOOL throw_ref_error)
9986{
9987 JSObject *p;
9988 JSShapeProperty *prs;
9989 JSProperty *pr;
9990
9991 /* no exotic behavior is possible in global_var_obj */
9992 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
9993 prs = find_own_property(&pr, p, prop);
9994 if (prs) {
9995 /* XXX: should handle JS_PROP_TMASK properties */
9996 if (unlikely(JS_IsUninitialized(pr->u.value)))
9997 return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
9998 return JS_DupValue(ctx, pr->u.value);
9999 }
10000 return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
10001 ctx->global_obj, throw_ref_error);
10002}
10003
10004/* construct a reference to a global variable */
10005static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
10006{
10007 JSObject *p;
10008 JSShapeProperty *prs;
10009 JSProperty *pr;
10010
10011 /* no exotic behavior is possible in global_var_obj */
10012 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
10013 prs = find_own_property(&pr, p, prop);
10014 if (prs) {
10015 /* XXX: should handle JS_PROP_AUTOINIT properties? */
10016 /* XXX: conformance: do these tests in
10017 OP_put_var_ref/OP_get_var_ref ? */
10018 if (unlikely(JS_IsUninitialized(pr->u.value))) {
10019 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
10020 return -1;
10021 }
10022 if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
10023 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
10024 }
10025 sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
10026 } else {
10027 int ret;
10028 ret = JS_HasProperty(ctx, ctx->global_obj, prop);
10029 if (ret < 0)
10030 return -1;
10031 if (ret) {
10032 sp[0] = JS_DupValue(ctx, ctx->global_obj);
10033 } else {
10034 sp[0] = JS_UNDEFINED;
10035 }
10036 }
10037 sp[1] = JS_AtomToValue(ctx, prop);
10038 return 0;
10039}
10040
10041/* use for strict variable access: test if the variable exists */
10042static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
10043{
10044 JSObject *p;
10045 JSShapeProperty *prs;
10046 int ret;
10047
10048 /* no exotic behavior is possible in global_var_obj */
10049 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
10050 prs = find_own_property1(p, prop);
10051 if (prs) {
10052 ret = TRUE;
10053 } else {
10054 ret = JS_HasProperty(ctx, ctx->global_obj, prop);
10055 if (ret < 0)
10056 return -1;
10057 }
10058 return ret;
10059}
10060
10061/* flag = 0: normal variable write
10062 flag = 1: initialize lexical variable
10063 flag = 2: normal variable write, strict check was done before
10064*/
10065static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
10066 int flag)
10067{
10068 JSObject *p;
10069 JSShapeProperty *prs;
10070 JSProperty *pr;
10071 int flags;
10072
10073 /* no exotic behavior is possible in global_var_obj */
10074 p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
10075 prs = find_own_property(&pr, p, prop);
10076 if (prs) {
10077 /* XXX: should handle JS_PROP_AUTOINIT properties? */
10078 if (flag != 1) {
10079 if (unlikely(JS_IsUninitialized(pr->u.value))) {
10080 JS_FreeValue(ctx, val);
10081 JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
10082 return -1;
10083 }
10084 if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
10085 JS_FreeValue(ctx, val);
10086 return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
10087 }
10088 }
10089 set_value(ctx, &pr->u.value, val);
10090 return 0;
10091 }
10092 /* XXX: add a fast path where the property exists and the object
10093 is not exotic. Otherwise do as in OP_put_ref_value and remove
10094 JS_PROP_NO_ADD which is no longer necessary */
10095 flags = JS_PROP_THROW_STRICT;
10096 if (is_strict_mode(ctx))
10097 flags |= JS_PROP_NO_ADD;
10098 return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags);
10099}
10100
10101/* return -1, FALSE or TRUE. return FALSE if not configurable or
10102 invalid object. return -1 in case of exception.
10103 flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
10104int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
10105{
10106 JSValue obj1;
10107 JSObject *p;
10108 int res;
10109
10110 obj1 = JS_ToObject(ctx, obj);
10111 if (JS_IsException(obj1))
10112 return -1;
10113 p = JS_VALUE_GET_OBJ(obj1);
10114 res = delete_property(ctx, p, prop);
10115 JS_FreeValue(ctx, obj1);
10116 if (res != FALSE)
10117 return res;
10118 if ((flags & JS_PROP_THROW) ||
10119 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
10120 JS_ThrowTypeError(ctx, "could not delete property");
10121 return -1;
10122 }
10123 return FALSE;
10124}
10125
10126int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
10127{
10128 JSAtom prop;
10129 int res;
10130
10131 if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
10132 /* fast path for fast arrays */
10133 return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
10134 }
10135 prop = JS_NewAtomInt64(ctx, idx);
10136 if (prop == JS_ATOM_NULL)
10137 return -1;
10138 res = JS_DeleteProperty(ctx, obj, prop, flags);
10139 JS_FreeAtom(ctx, prop);
10140 return res;
10141}
10142
10143BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
10144{
10145 JSObject *p;
10146 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10147 return FALSE;
10148 p = JS_VALUE_GET_OBJ(val);
10149 switch(p->class_id) {
10150 case JS_CLASS_BYTECODE_FUNCTION:
10151 return TRUE;
10152 case JS_CLASS_PROXY:
10153 return p->u.proxy_data->is_func;
10154 default:
10155 return (ctx->rt->class_array[p->class_id].call != NULL);
10156 }
10157}
10158
10159BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
10160{
10161 JSObject *p;
10162 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10163 return FALSE;
10164 p = JS_VALUE_GET_OBJ(val);
10165 if (p->class_id == JS_CLASS_C_FUNCTION)
10166 return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
10167 else
10168 return FALSE;
10169}
10170
10171BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
10172{
10173 JSObject *p;
10174 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10175 return FALSE;
10176 p = JS_VALUE_GET_OBJ(val);
10177 return p->is_constructor;
10178}
10179
10180BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
10181{
10182 JSObject *p;
10183 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
10184 return FALSE;
10185 p = JS_VALUE_GET_OBJ(func_obj);
10186 p->is_constructor = val;
10187 return TRUE;
10188}
10189
10190BOOL JS_IsError(JSContext *ctx, JSValueConst val)
10191{
10192 JSObject *p;
10193 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10194 return FALSE;
10195 p = JS_VALUE_GET_OBJ(val);
10196 return (p->class_id == JS_CLASS_ERROR);
10197}
10198
10199/* used to avoid catching interrupt exceptions */
10200BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
10201{
10202 JSObject *p;
10203 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10204 return FALSE;
10205 p = JS_VALUE_GET_OBJ(val);
10206 return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
10207}
10208
10209void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
10210{
10211 JSObject *p;
10212 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10213 return;
10214 p = JS_VALUE_GET_OBJ(val);
10215 if (p->class_id == JS_CLASS_ERROR)
10216 p->is_uncatchable_error = flag;
10217}
10218
10219void JS_ResetUncatchableError(JSContext *ctx)
10220{
10221 JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
10222}
10223
10224void JS_SetOpaque(JSValue obj, void *opaque)
10225{
10226 JSObject *p;
10227 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
10228 p = JS_VALUE_GET_OBJ(obj);
10229 p->u.opaque = opaque;
10230 }
10231}
10232
10233/* return NULL if not an object of class class_id */
10234void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
10235{
10236 JSObject *p;
10237 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
10238 return NULL;
10239 p = JS_VALUE_GET_OBJ(obj);
10240 if (p->class_id != class_id)
10241 return NULL;
10242 return p->u.opaque;
10243}
10244
10245void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
10246{
10247 void *p = JS_GetOpaque(obj, class_id);
10248 if (unlikely(!p)) {
10249 JS_ThrowTypeErrorInvalidClass(ctx, class_id);
10250 }
10251 return p;
10252}
10253
10254void *JS_GetAnyOpaque(JSValueConst obj, JSClassID *class_id)
10255{
10256 JSObject *p;
10257 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
10258 *class_id = 0;
10259 return NULL;
10260 }
10261 p = JS_VALUE_GET_OBJ(obj);
10262 *class_id = p->class_id;
10263 return p->u.opaque;
10264}
10265
10266static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
10267{
10268 int i;
10269 BOOL force_ordinary;
10270
10271 JSAtom method_name;
10272 JSValue method, ret;
10273 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
10274 return val;
10275 force_ordinary = hint & HINT_FORCE_ORDINARY;
10276 hint &= ~HINT_FORCE_ORDINARY;
10277 if (!force_ordinary) {
10278 method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
10279 if (JS_IsException(method))
10280 goto exception;
10281 /* ECMA says *If exoticToPrim is not undefined* but tests in
10282 test262 use null as a non callable converter */
10283 if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
10284 JSAtom atom;
10285 JSValue arg;
10286 switch(hint) {
10287 case HINT_STRING:
10288 atom = JS_ATOM_string;
10289 break;
10290 case HINT_NUMBER:
10291 atom = JS_ATOM_number;
10292 break;
10293 default:
10294 case HINT_NONE:
10295 atom = JS_ATOM_default;
10296 break;
10297 }
10298 arg = JS_AtomToString(ctx, atom);
10299 ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
10300 JS_FreeValue(ctx, arg);
10301 if (JS_IsException(ret))
10302 goto exception;
10303 JS_FreeValue(ctx, val);
10304 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
10305 return ret;
10306 JS_FreeValue(ctx, ret);
10307 return JS_ThrowTypeError(ctx, "toPrimitive");
10308 }
10309 }
10310 if (hint != HINT_STRING)
10311 hint = HINT_NUMBER;
10312 for(i = 0; i < 2; i++) {
10313 if ((i ^ hint) == 0) {
10314 method_name = JS_ATOM_toString;
10315 } else {
10316 method_name = JS_ATOM_valueOf;
10317 }
10318 method = JS_GetProperty(ctx, val, method_name);
10319 if (JS_IsException(method))
10320 goto exception;
10321 if (JS_IsFunction(ctx, method)) {
10322 ret = JS_CallFree(ctx, method, val, 0, NULL);
10323 if (JS_IsException(ret))
10324 goto exception;
10325 if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
10326 JS_FreeValue(ctx, val);
10327 return ret;
10328 }
10329 JS_FreeValue(ctx, ret);
10330 } else {
10331 JS_FreeValue(ctx, method);
10332 }
10333 }
10334 JS_ThrowTypeError(ctx, "toPrimitive");
10335exception:
10336 JS_FreeValue(ctx, val);
10337 return JS_EXCEPTION;
10338}
10339
10340static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
10341{
10342 return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
10343}
10344
10345void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
10346{
10347 JSObject *p;
10348 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
10349 return;
10350 p = JS_VALUE_GET_OBJ(obj);
10351 p->is_HTMLDDA = TRUE;
10352}
10353
10354static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
10355{
10356 JSObject *p;
10357 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
10358 return FALSE;
10359 p = JS_VALUE_GET_OBJ(obj);
10360 return p->is_HTMLDDA;
10361}
10362
10363static int JS_ToBoolFree(JSContext *ctx, JSValue val)
10364{
10365 uint32_t tag = JS_VALUE_GET_TAG(val);
10366 switch(tag) {
10367 case JS_TAG_INT:
10368 return JS_VALUE_GET_INT(val) != 0;
10369 case JS_TAG_BOOL:
10370 case JS_TAG_NULL:
10371 case JS_TAG_UNDEFINED:
10372 return JS_VALUE_GET_INT(val);
10373 case JS_TAG_EXCEPTION:
10374 return -1;
10375 case JS_TAG_STRING:
10376 {
10377 BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
10378 JS_FreeValue(ctx, val);
10379 return ret;
10380 }
10381 case JS_TAG_STRING_ROPE:
10382 {
10383 BOOL ret = JS_VALUE_GET_STRING_ROPE(val)->len != 0;
10384 JS_FreeValue(ctx, val);
10385 return ret;
10386 }
10387 case JS_TAG_SHORT_BIG_INT:
10388 return JS_VALUE_GET_SHORT_BIG_INT(val) != 0;
10389 case JS_TAG_BIG_INT:
10390 {
10391 JSBigInt *p = JS_VALUE_GET_PTR(val);
10392 BOOL ret;
10393 int i;
10394
10395 /* fail safe: we assume it is not necessarily
10396 normalized. Beginning from the MSB ensures that the
10397 test is fast. */
10398 ret = FALSE;
10399 for(i = p->len - 1; i >= 0; i--) {
10400 if (p->tab[i] != 0) {
10401 ret = TRUE;
10402 break;
10403 }
10404 }
10405 JS_FreeValue(ctx, val);
10406 return ret;
10407 }
10408 case JS_TAG_OBJECT:
10409 {
10410 JSObject *p = JS_VALUE_GET_OBJ(val);
10411 BOOL ret;
10412 ret = !p->is_HTMLDDA;
10413 JS_FreeValue(ctx, val);
10414 return ret;
10415 }
10416 break;
10417 default:
10418 if (JS_TAG_IS_FLOAT64(tag)) {
10419 double d = JS_VALUE_GET_FLOAT64(val);
10420 return !isnan(d) && d != 0;
10421 } else {
10422 JS_FreeValue(ctx, val);
10423 return TRUE;
10424 }
10425 }
10426}
10427
10428int JS_ToBool(JSContext *ctx, JSValueConst val)
10429{
10430 return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
10431}
10432
10433static int skip_spaces(const char *pc)
10434{
10435 const uint8_t *p, *p_next, *p_start;
10436 uint32_t c;
10437
10438 p = p_start = (const uint8_t *)pc;
10439 for (;;) {
10440 c = *p;
10441 if (c < 128) {
10442 if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
10443 break;
10444 p++;
10445 } else {
10446 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
10447 if (!lre_is_space(c))
10448 break;
10449 p = p_next;
10450 }
10451 }
10452 return p - p_start;
10453}
10454
10455static inline int to_digit(int c)
10456{
10457 if (c >= '0' && c <= '9')
10458 return c - '0';
10459 else if (c >= 'A' && c <= 'Z')
10460 return c - 'A' + 10;
10461 else if (c >= 'a' && c <= 'z')
10462 return c - 'a' + 10;
10463 else
10464 return 36;
10465}
10466
10467/* bigint support */
10468
10469#define JS_BIGINT_MAX_SIZE ((1024 * 1024) / JS_LIMB_BITS) /* in limbs */
10470
10471/* it is currently assumed that JS_SHORT_BIG_INT_BITS = JS_LIMB_BITS */
10472#if JS_SHORT_BIG_INT_BITS == 32
10473#define JS_SHORT_BIG_INT_MIN INT32_MIN
10474#define JS_SHORT_BIG_INT_MAX INT32_MAX
10475#elif JS_SHORT_BIG_INT_BITS == 64
10476#define JS_SHORT_BIG_INT_MIN INT64_MIN
10477#define JS_SHORT_BIG_INT_MAX INT64_MAX
10478#else
10479#error unsupported
10480#endif
10481
10482#define ADDC(res, carry_out, op1, op2, carry_in) \
10483do { \
10484 js_limb_t __v, __a, __k, __k1; \
10485 __v = (op1); \
10486 __a = __v + (op2); \
10487 __k1 = __a < __v; \
10488 __k = (carry_in); \
10489 __a = __a + __k; \
10490 carry_out = (__a < __k) | __k1; \
10491 res = __a; \
10492} while (0)
10493
10494#if JS_LIMB_BITS == 32
10495/* a != 0 */
10496static inline js_limb_t js_limb_clz(js_limb_t a)
10497{
10498 return clz32(a);
10499}
10500#else
10501static inline js_limb_t js_limb_clz(js_limb_t a)
10502{
10503 return clz64(a);
10504}
10505#endif
10506
10507static js_limb_t mp_add(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
10508 js_limb_t n, js_limb_t carry)
10509{
10510 int i;
10511 for(i = 0;i < n; i++) {
10512 ADDC(res[i], carry, op1[i], op2[i], carry);
10513 }
10514 return carry;
10515}
10516
10517static js_limb_t mp_sub(js_limb_t *res, const js_limb_t *op1, const js_limb_t *op2,
10518 int n, js_limb_t carry)
10519{
10520 int i;
10521 js_limb_t k, a, v, k1;
10522
10523 k = carry;
10524 for(i=0;i<n;i++) {
10525 v = op1[i];
10526 a = v - op2[i];
10527 k1 = a > v;
10528 v = a - k;
10529 k = (v > a) | k1;
10530 res[i] = v;
10531 }
10532 return k;
10533}
10534
10535/* compute 0 - op2. carry = 0 or 1. */
10536static js_limb_t mp_neg(js_limb_t *res, const js_limb_t *op2, int n)
10537{
10538 int i;
10539 js_limb_t v, carry;
10540
10541 carry = 1;
10542 for(i=0;i<n;i++) {
10543 v = ~op2[i] + carry;
10544 carry = v < carry;
10545 res[i] = v;
10546 }
10547 return carry;
10548}
10549
10550/* tabr[] = taba[] * b + l. Return the high carry */
10551static js_limb_t mp_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
10552 js_limb_t b, js_limb_t l)
10553{
10554 js_limb_t i;
10555 js_dlimb_t t;
10556
10557 for(i = 0; i < n; i++) {
10558 t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l;
10559 tabr[i] = t;
10560 l = t >> JS_LIMB_BITS;
10561 }
10562 return l;
10563}
10564
10565static js_limb_t mp_div1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
10566 js_limb_t b, js_limb_t r)
10567{
10568 js_slimb_t i;
10569 js_dlimb_t a1;
10570 for(i = n - 1; i >= 0; i--) {
10571 a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
10572 tabr[i] = a1 / b;
10573 r = a1 % b;
10574 }
10575 return r;
10576}
10577
10578/* tabr[] += taba[] * b, return the high word. */
10579static js_limb_t mp_add_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
10580 js_limb_t b)
10581{
10582 js_limb_t i, l;
10583 js_dlimb_t t;
10584
10585 l = 0;
10586 for(i = 0; i < n; i++) {
10587 t = (js_dlimb_t)taba[i] * (js_dlimb_t)b + l + tabr[i];
10588 tabr[i] = t;
10589 l = t >> JS_LIMB_BITS;
10590 }
10591 return l;
10592}
10593
10594/* size of the result : op1_size + op2_size. */
10595static void mp_mul_basecase(js_limb_t *result,
10596 const js_limb_t *op1, js_limb_t op1_size,
10597 const js_limb_t *op2, js_limb_t op2_size)
10598{
10599 int i;
10600 js_limb_t r;
10601
10602 result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0);
10603 for(i=1;i<op2_size;i++) {
10604 r = mp_add_mul1(result + i, op1, op1_size, op2[i]);
10605 result[i + op1_size] = r;
10606 }
10607}
10608
10609/* tabr[] -= taba[] * b. Return the value to substract to the high
10610 word. */
10611static js_limb_t mp_sub_mul1(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
10612 js_limb_t b)
10613{
10614 js_limb_t i, l;
10615 js_dlimb_t t;
10616
10617 l = 0;
10618 for(i = 0; i < n; i++) {
10619 t = tabr[i] - (js_dlimb_t)taba[i] * (js_dlimb_t)b - l;
10620 tabr[i] = t;
10621 l = -(t >> JS_LIMB_BITS);
10622 }
10623 return l;
10624}
10625
10626/* WARNING: d must be >= 2^(JS_LIMB_BITS-1) */
10627static inline js_limb_t udiv1norm_init(js_limb_t d)
10628{
10629 js_limb_t a0, a1;
10630 a1 = -d - 1;
10631 a0 = -1;
10632 return (((js_dlimb_t)a1 << JS_LIMB_BITS) | a0) / d;
10633}
10634
10635/* return the quotient and the remainder in '*pr'of 'a1*2^JS_LIMB_BITS+a0
10636 / d' with 0 <= a1 < d. */
10637static inline js_limb_t udiv1norm(js_limb_t *pr, js_limb_t a1, js_limb_t a0,
10638 js_limb_t d, js_limb_t d_inv)
10639{
10640 js_limb_t n1m, n_adj, q, r, ah;
10641 js_dlimb_t a;
10642 n1m = ((js_slimb_t)a0 >> (JS_LIMB_BITS - 1));
10643 n_adj = a0 + (n1m & d);
10644 a = (js_dlimb_t)d_inv * (a1 - n1m) + n_adj;
10645 q = (a >> JS_LIMB_BITS) + a1;
10646 /* compute a - q * r and update q so that the remainder is\
10647 between 0 and d - 1 */
10648 a = ((js_dlimb_t)a1 << JS_LIMB_BITS) | a0;
10649 a = a - (js_dlimb_t)q * d - d;
10650 ah = a >> JS_LIMB_BITS;
10651 q += 1 + ah;
10652 r = (js_limb_t)a + (ah & d);
10653 *pr = r;
10654 return q;
10655}
10656
10657#define UDIV1NORM_THRESHOLD 3
10658
10659/* b must be >= 1 << (JS_LIMB_BITS - 1) */
10660static js_limb_t mp_div1norm(js_limb_t *tabr, const js_limb_t *taba, js_limb_t n,
10661 js_limb_t b, js_limb_t r)
10662{
10663 js_slimb_t i;
10664
10665 if (n >= UDIV1NORM_THRESHOLD) {
10666 js_limb_t b_inv;
10667 b_inv = udiv1norm_init(b);
10668 for(i = n - 1; i >= 0; i--) {
10669 tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv);
10670 }
10671 } else {
10672 js_dlimb_t a1;
10673 for(i = n - 1; i >= 0; i--) {
10674 a1 = ((js_dlimb_t)r << JS_LIMB_BITS) | taba[i];
10675 tabr[i] = a1 / b;
10676 r = a1 % b;
10677 }
10678 }
10679 return r;
10680}
10681
10682/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb
10683 - 1] must be >= 1 << (JS_LIMB_BITS - 1). na - nb must be >= 0. 'taba'
10684 is modified and contains the remainder (nb limbs). tabq[0..na-nb]
10685 contains the quotient with tabq[na - nb] <= 1. */
10686static void mp_divnorm(js_limb_t *tabq, js_limb_t *taba, js_limb_t na,
10687 const js_limb_t *tabb, js_limb_t nb)
10688{
10689 js_limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r;
10690 int i, j;
10691
10692 b1 = tabb[nb - 1];
10693 if (nb == 1) {
10694 taba[0] = mp_div1norm(tabq, taba, na, b1, 0);
10695 return;
10696 }
10697 n = na - nb;
10698
10699 if (n >= UDIV1NORM_THRESHOLD)
10700 b1_inv = udiv1norm_init(b1);
10701 else
10702 b1_inv = 0;
10703
10704 /* first iteration: the quotient is only 0 or 1 */
10705 q = 1;
10706 for(j = nb - 1; j >= 0; j--) {
10707 if (taba[n + j] != tabb[j]) {
10708 if (taba[n + j] < tabb[j])
10709 q = 0;
10710 break;
10711 }
10712 }
10713 tabq[n] = q;
10714 if (q) {
10715 mp_sub(taba + n, taba + n, tabb, nb, 0);
10716 }
10717
10718 for(i = n - 1; i >= 0; i--) {
10719 if (unlikely(taba[i + nb] >= b1)) {
10720 q = -1;
10721 } else if (b1_inv) {
10722 q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv);
10723 } else {
10724 js_dlimb_t al;
10725 al = ((js_dlimb_t)taba[i + nb] << JS_LIMB_BITS) | taba[i + nb - 1];
10726 q = al / b1;
10727 r = al % b1;
10728 }
10729 r = mp_sub_mul1(taba + i, tabb, nb, q);
10730
10731 v = taba[i + nb];
10732 a = v - r;
10733 c = (a > v);
10734 taba[i + nb] = a;
10735
10736 if (c != 0) {
10737 /* negative result */
10738 for(;;) {
10739 q--;
10740 c = mp_add(taba + i, taba + i, tabb, nb, 0);
10741 /* propagate carry and test if positive result */
10742 if (c != 0) {
10743 if (++taba[i + nb] == 0) {
10744 break;
10745 }
10746 }
10747 }
10748 }
10749 tabq[i] = q;
10750 }
10751}
10752
10753/* 1 <= shift <= JS_LIMB_BITS - 1 */
10754static js_limb_t mp_shl(js_limb_t *tabr, const js_limb_t *taba, int n,
10755 int shift)
10756{
10757 int i;
10758 js_limb_t l, v;
10759 l = 0;
10760 for(i = 0; i < n; i++) {
10761 v = taba[i];
10762 tabr[i] = (v << shift) | l;
10763 l = v >> (JS_LIMB_BITS - shift);
10764 }
10765 return l;
10766}
10767
10768/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift).
10769 1 <= shift <= LIMB_BITS - 1 */
10770static js_limb_t mp_shr(js_limb_t *tab_r, const js_limb_t *tab, int n,
10771 int shift, js_limb_t high)
10772{
10773 int i;
10774 js_limb_t l, a;
10775
10776 l = high;
10777 for(i = n - 1; i >= 0; i--) {
10778 a = tab[i];
10779 tab_r[i] = (a >> shift) | (l << (JS_LIMB_BITS - shift));
10780 l = a;
10781 }
10782 return l & (((js_limb_t)1 << shift) - 1);
10783}
10784
10785static JSBigInt *js_bigint_new(JSContext *ctx, int len)
10786{
10787 JSBigInt *r;
10788 if (len > JS_BIGINT_MAX_SIZE) {
10789 JS_ThrowRangeError(ctx, "BigInt is too large to allocate");
10790 return NULL;
10791 }
10792 r = js_malloc(ctx, sizeof(JSBigInt) + len * sizeof(js_limb_t));
10793 if (!r)
10794 return NULL;
10795 r->header.ref_count = 1;
10796 r->len = len;
10797 return r;
10798}
10799
10800static JSBigInt *js_bigint_set_si(JSBigIntBuf *buf, js_slimb_t a)
10801{
10802 JSBigInt *r = (JSBigInt *)buf->big_int_buf;
10803 r->header.ref_count = 0; /* fail safe */
10804 r->len = 1;
10805 r->tab[0] = a;
10806 return r;
10807}
10808
10809static JSBigInt *js_bigint_set_si64(JSBigIntBuf *buf, int64_t a)
10810{
10811#if JS_LIMB_BITS == 64
10812 return js_bigint_set_si(buf, a);
10813#else
10814 JSBigInt *r = (JSBigInt *)buf->big_int_buf;
10815 r->header.ref_count = 0; /* fail safe */
10816 if (a >= INT32_MIN && a <= INT32_MAX) {
10817 r->len = 1;
10818 r->tab[0] = a;
10819 } else {
10820 r->len = 2;
10821 r->tab[0] = a;
10822 r->tab[1] = a >> JS_LIMB_BITS;
10823 }
10824 return r;
10825#endif
10826}
10827
10828/* val must be a short big int */
10829static JSBigInt *js_bigint_set_short(JSBigIntBuf *buf, JSValueConst val)
10830{
10831 return js_bigint_set_si(buf, JS_VALUE_GET_SHORT_BIG_INT(val));
10832}
10833
10834static __maybe_unused void js_bigint_dump1(JSContext *ctx, const char *str,
10835 const js_limb_t *tab, int len)
10836{
10837 int i;
10838 printf("%s: ", str);
10839 for(i = len - 1; i >= 0; i--) {
10840#if JS_LIMB_BITS == 32
10841 printf(" %08x", tab[i]);
10842#else
10843 printf(" %016" PRIx64, tab[i]);
10844#endif
10845 }
10846 printf("\n");
10847}
10848
10849static __maybe_unused void js_bigint_dump(JSContext *ctx, const char *str,
10850 const JSBigInt *p)
10851{
10852 js_bigint_dump1(ctx, str, p->tab, p->len);
10853}
10854
10855static JSBigInt *js_bigint_new_si(JSContext *ctx, js_slimb_t a)
10856{
10857 JSBigInt *r;
10858 r = js_bigint_new(ctx, 1);
10859 if (!r)
10860 return NULL;
10861 r->tab[0] = a;
10862 return r;
10863}
10864
10865static JSBigInt *js_bigint_new_si64(JSContext *ctx, int64_t a)
10866{
10867#if JS_LIMB_BITS == 64
10868 return js_bigint_new_si(ctx, a);
10869#else
10870 if (a >= INT32_MIN && a <= INT32_MAX) {
10871 return js_bigint_new_si(ctx, a);
10872 } else {
10873 JSBigInt *r;
10874 r = js_bigint_new(ctx, 2);
10875 if (!r)
10876 return NULL;
10877 r->tab[0] = a;
10878 r->tab[1] = a >> 32;
10879 return r;
10880 }
10881#endif
10882}
10883
10884static JSBigInt *js_bigint_new_ui64(JSContext *ctx, uint64_t a)
10885{
10886 if (a <= INT64_MAX) {
10887 return js_bigint_new_si64(ctx, a);
10888 } else {
10889 JSBigInt *r;
10890 r = js_bigint_new(ctx, (65 + JS_LIMB_BITS - 1) / JS_LIMB_BITS);
10891 if (!r)
10892 return NULL;
10893#if JS_LIMB_BITS == 64
10894 r->tab[0] = a;
10895 r->tab[1] = 0;
10896#else
10897 r->tab[0] = a;
10898 r->tab[1] = a >> 32;
10899 r->tab[2] = 0;
10900#endif
10901 return r;
10902 }
10903}
10904
10905static JSBigInt *js_bigint_new_di(JSContext *ctx, js_sdlimb_t a)
10906{
10907 JSBigInt *r;
10908 if (a == (js_slimb_t)a) {
10909 r = js_bigint_new(ctx, 1);
10910 if (!r)
10911 return NULL;
10912 r->tab[0] = a;
10913 } else {
10914 r = js_bigint_new(ctx, 2);
10915 if (!r)
10916 return NULL;
10917 r->tab[0] = a;
10918 r->tab[1] = a >> JS_LIMB_BITS;
10919 }
10920 return r;
10921}
10922
10923/* Remove redundant high order limbs. Warning: 'a' may be
10924 reallocated. Can never fail.
10925*/
10926static JSBigInt *js_bigint_normalize1(JSContext *ctx, JSBigInt *a, int l)
10927{
10928 js_limb_t v;
10929
10930 assert(a->header.ref_count == 1);
10931 while (l > 1) {
10932 v = a->tab[l - 1];
10933 if ((v != 0 && v != -1) ||
10934 (v & 1) != (a->tab[l - 2] >> (JS_LIMB_BITS - 1))) {
10935 break;
10936 }
10937 l--;
10938 }
10939 if (l != a->len) {
10940 JSBigInt *a1;
10941 /* realloc to reduce the size */
10942 a->len = l;
10943 a1 = js_realloc(ctx, a, sizeof(JSBigInt) + l * sizeof(js_limb_t));
10944 if (a1)
10945 a = a1;
10946 }
10947 return a;
10948}
10949
10950static JSBigInt *js_bigint_normalize(JSContext *ctx, JSBigInt *a)
10951{
10952 return js_bigint_normalize1(ctx, a, a->len);
10953}
10954
10955/* return 0 or 1 depending on the sign */
10956static inline int js_bigint_sign(const JSBigInt *a)
10957{
10958 return a->tab[a->len - 1] >> (JS_LIMB_BITS - 1);
10959}
10960
10961static js_slimb_t js_bigint_get_si_sat(const JSBigInt *a)
10962{
10963 if (a->len == 1) {
10964 return a->tab[0];
10965 } else {
10966#if JS_LIMB_BITS == 32
10967 if (js_bigint_sign(a))
10968 return INT32_MIN;
10969 else
10970 return INT32_MAX;
10971#else
10972 if (js_bigint_sign(a))
10973 return INT64_MIN;
10974 else
10975 return INT64_MAX;
10976#endif
10977 }
10978}
10979
10980/* add the op1 limb */
10981static JSBigInt *js_bigint_extend(JSContext *ctx, JSBigInt *r,
10982 js_limb_t op1)
10983{
10984 int n2 = r->len;
10985 if ((op1 != 0 && op1 != -1) ||
10986 (op1 & 1) != r->tab[n2 - 1] >> (JS_LIMB_BITS - 1)) {
10987 JSBigInt *r1;
10988 r1 = js_realloc(ctx, r,
10989 sizeof(JSBigInt) + (n2 + 1) * sizeof(js_limb_t));
10990 if (!r1) {
10991 js_free(ctx, r);
10992 return NULL;
10993 }
10994 r = r1;
10995 r->len = n2 + 1;
10996 r->tab[n2] = op1;
10997 } else {
10998 /* otherwise still need to normalize the result */
10999 r = js_bigint_normalize(ctx, r);
11000 }
11001 return r;
11002}
11003
11004/* return NULL in case of error. Compute a + b (b_neg = 0) or a - b
11005 (b_neg = 1) */
11006/* XXX: optimize */
11007static JSBigInt *js_bigint_add(JSContext *ctx, const JSBigInt *a,
11008 const JSBigInt *b, int b_neg)
11009{
11010 JSBigInt *r;
11011 int n1, n2, i;
11012 js_limb_t carry, op1, op2, a_sign, b_sign;
11013
11014 n2 = max_int(a->len, b->len);
11015 n1 = min_int(a->len, b->len);
11016 r = js_bigint_new(ctx, n2);
11017 if (!r)
11018 return NULL;
11019 /* XXX: optimize */
11020 /* common part */
11021 carry = b_neg;
11022 for(i = 0; i < n1; i++) {
11023 op1 = a->tab[i];
11024 op2 = b->tab[i] ^ (-b_neg);
11025 ADDC(r->tab[i], carry, op1, op2, carry);
11026 }
11027 a_sign = -js_bigint_sign(a);
11028 b_sign = (-js_bigint_sign(b)) ^ (-b_neg);
11029 /* part with sign extension of one operand */
11030 if (a->len > b->len) {
11031 for(i = n1; i < n2; i++) {
11032 op1 = a->tab[i];
11033 ADDC(r->tab[i], carry, op1, b_sign, carry);
11034 }
11035 } else if (a->len < b->len) {
11036 for(i = n1; i < n2; i++) {
11037 op2 = b->tab[i] ^ (-b_neg);
11038 ADDC(r->tab[i], carry, a_sign, op2, carry);
11039 }
11040 }
11041
11042 /* part with sign extension for both operands. Extend the result
11043 if necessary */
11044 return js_bigint_extend(ctx, r, a_sign + b_sign + carry);
11045}
11046
11047/* XXX: optimize */
11048static JSBigInt *js_bigint_neg(JSContext *ctx, const JSBigInt *a)
11049{
11050 JSBigIntBuf buf;
11051 JSBigInt *b;
11052 b = js_bigint_set_si(&buf, 0);
11053 return js_bigint_add(ctx, b, a, 1);
11054}
11055
11056static JSBigInt *js_bigint_mul(JSContext *ctx, const JSBigInt *a,
11057 const JSBigInt *b)
11058{
11059 JSBigInt *r;
11060
11061 r = js_bigint_new(ctx, a->len + b->len);
11062 if (!r)
11063 return NULL;
11064 mp_mul_basecase(r->tab, a->tab, a->len, b->tab, b->len);
11065 /* correct the result if negative operands (no overflow is
11066 possible) */
11067 if (js_bigint_sign(a))
11068 mp_sub(r->tab + a->len, r->tab + a->len, b->tab, b->len, 0);
11069 if (js_bigint_sign(b))
11070 mp_sub(r->tab + b->len, r->tab + b->len, a->tab, a->len, 0);
11071 return js_bigint_normalize(ctx, r);
11072}
11073
11074/* return the division or the remainder. 'b' must be != 0. return NULL
11075 in case of exception (division by zero or memory error) */
11076static JSBigInt *js_bigint_divrem(JSContext *ctx, const JSBigInt *a,
11077 const JSBigInt *b, BOOL is_rem)
11078{
11079 JSBigInt *r, *q;
11080 js_limb_t *tabb, h;
11081 int na, nb, a_sign, b_sign, shift;
11082
11083 if (b->len == 1 && b->tab[0] == 0) {
11084 JS_ThrowRangeError(ctx, "BigInt division by zero");
11085 return NULL;
11086 }
11087
11088 a_sign = js_bigint_sign(a);
11089 b_sign = js_bigint_sign(b);
11090 na = a->len;
11091 nb = b->len;
11092
11093 r = js_bigint_new(ctx, na + 2);
11094 if (!r)
11095 return NULL;
11096 if (a_sign) {
11097 mp_neg(r->tab, a->tab, na);
11098 } else {
11099 memcpy(r->tab, a->tab, na * sizeof(a->tab[0]));
11100 }
11101 /* normalize */
11102 while (na > 1 && r->tab[na - 1] == 0)
11103 na--;
11104
11105 tabb = js_malloc(ctx, nb * sizeof(tabb[0]));
11106 if (!tabb) {
11107 js_free(ctx, r);
11108 return NULL;
11109 }
11110 if (b_sign) {
11111 mp_neg(tabb, b->tab, nb);
11112 } else {
11113 memcpy(tabb, b->tab, nb * sizeof(tabb[0]));
11114 }
11115 /* normalize */
11116 while (nb > 1 && tabb[nb - 1] == 0)
11117 nb--;
11118
11119 /* trivial case if 'a' is small */
11120 if (na < nb) {
11121 js_free(ctx, r);
11122 js_free(ctx, tabb);
11123 if (is_rem) {
11124 /* r = a */
11125 r = js_bigint_new(ctx, a->len);
11126 if (!r)
11127 return NULL;
11128 memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
11129 return r;
11130 } else {
11131 /* q = 0 */
11132 return js_bigint_new_si(ctx, 0);
11133 }
11134 }
11135
11136 /* normalize 'b' */
11137 shift = js_limb_clz(tabb[nb - 1]);
11138 if (shift != 0) {
11139 mp_shl(tabb, tabb, nb, shift);
11140 h = mp_shl(r->tab, r->tab, na, shift);
11141 if (h != 0)
11142 r->tab[na++] = h;
11143 }
11144
11145 q = js_bigint_new(ctx, na - nb + 2); /* one more limb for the sign */
11146 if (!q) {
11147 js_free(ctx, r);
11148 js_free(ctx, tabb);
11149 return NULL;
11150 }
11151
11152 // js_bigint_dump1(ctx, "a", r->tab, na);
11153 // js_bigint_dump1(ctx, "b", tabb, nb);
11154 mp_divnorm(q->tab, r->tab, na, tabb, nb);
11155 js_free(ctx, tabb);
11156
11157 if (is_rem) {
11158 js_free(ctx, q);
11159 if (shift != 0)
11160 mp_shr(r->tab, r->tab, nb, shift, 0);
11161 r->tab[nb++] = 0;
11162 if (a_sign)
11163 mp_neg(r->tab, r->tab, nb);
11164 r = js_bigint_normalize1(ctx, r, nb);
11165 return r;
11166 } else {
11167 js_free(ctx, r);
11168 q->tab[na - nb + 1] = 0;
11169 if (a_sign ^ b_sign) {
11170 mp_neg(q->tab, q->tab, q->len);
11171 }
11172 q = js_bigint_normalize(ctx, q);
11173 return q;
11174 }
11175}
11176
11177/* and, or, xor */
11178static JSBigInt *js_bigint_logic(JSContext *ctx, const JSBigInt *a,
11179 const JSBigInt *b, OPCodeEnum op)
11180{
11181 JSBigInt *r;
11182 js_limb_t b_sign;
11183 int a_len, b_len, i;
11184
11185 if (a->len < b->len) {
11186 const JSBigInt *tmp;
11187 tmp = a;
11188 a = b;
11189 b = tmp;
11190 }
11191 /* a_len >= b_len */
11192 a_len = a->len;
11193 b_len = b->len;
11194 b_sign = -js_bigint_sign(b);
11195
11196 r = js_bigint_new(ctx, a_len);
11197 if (!r)
11198 return NULL;
11199 switch(op) {
11200 case OP_or:
11201 for(i = 0; i < b_len; i++) {
11202 r->tab[i] = a->tab[i] | b->tab[i];
11203 }
11204 for(i = b_len; i < a_len; i++) {
11205 r->tab[i] = a->tab[i] | b_sign;
11206 }
11207 break;
11208 case OP_and:
11209 for(i = 0; i < b_len; i++) {
11210 r->tab[i] = a->tab[i] & b->tab[i];
11211 }
11212 for(i = b_len; i < a_len; i++) {
11213 r->tab[i] = a->tab[i] & b_sign;
11214 }
11215 break;
11216 case OP_xor:
11217 for(i = 0; i < b_len; i++) {
11218 r->tab[i] = a->tab[i] ^ b->tab[i];
11219 }
11220 for(i = b_len; i < a_len; i++) {
11221 r->tab[i] = a->tab[i] ^ b_sign;
11222 }
11223 break;
11224 default:
11225 abort();
11226 }
11227 return js_bigint_normalize(ctx, r);
11228}
11229
11230static JSBigInt *js_bigint_not(JSContext *ctx, const JSBigInt *a)
11231{
11232 JSBigInt *r;
11233 int i;
11234
11235 r = js_bigint_new(ctx, a->len);
11236 if (!r)
11237 return NULL;
11238 for(i = 0; i < a->len; i++) {
11239 r->tab[i] = ~a->tab[i];
11240 }
11241 /* no normalization is needed */
11242 return r;
11243}
11244
11245static JSBigInt *js_bigint_shl(JSContext *ctx, const JSBigInt *a,
11246 unsigned int shift1)
11247{
11248 int d, i, shift;
11249 JSBigInt *r;
11250 js_limb_t l;
11251
11252 if (a->len == 1 && a->tab[0] == 0)
11253 return js_bigint_new_si(ctx, 0); /* zero case */
11254 d = shift1 / JS_LIMB_BITS;
11255 shift = shift1 % JS_LIMB_BITS;
11256 r = js_bigint_new(ctx, a->len + d);
11257 if (!r)
11258 return NULL;
11259 for(i = 0; i < d; i++)
11260 r->tab[i] = 0;
11261 if (shift == 0) {
11262 for(i = 0; i < a->len; i++) {
11263 r->tab[i + d] = a->tab[i];
11264 }
11265 } else {
11266 l = mp_shl(r->tab + d, a->tab, a->len, shift);
11267 if (js_bigint_sign(a))
11268 l |= (js_limb_t)(-1) << shift;
11269 r = js_bigint_extend(ctx, r, l);
11270 }
11271 return r;
11272}
11273
11274static JSBigInt *js_bigint_shr(JSContext *ctx, const JSBigInt *a,
11275 unsigned int shift1)
11276{
11277 int d, i, shift, a_sign, n1;
11278 JSBigInt *r;
11279
11280 d = shift1 / JS_LIMB_BITS;
11281 shift = shift1 % JS_LIMB_BITS;
11282 a_sign = js_bigint_sign(a);
11283 if (d >= a->len)
11284 return js_bigint_new_si(ctx, -a_sign);
11285 n1 = a->len - d;
11286 r = js_bigint_new(ctx, n1);
11287 if (!r)
11288 return NULL;
11289 if (shift == 0) {
11290 for(i = 0; i < n1; i++) {
11291 r->tab[i] = a->tab[i + d];
11292 }
11293 /* no normalization is needed */
11294 } else {
11295 mp_shr(r->tab, a->tab + d, n1, shift, -a_sign);
11296 r = js_bigint_normalize(ctx, r);
11297 }
11298 return r;
11299}
11300
11301static JSBigInt *js_bigint_pow(JSContext *ctx, const JSBigInt *a, JSBigInt *b)
11302{
11303 uint32_t e;
11304 int n_bits, i;
11305 JSBigInt *r, *r1;
11306
11307 /* b must be >= 0 */
11308 if (js_bigint_sign(b)) {
11309 JS_ThrowRangeError(ctx, "BigInt negative exponent");
11310 return NULL;
11311 }
11312 if (b->len == 1 && b->tab[0] == 0) {
11313 /* a^0 = 1 */
11314 return js_bigint_new_si(ctx, 1);
11315 } else if (a->len == 1) {
11316 js_limb_t v;
11317 BOOL is_neg;
11318
11319 v = a->tab[0];
11320 if (v <= 1)
11321 return js_bigint_new_si(ctx, v);
11322 else if (v == -1)
11323 return js_bigint_new_si(ctx, 1 - 2 * (b->tab[0] & 1));
11324 is_neg = (js_slimb_t)v < 0;
11325 if (is_neg)
11326 v = -v;
11327 if ((v & (v - 1)) == 0) {
11328 uint64_t e1;
11329 int n;
11330 /* v = 2^n */
11331 n = JS_LIMB_BITS - 1 - js_limb_clz(v);
11332 if (b->len > 1)
11333 goto overflow;
11334 if (b->tab[0] > INT32_MAX)
11335 goto overflow;
11336 e = b->tab[0];
11337 e1 = (uint64_t)e * n;
11338 if (e1 > JS_BIGINT_MAX_SIZE * JS_LIMB_BITS)
11339 goto overflow;
11340 e = e1;
11341 if (is_neg)
11342 is_neg = b->tab[0] & 1;
11343 r = js_bigint_new(ctx,
11344 (e + JS_LIMB_BITS + 1 - is_neg) / JS_LIMB_BITS);
11345 if (!r)
11346 return NULL;
11347 memset(r->tab, 0, sizeof(r->tab[0]) * r->len);
11348 r->tab[e / JS_LIMB_BITS] =
11349 (js_limb_t)(1 - 2 * is_neg) << (e % JS_LIMB_BITS);
11350 return r;
11351 }
11352 }
11353 if (b->len > 1)
11354 goto overflow;
11355 if (b->tab[0] > INT32_MAX)
11356 goto overflow;
11357 e = b->tab[0];
11358 n_bits = 32 - clz32(e);
11359
11360 r = js_bigint_new(ctx, a->len);
11361 if (!r)
11362 return NULL;
11363 memcpy(r->tab, a->tab, a->len * sizeof(a->tab[0]));
11364 for(i = n_bits - 2; i >= 0; i--) {
11365 r1 = js_bigint_mul(ctx, r, r);
11366 if (!r1)
11367 return NULL;
11368 js_free(ctx, r);
11369 r = r1;
11370 if ((e >> i) & 1) {
11371 r1 = js_bigint_mul(ctx, r, a);
11372 if (!r1)
11373 return NULL;
11374 js_free(ctx, r);
11375 r = r1;
11376 }
11377 }
11378 return r;
11379 overflow:
11380 JS_ThrowRangeError(ctx, "BigInt is too large");
11381 return NULL;
11382}
11383
11384/* return (mant, exp) so that abs(a) ~ mant*2^(exp - (limb_bits -
11385 1). a must be != 0. */
11386static uint64_t js_bigint_get_mant_exp(JSContext *ctx,
11387 int *pexp, const JSBigInt *a)
11388{
11389 js_limb_t t[4 - JS_LIMB_BITS / 32], carry, v, low_bits;
11390 int n1, n2, sgn, shift, i, j, e;
11391 uint64_t a1, a0;
11392
11393 n2 = 4 - JS_LIMB_BITS / 32;
11394 n1 = a->len - n2;
11395 sgn = js_bigint_sign(a);
11396
11397 /* low_bits != 0 if there are a non zero low bit in abs(a) */
11398 low_bits = 0;
11399 carry = sgn;
11400 for(i = 0; i < n1; i++) {
11401 v = (a->tab[i] ^ (-sgn)) + carry;
11402 carry = v < carry;
11403 low_bits |= v;
11404 }
11405 /* get the n2 high limbs of abs(a) */
11406 for(j = 0; j < n2; j++) {
11407 i = j + n1;
11408 if (i < 0) {
11409 v = 0;
11410 } else {
11411 v = (a->tab[i] ^ (-sgn)) + carry;
11412 carry = v < carry;
11413 }
11414 t[j] = v;
11415 }
11416
11417#if JS_LIMB_BITS == 32
11418 a1 = ((uint64_t)t[2] << 32) | t[1];
11419 a0 = (uint64_t)t[0] << 32;
11420#else
11421 a1 = t[1];
11422 a0 = t[0];
11423#endif
11424 a0 |= (low_bits != 0);
11425 /* normalize */
11426 if (a1 == 0) {
11427 /* JS_LIMB_BITS = 64 bit only */
11428 shift = 64;
11429 a1 = a0;
11430 a0 = 0;
11431 } else {
11432 shift = clz64(a1);
11433 if (shift != 0) {
11434 a1 = (a1 << shift) | (a0 >> (64 - shift));
11435 a0 <<= shift;
11436 }
11437 }
11438 a1 |= (a0 != 0); /* keep the bits for the final rounding */
11439 /* compute the exponent */
11440 e = a->len * JS_LIMB_BITS - shift - 1;
11441 *pexp = e;
11442 return a1;
11443}
11444
11445/* shift left with round to nearest, ties to even. n >= 1 */
11446static uint64_t shr_rndn(uint64_t a, int n)
11447{
11448 uint64_t addend = ((a >> n) & 1) + ((1 << (n - 1)) - 1);
11449 return (a + addend) >> n;
11450}
11451
11452/* convert to float64 with round to nearest, ties to even. Return
11453 +/-infinity if too large. */
11454static double js_bigint_to_float64(JSContext *ctx, const JSBigInt *a)
11455{
11456 int sgn, e;
11457 uint64_t mant;
11458
11459 if (a->len == 1) {
11460 /* fast case, including zero */
11461 return (double)(js_slimb_t)a->tab[0];
11462 }
11463
11464 sgn = js_bigint_sign(a);
11465 mant = js_bigint_get_mant_exp(ctx, &e, a);
11466 if (e > 1023) {
11467 /* overflow: return infinity */
11468 mant = 0;
11469 e = 1024;
11470 } else {
11471 mant = (mant >> 1) | (mant & 1); /* avoid overflow in rounding */
11472 mant = shr_rndn(mant, 10);
11473 /* rounding can cause an overflow */
11474 if (mant >= ((uint64_t)1 << 53)) {
11475 mant >>= 1;
11476 e++;
11477 }
11478 mant &= (((uint64_t)1 << 52) - 1);
11479 }
11480 return uint64_as_float64(((uint64_t)sgn << 63) |
11481 ((uint64_t)(e + 1023) << 52) |
11482 mant);
11483}
11484
11485/* return (1, NULL) if not an integer, (2, NULL) if NaN or Infinity,
11486 (0, n) if an integer, (0, NULL) in case of memory error */
11487static JSBigInt *js_bigint_from_float64(JSContext *ctx, int *pres, double a1)
11488{
11489 uint64_t a = float64_as_uint64(a1);
11490 int sgn, e, shift;
11491 uint64_t mant;
11492 JSBigIntBuf buf;
11493 JSBigInt *r;
11494
11495 sgn = a >> 63;
11496 e = (a >> 52) & ((1 << 11) - 1);
11497 mant = a & (((uint64_t)1 << 52) - 1);
11498 if (e == 2047) {
11499 /* NaN, Infinity */
11500 *pres = 2;
11501 return NULL;
11502 }
11503 if (e == 0 && mant == 0) {
11504 /* zero */
11505 *pres = 0;
11506 return js_bigint_new_si(ctx, 0);
11507 }
11508 e -= 1023;
11509 /* 0 < a < 1 : not an integer */
11510 if (e < 0)
11511 goto not_an_integer;
11512 mant |= (uint64_t)1 << 52;
11513 if (e < 52) {
11514 shift = 52 - e;
11515 /* check that there is no fractional part */
11516 if (mant & (((uint64_t)1 << shift) - 1)) {
11517 not_an_integer:
11518 *pres = 1;
11519 return NULL;
11520 }
11521 mant >>= shift;
11522 e = 0;
11523 } else {
11524 e -= 52;
11525 }
11526 if (sgn)
11527 mant = -mant;
11528 /* the integer is mant*2^e */
11529 r = js_bigint_set_si64(&buf, (int64_t)mant);
11530 *pres = 0;
11531 return js_bigint_shl(ctx, r, e);
11532}
11533
11534/* return -1, 0, 1 or (2) (unordered) */
11535static int js_bigint_float64_cmp(JSContext *ctx, const JSBigInt *a,
11536 double b)
11537{
11538 int b_sign, a_sign, e, f;
11539 uint64_t mant, b1, a_mant;
11540
11541 b1 = float64_as_uint64(b);
11542 b_sign = b1 >> 63;
11543 e = (b1 >> 52) & ((1 << 11) - 1);
11544 mant = b1 & (((uint64_t)1 << 52) - 1);
11545 a_sign = js_bigint_sign(a);
11546 if (e == 2047) {
11547 if (mant != 0) {
11548 /* NaN */
11549 return 2;
11550 } else {
11551 /* +/- infinity */
11552 return 2 * b_sign - 1;
11553 }
11554 } else if (e == 0 && mant == 0) {
11555 /* b = +/-0 */
11556 if (a->len == 1 && a->tab[0] == 0)
11557 return 0;
11558 else
11559 return 1 - 2 * a_sign;
11560 } else if (a->len == 1 && a->tab[0] == 0) {
11561 /* a = 0, b != 0 */
11562 return 2 * b_sign - 1;
11563 } else if (a_sign != b_sign) {
11564 return 1 - 2 * a_sign;
11565 } else {
11566 e -= 1023;
11567 /* Note: handling denormals is not necessary because we
11568 compare to integers hence f >= 0 */
11569 /* compute f so that 2^f <= abs(a) < 2^(f+1) */
11570 a_mant = js_bigint_get_mant_exp(ctx, &f, a);
11571 if (f != e) {
11572 if (f < e)
11573 return -1;
11574 else
11575 return 1;
11576 } else {
11577 mant = (mant | ((uint64_t)1 << 52)) << 11; /* align to a_mant */
11578 if (a_mant < mant)
11579 return 2 * a_sign - 1;
11580 else if (a_mant > mant)
11581 return 1 - 2 * a_sign;
11582 else
11583 return 0;
11584 }
11585 }
11586}
11587
11588/* return -1, 0 or 1 */
11589static int js_bigint_cmp(JSContext *ctx, const JSBigInt *a,
11590 const JSBigInt *b)
11591{
11592 int a_sign, b_sign, res, i;
11593 a_sign = js_bigint_sign(a);
11594 b_sign = js_bigint_sign(b);
11595 if (a_sign != b_sign) {
11596 res = 1 - 2 * a_sign;
11597 } else {
11598 /* we assume the numbers are normalized */
11599 if (a->len != b->len) {
11600 if (a->len < b->len)
11601 res = 2 * a_sign - 1;
11602 else
11603 res = 1 - 2 * a_sign;
11604 } else {
11605 res = 0;
11606 for(i = a->len -1; i >= 0; i--) {
11607 if (a->tab[i] != b->tab[i]) {
11608 if (a->tab[i] < b->tab[i])
11609 res = -1;
11610 else
11611 res = 1;
11612 break;
11613 }
11614 }
11615 }
11616 }
11617 return res;
11618}
11619
11620/* contains 10^i */
11621static const js_limb_t js_pow_dec[JS_LIMB_DIGITS + 1] = {
11622 1U,
11623 10U,
11624 100U,
11625 1000U,
11626 10000U,
11627 100000U,
11628 1000000U,
11629 10000000U,
11630 100000000U,
11631 1000000000U,
11632#if JS_LIMB_BITS == 64
11633 10000000000U,
11634 100000000000U,
11635 1000000000000U,
11636 10000000000000U,
11637 100000000000000U,
11638 1000000000000000U,
11639 10000000000000000U,
11640 100000000000000000U,
11641 1000000000000000000U,
11642 10000000000000000000U,
11643#endif
11644};
11645
11646/* syntax: [-]digits in base radix. Return NULL if memory error. radix
11647 = 10, 2, 8 or 16. */
11648static JSBigInt *js_bigint_from_string(JSContext *ctx,
11649 const char *str, int radix)
11650{
11651 const char *p = str;
11652 int is_neg, n_digits, n_limbs, len, log2_radix, n_bits, i;
11653 JSBigInt *r;
11654 js_limb_t v, c, h;
11655
11656 is_neg = 0;
11657 if (*p == '-') {
11658 is_neg = 1;
11659 p++;
11660 }
11661 while (*p == '0')
11662 p++;
11663 n_digits = strlen(p);
11664 log2_radix = 32 - clz32(radix - 1); /* ceil(log2(radix)) */
11665 /* compute the maximum number of limbs */
11666 /* XXX: overflow */
11667 if (radix == 10) {
11668 n_bits = (n_digits * 27 + 7) / 8; /* >= ceil(n_digits * log2(10)) */
11669 } else {
11670 n_bits = n_digits * log2_radix;
11671 }
11672 /* we add one extra bit for the sign */
11673 n_limbs = max_int(1, n_bits / JS_LIMB_BITS + 1);
11674 r = js_bigint_new(ctx, n_limbs);
11675 if (!r)
11676 return NULL;
11677 if (radix == 10) {
11678 int digits_per_limb = JS_LIMB_DIGITS;
11679 len = 1;
11680 r->tab[0] = 0;
11681 for(;;) {
11682 /* XXX: slow */
11683 v = 0;
11684 for(i = 0; i < digits_per_limb; i++) {
11685 c = to_digit(*p);
11686 if (c >= radix)
11687 break;
11688 p++;
11689 v = v * 10 + c;
11690 }
11691 if (i == 0)
11692 break;
11693 if (len == 1 && r->tab[0] == 0) {
11694 r->tab[0] = v;
11695 } else {
11696 h = mp_mul1(r->tab, r->tab, len, js_pow_dec[i], v);
11697 if (h != 0) {
11698 r->tab[len++] = h;
11699 }
11700 }
11701 }
11702 /* add one extra limb to have the correct sign*/
11703 if ((r->tab[len - 1] >> (JS_LIMB_BITS - 1)) != 0)
11704 r->tab[len++] = 0;
11705 r->len = len;
11706 } else {
11707 unsigned int bit_pos, shift, pos;
11708
11709 /* power of two base: no multiplication is needed */
11710 r->len = n_limbs;
11711 memset(r->tab, 0, sizeof(r->tab[0]) * n_limbs);
11712 for(i = 0; i < n_digits; i++) {
11713 c = to_digit(p[n_digits - 1 - i]);
11714 assert(c < radix);
11715 bit_pos = i * log2_radix;
11716 shift = bit_pos & (JS_LIMB_BITS - 1);
11717 pos = bit_pos / JS_LIMB_BITS;
11718 r->tab[pos] |= c << shift;
11719 /* if log2_radix does not divide JS_LIMB_BITS, needed an
11720 additional op */
11721 if (shift + log2_radix > JS_LIMB_BITS) {
11722 r->tab[pos + 1] |= c >> (JS_LIMB_BITS - shift);
11723 }
11724 }
11725 }
11726 r = js_bigint_normalize(ctx, r);
11727 /* XXX: could do it in place */
11728 if (is_neg) {
11729 JSBigInt *r1;
11730 r1 = js_bigint_neg(ctx, r);
11731 js_free(ctx, r);
11732 r = r1;
11733 }
11734 return r;
11735}
11736
11737/* 2 <= base <= 36 */
11738static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
11739
11740/* special version going backwards */
11741/* XXX: use dtoa.c */
11742static char *js_u64toa(char *q, int64_t n, unsigned int base)
11743{
11744 int digit;
11745 if (base == 10) {
11746 /* division by known base uses multiplication */
11747 do {
11748 digit = (uint64_t)n % 10;
11749 n = (uint64_t)n / 10;
11750 *--q = '0' + digit;
11751 } while (n != 0);
11752 } else {
11753 do {
11754 digit = (uint64_t)n % base;
11755 n = (uint64_t)n / base;
11756 *--q = digits[digit];
11757 } while (n != 0);
11758 }
11759 return q;
11760}
11761
11762/* len >= 1. 2 <= radix <= 36 */
11763static char *limb_to_a(char *q, js_limb_t n, unsigned int radix, int len)
11764{
11765 int digit, i;
11766
11767 if (radix == 10) {
11768 /* specific case with constant divisor */
11769 /* XXX: optimize */
11770 for(i = 0; i < len; i++) {
11771 digit = (js_limb_t)n % 10;
11772 n = (js_limb_t)n / 10;
11773 *--q = digit + '0';
11774 }
11775 } else {
11776 for(i = 0; i < len; i++) {
11777 digit = (js_limb_t)n % radix;
11778 n = (js_limb_t)n / radix;
11779 *--q = digits[digit];
11780 }
11781 }
11782 return q;
11783}
11784
11785#define JS_RADIX_MAX 36
11786
11787static const uint8_t digits_per_limb_table[JS_RADIX_MAX - 1] = {
11788#if JS_LIMB_BITS == 32
1178932,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
11790#else
1179164,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12,
11792#endif
11793};
11794
11795static const js_limb_t radix_base_table[JS_RADIX_MAX - 1] = {
11796#if JS_LIMB_BITS == 32
11797 0x00000000, 0xcfd41b91, 0x00000000, 0x48c27395,
11798 0x81bf1000, 0x75db9c97, 0x40000000, 0xcfd41b91,
11799 0x3b9aca00, 0x8c8b6d2b, 0x19a10000, 0x309f1021,
11800 0x57f6c100, 0x98c29b81, 0x00000000, 0x18754571,
11801 0x247dbc80, 0x3547667b, 0x4c4b4000, 0x6b5a6e1d,
11802 0x94ace180, 0xcaf18367, 0x0b640000, 0x0e8d4a51,
11803 0x1269ae40, 0x17179149, 0x1cb91000, 0x23744899,
11804 0x2b73a840, 0x34e63b41, 0x40000000, 0x4cfa3cc1,
11805 0x5c13d840, 0x6d91b519, 0x81bf1000,
11806#else
11807 0x0000000000000000, 0xa8b8b452291fe821, 0x0000000000000000, 0x6765c793fa10079d,
11808 0x41c21cb8e1000000, 0x3642798750226111, 0x8000000000000000, 0xa8b8b452291fe821,
11809 0x8ac7230489e80000, 0x4d28cb56c33fa539, 0x1eca170c00000000, 0x780c7372621bd74d,
11810 0x1e39a5057d810000, 0x5b27ac993df97701, 0x0000000000000000, 0x27b95e997e21d9f1,
11811 0x5da0e1e53c5c8000, 0xd2ae3299c1c4aedb, 0x16bcc41e90000000, 0x2d04b7fdd9c0ef49,
11812 0x5658597bcaa24000, 0xa0e2073737609371, 0x0c29e98000000000, 0x14adf4b7320334b9,
11813 0x226ed36478bfa000, 0x383d9170b85ff80b, 0x5a3c23e39c000000, 0x8e65137388122bcd,
11814 0xdd41bb36d259e000, 0x0aee5720ee830681, 0x1000000000000000, 0x172588ad4f5f0981,
11815 0x211e44f7d02c1000, 0x2ee56725f06e5c71, 0x41c21cb8e1000000,
11816#endif
11817};
11818
11819static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
11820{
11821 if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
11822 char buf[66];
11823 int len;
11824 len = i64toa_radix(buf, JS_VALUE_GET_SHORT_BIG_INT(val), radix);
11825 return js_new_string8_len(ctx, buf, len);
11826 } else {
11827 JSBigInt *r, *tmp = NULL;
11828 char *buf, *q, *buf_end;
11829 int is_neg, n_bits, log2_radix, n_digits;
11830 BOOL is_binary_radix;
11831 JSValue res;
11832
11833 assert(JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT);
11834 r = JS_VALUE_GET_PTR(val);
11835 if (r->len == 1 && r->tab[0] == 0) {
11836 /* '0' case */
11837 return js_new_string8_len(ctx, "0", 1);
11838 }
11839 is_binary_radix = ((radix & (radix - 1)) == 0);
11840 is_neg = js_bigint_sign(r);
11841 if (is_neg) {
11842 tmp = js_bigint_neg(ctx, r);
11843 if (!tmp)
11844 return JS_EXCEPTION;
11845 r = tmp;
11846 } else if (!is_binary_radix) {
11847 /* need to modify 'r' */
11848 tmp = js_bigint_new(ctx, r->len);
11849 if (!tmp)
11850 return JS_EXCEPTION;
11851 memcpy(tmp->tab, r->tab, r->len * sizeof(r->tab[0]));
11852 r = tmp;
11853 }
11854 log2_radix = 31 - clz32(radix); /* floor(log2(radix)) */
11855 n_bits = r->len * JS_LIMB_BITS - js_limb_clz(r->tab[r->len - 1]);
11856 /* n_digits is exact only if radix is a power of
11857 two. Otherwise it is >= the exact number of digits */
11858 n_digits = (n_bits + log2_radix - 1) / log2_radix;
11859 /* XXX: could directly build the JSString */
11860 buf = js_malloc(ctx, n_digits + is_neg + 1);
11861 if (!buf) {
11862 js_free(ctx, tmp);
11863 return JS_EXCEPTION;
11864 }
11865 q = buf + n_digits + is_neg + 1;
11866 *--q = '\0';
11867 buf_end = q;
11868 if (!is_binary_radix) {
11869 int len;
11870 js_limb_t radix_base, v;
11871 radix_base = radix_base_table[radix - 2];
11872 len = r->len;
11873 for(;;) {
11874 /* remove leading zero limbs */
11875 while (len > 1 && r->tab[len - 1] == 0)
11876 len--;
11877 if (len == 1 && r->tab[0] < radix_base) {
11878 v = r->tab[0];
11879 if (v != 0) {
11880 q = js_u64toa(q, v, radix);
11881 }
11882 break;
11883 } else {
11884 v = mp_div1(r->tab, r->tab, len, radix_base, 0);
11885 q = limb_to_a(q, v, radix, digits_per_limb_table[radix - 2]);
11886 }
11887 }
11888 } else {
11889 int i, shift;
11890 unsigned int bit_pos, pos, c;
11891
11892 /* radix is a power of two */
11893 for(i = 0; i < n_digits; i++) {
11894 bit_pos = i * log2_radix;
11895 pos = bit_pos / JS_LIMB_BITS;
11896 shift = bit_pos % JS_LIMB_BITS;
11897 if (likely((shift + log2_radix) <= JS_LIMB_BITS)) {
11898 c = r->tab[pos] >> shift;
11899 } else {
11900 c = (r->tab[pos] >> shift) |
11901 (r->tab[pos + 1] << (JS_LIMB_BITS - shift));
11902 }
11903 c &= (radix - 1);
11904 *--q = digits[c];
11905 }
11906 }
11907 if (is_neg)
11908 *--q = '-';
11909 js_free(ctx, tmp);
11910 res = js_new_string8_len(ctx, q, buf_end - q);
11911 js_free(ctx, buf);
11912 return res;
11913 }
11914}
11915
11916/* if possible transform a BigInt to short big and free it, otherwise
11917 return a normal bigint */
11918static JSValue JS_CompactBigInt(JSContext *ctx, JSBigInt *p)
11919{
11920 JSValue res;
11921 if (p->len == 1) {
11922 res = __JS_NewShortBigInt(ctx, (js_slimb_t)p->tab[0]);
11923 js_free(ctx, p);
11924 return res;
11925 } else {
11926 return JS_MKPTR(JS_TAG_BIG_INT, p);
11927 }
11928}
11929
11930#define ATOD_INT_ONLY (1 << 0)
11931/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
11932#define ATOD_ACCEPT_BIN_OCT (1 << 2)
11933/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
11934#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
11935/* accept _ between digits as a digit separator */
11936#define ATOD_ACCEPT_UNDERSCORES (1 << 5)
11937/* allow a suffix to override the type */
11938#define ATOD_ACCEPT_SUFFIX (1 << 6)
11939/* default type */
11940#define ATOD_TYPE_MASK (3 << 7)
11941#define ATOD_TYPE_FLOAT64 (0 << 7)
11942#define ATOD_TYPE_BIG_INT (1 << 7)
11943/* accept -0x1 */
11944#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
11945
11946/* return an exception in case of memory error. Return JS_NAN if
11947 invalid syntax */
11948/* XXX: directly use js_atod() */
11949static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
11950 int radix, int flags)
11951{
11952 const char *p, *p_start;
11953 int sep, is_neg;
11954 BOOL is_float, has_legacy_octal;
11955 int atod_type = flags & ATOD_TYPE_MASK;
11956 char buf1[64], *buf;
11957 int i, j, len;
11958 BOOL buf_allocated = FALSE;
11959 JSValue val;
11960 JSATODTempMem atod_mem;
11961
11962 /* optional separator between digits */
11963 sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
11964 has_legacy_octal = FALSE;
11965
11966 p = str;
11967 p_start = p;
11968 is_neg = 0;
11969 if (p[0] == '+') {
11970 p++;
11971 p_start++;
11972 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
11973 goto no_radix_prefix;
11974 } else if (p[0] == '-') {
11975 p++;
11976 p_start++;
11977 is_neg = 1;
11978 if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
11979 goto no_radix_prefix;
11980 }
11981 if (p[0] == '0') {
11982 if ((p[1] == 'x' || p[1] == 'X') &&
11983 (radix == 0 || radix == 16)) {
11984 p += 2;
11985 radix = 16;
11986 } else if ((p[1] == 'o' || p[1] == 'O') &&
11987 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
11988 p += 2;
11989 radix = 8;
11990 } else if ((p[1] == 'b' || p[1] == 'B') &&
11991 radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
11992 p += 2;
11993 radix = 2;
11994 } else if ((p[1] >= '0' && p[1] <= '9') &&
11995 radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
11996 int i;
11997 has_legacy_octal = TRUE;
11998 sep = 256;
11999 for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
12000 continue;
12001 if (p[i] == '8' || p[i] == '9')
12002 goto no_prefix;
12003 p += 1;
12004 radix = 8;
12005 } else {
12006 goto no_prefix;
12007 }
12008 /* there must be a digit after the prefix */
12009 if (to_digit((uint8_t)*p) >= radix)
12010 goto fail;
12011 no_prefix: ;
12012 } else {
12013 no_radix_prefix:
12014 if (!(flags & ATOD_INT_ONLY) &&
12015 (atod_type == ATOD_TYPE_FLOAT64) &&
12016 strstart(p, "Infinity", &p)) {
12017 double d = 1.0 / 0.0;
12018 if (is_neg)
12019 d = -d;
12020 val = JS_NewFloat64(ctx, d);
12021 goto done;
12022 }
12023 }
12024 if (radix == 0)
12025 radix = 10;
12026 is_float = FALSE;
12027 p_start = p;
12028 while (to_digit((uint8_t)*p) < radix
12029 || (*p == sep && (radix != 10 ||
12030 p != p_start + 1 || p[-1] != '0') &&
12031 to_digit((uint8_t)p[1]) < radix)) {
12032 p++;
12033 }
12034 if (!(flags & ATOD_INT_ONLY)) {
12035 if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
12036 is_float = TRUE;
12037 p++;
12038 if (*p == sep)
12039 goto fail;
12040 while (to_digit((uint8_t)*p) < radix ||
12041 (*p == sep && to_digit((uint8_t)p[1]) < radix))
12042 p++;
12043 }
12044 if (p > p_start &&
12045 (((*p == 'e' || *p == 'E') && radix == 10) ||
12046 ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
12047 const char *p1 = p + 1;
12048 is_float = TRUE;
12049 if (*p1 == '+') {
12050 p1++;
12051 } else if (*p1 == '-') {
12052 p1++;
12053 }
12054 if (is_digit((uint8_t)*p1)) {
12055 p = p1 + 1;
12056 while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
12057 p++;
12058 }
12059 }
12060 }
12061 if (p == p_start)
12062 goto fail;
12063
12064 buf = buf1;
12065 buf_allocated = FALSE;
12066 len = p - p_start;
12067 if (unlikely((len + 2) > sizeof(buf1))) {
12068 buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
12069 if (!buf)
12070 goto mem_error;
12071 buf_allocated = TRUE;
12072 }
12073 /* remove the separators and the radix prefixes */
12074 j = 0;
12075 if (is_neg)
12076 buf[j++] = '-';
12077 for (i = 0; i < len; i++) {
12078 if (p_start[i] != '_')
12079 buf[j++] = p_start[i];
12080 }
12081 buf[j] = '\0';
12082
12083 if (flags & ATOD_ACCEPT_SUFFIX) {
12084 if (*p == 'n') {
12085 p++;
12086 atod_type = ATOD_TYPE_BIG_INT;
12087 } else {
12088 if (is_float && radix != 10)
12089 goto fail;
12090 }
12091 } else {
12092 if (atod_type == ATOD_TYPE_FLOAT64) {
12093 if (is_float && radix != 10)
12094 goto fail;
12095 }
12096 }
12097
12098 switch(atod_type) {
12099 case ATOD_TYPE_FLOAT64:
12100 {
12101 double d;
12102 d = js_atod(buf,NULL, radix, is_float ? 0 : JS_ATOD_INT_ONLY,
12103 &atod_mem);
12104 /* return int or float64 */
12105 val = JS_NewFloat64(ctx, d);
12106 }
12107 break;
12108 case ATOD_TYPE_BIG_INT:
12109 {
12110 JSBigInt *r;
12111 if (has_legacy_octal || is_float)
12112 goto fail;
12113 r = js_bigint_from_string(ctx, buf, radix);
12114 if (!r)
12115 goto mem_error;
12116 val = JS_CompactBigInt(ctx, r);
12117 }
12118 break;
12119 default:
12120 abort();
12121 }
12122
12123done:
12124 if (buf_allocated)
12125 js_free_rt(ctx->rt, buf);
12126 if (pp)
12127 *pp = p;
12128 return val;
12129 fail:
12130 val = JS_NAN;
12131 goto done;
12132 mem_error:
12133 val = JS_ThrowOutOfMemory(ctx);
12134 goto done;
12135}
12136
12137typedef enum JSToNumberHintEnum {
12138 TON_FLAG_NUMBER,
12139 TON_FLAG_NUMERIC,
12140} JSToNumberHintEnum;
12141
12142static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
12143 JSToNumberHintEnum flag)
12144{
12145 uint32_t tag;
12146 JSValue ret;
12147
12148 redo:
12149 tag = JS_VALUE_GET_NORM_TAG(val);
12150 switch(tag) {
12151 case JS_TAG_BIG_INT:
12152 case JS_TAG_SHORT_BIG_INT:
12153 if (flag != TON_FLAG_NUMERIC) {
12154 JS_FreeValue(ctx, val);
12155 return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
12156 }
12157 ret = val;
12158 break;
12159 case JS_TAG_FLOAT64:
12160 case JS_TAG_INT:
12161 case JS_TAG_EXCEPTION:
12162 ret = val;
12163 break;
12164 case JS_TAG_BOOL:
12165 case JS_TAG_NULL:
12166 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
12167 break;
12168 case JS_TAG_UNDEFINED:
12169 ret = JS_NAN;
12170 break;
12171 case JS_TAG_OBJECT:
12172 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
12173 if (JS_IsException(val))
12174 return JS_EXCEPTION;
12175 goto redo;
12176 case JS_TAG_STRING:
12177 case JS_TAG_STRING_ROPE:
12178 {
12179 const char *str;
12180 const char *p;
12181 size_t len;
12182
12183 str = JS_ToCStringLen(ctx, &len, val);
12184 JS_FreeValue(ctx, val);
12185 if (!str)
12186 return JS_EXCEPTION;
12187 p = str;
12188 p += skip_spaces(p);
12189 if ((p - str) == len) {
12190 ret = JS_NewInt32(ctx, 0);
12191 } else {
12192 int flags = ATOD_ACCEPT_BIN_OCT;
12193 ret = js_atof(ctx, p, &p, 0, flags);
12194 if (!JS_IsException(ret)) {
12195 p += skip_spaces(p);
12196 if ((p - str) != len) {
12197 JS_FreeValue(ctx, ret);
12198 ret = JS_NAN;
12199 }
12200 }
12201 }
12202 JS_FreeCString(ctx, str);
12203 }
12204 break;
12205 case JS_TAG_SYMBOL:
12206 JS_FreeValue(ctx, val);
12207 return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
12208 default:
12209 JS_FreeValue(ctx, val);
12210 ret = JS_NAN;
12211 break;
12212 }
12213 return ret;
12214}
12215
12216static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
12217{
12218 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
12219}
12220
12221static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
12222{
12223 return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
12224}
12225
12226static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
12227{
12228 return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
12229}
12230
12231static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
12232 JSValue val)
12233{
12234 double d;
12235 uint32_t tag;
12236
12237 val = JS_ToNumberFree(ctx, val);
12238 if (JS_IsException(val))
12239 goto fail;
12240 tag = JS_VALUE_GET_NORM_TAG(val);
12241 switch(tag) {
12242 case JS_TAG_INT:
12243 d = JS_VALUE_GET_INT(val);
12244 break;
12245 case JS_TAG_FLOAT64:
12246 d = JS_VALUE_GET_FLOAT64(val);
12247 break;
12248 default:
12249 abort();
12250 }
12251 *pres = d;
12252 return 0;
12253 fail:
12254 *pres = JS_FLOAT64_NAN;
12255 return -1;
12256}
12257
12258static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
12259{
12260 uint32_t tag;
12261
12262 tag = JS_VALUE_GET_TAG(val);
12263 if (tag <= JS_TAG_NULL) {
12264 *pres = JS_VALUE_GET_INT(val);
12265 return 0;
12266 } else if (JS_TAG_IS_FLOAT64(tag)) {
12267 *pres = JS_VALUE_GET_FLOAT64(val);
12268 return 0;
12269 } else {
12270 return __JS_ToFloat64Free(ctx, pres, val);
12271 }
12272}
12273
12274int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
12275{
12276 return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
12277}
12278
12279static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
12280{
12281 return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
12282}
12283
12284/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
12285static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
12286{
12287 uint32_t tag;
12288 JSValue ret;
12289
12290 redo:
12291 tag = JS_VALUE_GET_NORM_TAG(val);
12292 switch(tag) {
12293 case JS_TAG_INT:
12294 case JS_TAG_BOOL:
12295 case JS_TAG_NULL:
12296 case JS_TAG_UNDEFINED:
12297 ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
12298 break;
12299 case JS_TAG_FLOAT64:
12300 {
12301 double d = JS_VALUE_GET_FLOAT64(val);
12302 if (isnan(d)) {
12303 ret = JS_NewInt32(ctx, 0);
12304 } else {
12305 /* convert -0 to +0 */
12306 d = trunc(d) + 0.0;
12307 ret = JS_NewFloat64(ctx, d);
12308 }
12309 }
12310 break;
12311 default:
12312 val = JS_ToNumberFree(ctx, val);
12313 if (JS_IsException(val))
12314 return val;
12315 goto redo;
12316 }
12317 return ret;
12318}
12319
12320/* Note: the integer value is satured to 32 bits */
12321static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
12322{
12323 uint32_t tag;
12324 int ret;
12325
12326 redo:
12327 tag = JS_VALUE_GET_NORM_TAG(val);
12328 switch(tag) {
12329 case JS_TAG_INT:
12330 case JS_TAG_BOOL:
12331 case JS_TAG_NULL:
12332 case JS_TAG_UNDEFINED:
12333 ret = JS_VALUE_GET_INT(val);
12334 break;
12335 case JS_TAG_EXCEPTION:
12336 *pres = 0;
12337 return -1;
12338 case JS_TAG_FLOAT64:
12339 {
12340 double d = JS_VALUE_GET_FLOAT64(val);
12341 if (isnan(d)) {
12342 ret = 0;
12343 } else {
12344 if (d < INT32_MIN)
12345 ret = INT32_MIN;
12346 else if (d > INT32_MAX)
12347 ret = INT32_MAX;
12348 else
12349 ret = (int)d;
12350 }
12351 }
12352 break;
12353 default:
12354 val = JS_ToNumberFree(ctx, val);
12355 if (JS_IsException(val)) {
12356 *pres = 0;
12357 return -1;
12358 }
12359 goto redo;
12360 }
12361 *pres = ret;
12362 return 0;
12363}
12364
12365int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
12366{
12367 return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
12368}
12369
12370int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
12371 int min, int max, int min_offset)
12372{
12373 int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
12374 if (res == 0) {
12375 if (*pres < min) {
12376 *pres += min_offset;
12377 if (*pres < min)
12378 *pres = min;
12379 } else {
12380 if (*pres > max)
12381 *pres = max;
12382 }
12383 }
12384 return res;
12385}
12386
12387static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
12388{
12389 uint32_t tag;
12390
12391 redo:
12392 tag = JS_VALUE_GET_NORM_TAG(val);
12393 switch(tag) {
12394 case JS_TAG_INT:
12395 case JS_TAG_BOOL:
12396 case JS_TAG_NULL:
12397 case JS_TAG_UNDEFINED:
12398 *pres = JS_VALUE_GET_INT(val);
12399 return 0;
12400 case JS_TAG_EXCEPTION:
12401 *pres = 0;
12402 return -1;
12403 case JS_TAG_FLOAT64:
12404 {
12405 double d = JS_VALUE_GET_FLOAT64(val);
12406 if (isnan(d)) {
12407 *pres = 0;
12408 } else {
12409 if (d < INT64_MIN)
12410 *pres = INT64_MIN;
12411 else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
12412 *pres = INT64_MAX;
12413 else
12414 *pres = (int64_t)d;
12415 }
12416 }
12417 return 0;
12418 default:
12419 val = JS_ToNumberFree(ctx, val);
12420 if (JS_IsException(val)) {
12421 *pres = 0;
12422 return -1;
12423 }
12424 goto redo;
12425 }
12426}
12427
12428int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
12429{
12430 return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
12431}
12432
12433int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
12434 int64_t min, int64_t max, int64_t neg_offset)
12435{
12436 int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
12437 if (res == 0) {
12438 if (*pres < 0)
12439 *pres += neg_offset;
12440 if (*pres < min)
12441 *pres = min;
12442 else if (*pres > max)
12443 *pres = max;
12444 }
12445 return res;
12446}
12447
12448/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
12449 in case of exception */
12450static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
12451{
12452 uint32_t tag;
12453 int64_t ret;
12454
12455 redo:
12456 tag = JS_VALUE_GET_NORM_TAG(val);
12457 switch(tag) {
12458 case JS_TAG_INT:
12459 case JS_TAG_BOOL:
12460 case JS_TAG_NULL:
12461 case JS_TAG_UNDEFINED:
12462 ret = JS_VALUE_GET_INT(val);
12463 break;
12464 case JS_TAG_FLOAT64:
12465 {
12466 JSFloat64Union u;
12467 double d;
12468 int e;
12469 d = JS_VALUE_GET_FLOAT64(val);
12470 u.d = d;
12471 /* we avoid doing fmod(x, 2^64) */
12472 e = (u.u64 >> 52) & 0x7ff;
12473 if (likely(e <= (1023 + 62))) {
12474 /* fast case */
12475 ret = (int64_t)d;
12476 } else if (e <= (1023 + 62 + 53)) {
12477 uint64_t v;
12478 /* remainder modulo 2^64 */
12479 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
12480 ret = v << ((e - 1023) - 52);
12481 /* take the sign into account */
12482 if (u.u64 >> 63)
12483 ret = -ret;
12484 } else {
12485 ret = 0; /* also handles NaN and +inf */
12486 }
12487 }
12488 break;
12489 default:
12490 val = JS_ToNumberFree(ctx, val);
12491 if (JS_IsException(val)) {
12492 *pres = 0;
12493 return -1;
12494 }
12495 goto redo;
12496 }
12497 *pres = ret;
12498 return 0;
12499}
12500
12501int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
12502{
12503 return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
12504}
12505
12506int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
12507{
12508 if (JS_IsBigInt(ctx, val))
12509 return JS_ToBigInt64(ctx, pres, val);
12510 else
12511 return JS_ToInt64(ctx, pres, val);
12512}
12513
12514/* return (<0, 0) in case of exception */
12515static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
12516{
12517 uint32_t tag;
12518 int32_t ret;
12519
12520 redo:
12521 tag = JS_VALUE_GET_NORM_TAG(val);
12522 switch(tag) {
12523 case JS_TAG_INT:
12524 case JS_TAG_BOOL:
12525 case JS_TAG_NULL:
12526 case JS_TAG_UNDEFINED:
12527 ret = JS_VALUE_GET_INT(val);
12528 break;
12529 case JS_TAG_FLOAT64:
12530 {
12531 JSFloat64Union u;
12532 double d;
12533 int e;
12534 d = JS_VALUE_GET_FLOAT64(val);
12535 u.d = d;
12536 /* we avoid doing fmod(x, 2^32) */
12537 e = (u.u64 >> 52) & 0x7ff;
12538 if (likely(e <= (1023 + 30))) {
12539 /* fast case */
12540 ret = (int32_t)d;
12541 } else if (e <= (1023 + 30 + 53)) {
12542 uint64_t v;
12543 /* remainder modulo 2^32 */
12544 v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
12545 v = v << ((e - 1023) - 52 + 32);
12546 ret = v >> 32;
12547 /* take the sign into account */
12548 if (u.u64 >> 63)
12549 ret = -ret;
12550 } else {
12551 ret = 0; /* also handles NaN and +inf */
12552 }
12553 }
12554 break;
12555 default:
12556 val = JS_ToNumberFree(ctx, val);
12557 if (JS_IsException(val)) {
12558 *pres = 0;
12559 return -1;
12560 }
12561 goto redo;
12562 }
12563 *pres = ret;
12564 return 0;
12565}
12566
12567int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
12568{
12569 return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
12570}
12571
12572static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
12573{
12574 return JS_ToInt32Free(ctx, (int32_t *)pres, val);
12575}
12576
12577static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
12578{
12579 uint32_t tag;
12580 int res;
12581
12582 redo:
12583 tag = JS_VALUE_GET_NORM_TAG(val);
12584 switch(tag) {
12585 case JS_TAG_INT:
12586 case JS_TAG_BOOL:
12587 case JS_TAG_NULL:
12588 case JS_TAG_UNDEFINED:
12589 res = JS_VALUE_GET_INT(val);
12590 res = max_int(0, min_int(255, res));
12591 break;
12592 case JS_TAG_FLOAT64:
12593 {
12594 double d = JS_VALUE_GET_FLOAT64(val);
12595 if (isnan(d)) {
12596 res = 0;
12597 } else {
12598 if (d < 0)
12599 res = 0;
12600 else if (d > 255)
12601 res = 255;
12602 else
12603 res = lrint(d);
12604 }
12605 }
12606 break;
12607 default:
12608 val = JS_ToNumberFree(ctx, val);
12609 if (JS_IsException(val)) {
12610 *pres = 0;
12611 return -1;
12612 }
12613 goto redo;
12614 }
12615 *pres = res;
12616 return 0;
12617}
12618
12619static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
12620 JSValue val, BOOL is_array_ctor)
12621{
12622 uint32_t tag, len;
12623
12624 tag = JS_VALUE_GET_TAG(val);
12625 switch(tag) {
12626 case JS_TAG_INT:
12627 case JS_TAG_BOOL:
12628 case JS_TAG_NULL:
12629 {
12630 int v;
12631 v = JS_VALUE_GET_INT(val);
12632 if (v < 0)
12633 goto fail;
12634 len = v;
12635 }
12636 break;
12637 default:
12638 if (JS_TAG_IS_FLOAT64(tag)) {
12639 double d;
12640 d = JS_VALUE_GET_FLOAT64(val);
12641 if (!(d >= 0 && d <= UINT32_MAX))
12642 goto fail;
12643 len = (uint32_t)d;
12644 if (len != d)
12645 goto fail;
12646 } else {
12647 uint32_t len1;
12648
12649 if (is_array_ctor) {
12650 val = JS_ToNumberFree(ctx, val);
12651 if (JS_IsException(val))
12652 return -1;
12653 /* cannot recurse because val is a number */
12654 if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
12655 return -1;
12656 } else {
12657 /* legacy behavior: must do the conversion twice and compare */
12658 if (JS_ToUint32(ctx, &len, val)) {
12659 JS_FreeValue(ctx, val);
12660 return -1;
12661 }
12662 val = JS_ToNumberFree(ctx, val);
12663 if (JS_IsException(val))
12664 return -1;
12665 /* cannot recurse because val is a number */
12666 if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
12667 return -1;
12668 if (len1 != len) {
12669 fail:
12670 JS_ThrowRangeError(ctx, "invalid array length");
12671 return -1;
12672 }
12673 }
12674 }
12675 break;
12676 }
12677 *plen = len;
12678 return 0;
12679}
12680
12681#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
12682
12683static BOOL is_safe_integer(double d)
12684{
12685 return isfinite(d) && floor(d) == d &&
12686 fabs(d) <= (double)MAX_SAFE_INTEGER;
12687}
12688
12689int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
12690{
12691 int64_t v;
12692 if (JS_ToInt64Sat(ctx, &v, val))
12693 return -1;
12694 if (v < 0 || v > MAX_SAFE_INTEGER) {
12695 JS_ThrowRangeError(ctx, "invalid array index");
12696 *plen = 0;
12697 return -1;
12698 }
12699 *plen = v;
12700 return 0;
12701}
12702
12703/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
12704 return -1 for exception */
12705static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
12706 JSValue val)
12707{
12708 int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
12709 JS_FreeValue(ctx, val);
12710 return res;
12711}
12712
12713/* Note: can return an exception */
12714static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
12715{
12716 double d;
12717 if (!JS_IsNumber(val))
12718 return FALSE;
12719 if (unlikely(JS_ToFloat64(ctx, &d, val)))
12720 return -1;
12721 return isfinite(d) && floor(d) == d;
12722}
12723
12724static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
12725{
12726 uint32_t tag;
12727
12728 tag = JS_VALUE_GET_NORM_TAG(val);
12729 switch(tag) {
12730 case JS_TAG_INT:
12731 {
12732 int v;
12733 v = JS_VALUE_GET_INT(val);
12734 return (v < 0);
12735 }
12736 case JS_TAG_FLOAT64:
12737 {
12738 JSFloat64Union u;
12739 u.d = JS_VALUE_GET_FLOAT64(val);
12740 return (u.u64 >> 63);
12741 }
12742 case JS_TAG_SHORT_BIG_INT:
12743 return (JS_VALUE_GET_SHORT_BIG_INT(val) < 0);
12744 case JS_TAG_BIG_INT:
12745 {
12746 JSBigInt *p = JS_VALUE_GET_PTR(val);
12747 return js_bigint_sign(p);
12748 }
12749 default:
12750 return FALSE;
12751 }
12752}
12753
12754static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
12755{
12756 return js_bigint_to_string1(ctx, val, 10);
12757}
12758
12759static JSValue js_dtoa2(JSContext *ctx,
12760 double d, int radix, int n_digits, int flags)
12761{
12762 char static_buf[128], *buf, *tmp_buf;
12763 int len, len_max;
12764 JSValue res;
12765 JSDTOATempMem dtoa_mem;
12766 len_max = js_dtoa_max_len(d, radix, n_digits, flags);
12767
12768 /* longer buffer may be used if radix != 10 */
12769 if (len_max > sizeof(static_buf) - 1) {
12770 tmp_buf = js_malloc(ctx, len_max + 1);
12771 if (!tmp_buf)
12772 return JS_EXCEPTION;
12773 buf = tmp_buf;
12774 } else {
12775 tmp_buf = NULL;
12776 buf = static_buf;
12777 }
12778 len = js_dtoa(buf, d, radix, n_digits, flags, &dtoa_mem);
12779 res = js_new_string8_len(ctx, buf, len);
12780 js_free(ctx, tmp_buf);
12781 return res;
12782}
12783
12784static JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
12785{
12786 uint32_t tag;
12787 char buf[32];
12788
12789 tag = JS_VALUE_GET_NORM_TAG(val);
12790 switch(tag) {
12791 case JS_TAG_STRING:
12792 return JS_DupValue(ctx, val);
12793 case JS_TAG_STRING_ROPE:
12794 return js_linearize_string_rope(ctx, JS_DupValue(ctx, val));
12795 case JS_TAG_INT:
12796 {
12797 size_t len;
12798 len = i32toa(buf, JS_VALUE_GET_INT(val));
12799 return js_new_string8_len(ctx, buf, len);
12800 }
12801 break;
12802 case JS_TAG_BOOL:
12803 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
12804 JS_ATOM_true : JS_ATOM_false);
12805 case JS_TAG_NULL:
12806 return JS_AtomToString(ctx, JS_ATOM_null);
12807 case JS_TAG_UNDEFINED:
12808 return JS_AtomToString(ctx, JS_ATOM_undefined);
12809 case JS_TAG_EXCEPTION:
12810 return JS_EXCEPTION;
12811 case JS_TAG_OBJECT:
12812 {
12813 JSValue val1, ret;
12814 val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
12815 if (JS_IsException(val1))
12816 return val1;
12817 ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
12818 JS_FreeValue(ctx, val1);
12819 return ret;
12820 }
12821 break;
12822 case JS_TAG_FUNCTION_BYTECODE:
12823 return js_new_string8(ctx, "[function bytecode]");
12824 case JS_TAG_SYMBOL:
12825 if (is_ToPropertyKey) {
12826 return JS_DupValue(ctx, val);
12827 } else {
12828 return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
12829 }
12830 case JS_TAG_FLOAT64:
12831 return js_dtoa2(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
12832 JS_DTOA_FORMAT_FREE);
12833 case JS_TAG_SHORT_BIG_INT:
12834 case JS_TAG_BIG_INT:
12835 return js_bigint_to_string(ctx, val);
12836 default:
12837 return js_new_string8(ctx, "[unsupported type]");
12838 }
12839}
12840
12841JSValue JS_ToString(JSContext *ctx, JSValueConst val)
12842{
12843 return JS_ToStringInternal(ctx, val, FALSE);
12844}
12845
12846static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
12847{
12848 JSValue ret;
12849 ret = JS_ToString(ctx, val);
12850 JS_FreeValue(ctx, val);
12851 return ret;
12852}
12853
12854static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
12855{
12856 if (JS_IsUndefined(val) || JS_IsNull(val))
12857 return JS_ToStringFree(ctx, val);
12858 return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
12859}
12860
12861JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
12862{
12863 return JS_ToStringInternal(ctx, val, TRUE);
12864}
12865
12866static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
12867{
12868 uint32_t tag = JS_VALUE_GET_TAG(val);
12869 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
12870 return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
12871 return JS_ToString(ctx, val);
12872}
12873
12874static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
12875{
12876 JSValue val;
12877 JSString *p;
12878 int i;
12879 uint32_t c;
12880 StringBuffer b_s, *b = &b_s;
12881 char buf[16];
12882
12883 val = JS_ToStringCheckObject(ctx, val1);
12884 if (JS_IsException(val))
12885 return val;
12886 p = JS_VALUE_GET_STRING(val);
12887
12888 if (string_buffer_init(ctx, b, p->len + 2))
12889 goto fail;
12890
12891 if (string_buffer_putc8(b, '\"'))
12892 goto fail;
12893 for(i = 0; i < p->len; ) {
12894 c = string_getc(p, &i);
12895 switch(c) {
12896 case '\t':
12897 c = 't';
12898 goto quote;
12899 case '\r':
12900 c = 'r';
12901 goto quote;
12902 case '\n':
12903 c = 'n';
12904 goto quote;
12905 case '\b':
12906 c = 'b';
12907 goto quote;
12908 case '\f':
12909 c = 'f';
12910 goto quote;
12911 case '\"':
12912 case '\\':
12913 quote:
12914 if (string_buffer_putc8(b, '\\'))
12915 goto fail;
12916 if (string_buffer_putc8(b, c))
12917 goto fail;
12918 break;
12919 default:
12920 if (c < 32 || is_surrogate(c)) {
12921 snprintf(buf, sizeof(buf), "\\u%04x", c);
12922 if (string_buffer_puts8(b, buf))
12923 goto fail;
12924 } else {
12925 if (string_buffer_putc(b, c))
12926 goto fail;
12927 }
12928 break;
12929 }
12930 }
12931 if (string_buffer_putc8(b, '\"'))
12932 goto fail;
12933 JS_FreeValue(ctx, val);
12934 return string_buffer_end(b);
12935 fail:
12936 JS_FreeValue(ctx, val);
12937 string_buffer_free(b);
12938 return JS_EXCEPTION;
12939}
12940
12941static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
12942{
12943 printf("%14s %4s %4s %14s %10s %s\n",
12944 "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
12945}
12946
12947/* for debug only: dump an object without side effect */
12948static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
12949{
12950 uint32_t i;
12951 char atom_buf[ATOM_GET_STR_BUF_SIZE];
12952 JSShape *sh;
12953 JSShapeProperty *prs;
12954 JSProperty *pr;
12955 BOOL is_first = TRUE;
12956
12957 /* XXX: should encode atoms with special characters */
12958 sh = p->shape; /* the shape can be NULL while freeing an object */
12959 printf("%14p %4d ",
12960 (void *)p,
12961 p->header.ref_count);
12962 if (sh) {
12963 printf("%3d%c %14p ",
12964 sh->header.ref_count,
12965 " *"[sh->is_hashed],
12966 (void *)sh->proto);
12967 } else {
12968 printf("%3s %14s ", "-", "-");
12969 }
12970 printf("%10s ",
12971 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
12972 if (p->is_exotic && p->fast_array) {
12973 printf("[ ");
12974 for(i = 0; i < p->u.array.count; i++) {
12975 if (i != 0)
12976 printf(", ");
12977 switch (p->class_id) {
12978 case JS_CLASS_ARRAY:
12979 case JS_CLASS_ARGUMENTS:
12980 JS_DumpValueShort(rt, p->u.array.u.values[i]);
12981 break;
12982 case JS_CLASS_UINT8C_ARRAY:
12983 case JS_CLASS_INT8_ARRAY:
12984 case JS_CLASS_UINT8_ARRAY:
12985 case JS_CLASS_INT16_ARRAY:
12986 case JS_CLASS_UINT16_ARRAY:
12987 case JS_CLASS_INT32_ARRAY:
12988 case JS_CLASS_UINT32_ARRAY:
12989 case JS_CLASS_BIG_INT64_ARRAY:
12990 case JS_CLASS_BIG_UINT64_ARRAY:
12991 case JS_CLASS_FLOAT32_ARRAY:
12992 case JS_CLASS_FLOAT64_ARRAY:
12993 {
12994 int size = 1 << typed_array_size_log2(p->class_id);
12995 const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
12996 while (size-- > 0)
12997 printf("%02X", *b++);
12998 }
12999 break;
13000 }
13001 }
13002 printf(" ] ");
13003 }
13004
13005 if (sh) {
13006 printf("{ ");
13007 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
13008 if (prs->atom != JS_ATOM_NULL) {
13009 pr = &p->prop[i];
13010 if (!is_first)
13011 printf(", ");
13012 printf("%s: ",
13013 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
13014 if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
13015 printf("[getset %p %p]", (void *)pr->u.getset.getter,
13016 (void *)pr->u.getset.setter);
13017 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
13018 printf("[varref %p]", (void *)pr->u.var_ref);
13019 } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
13020 printf("[autoinit %p %d %p]",
13021 (void *)js_autoinit_get_realm(pr),
13022 js_autoinit_get_id(pr),
13023 (void *)pr->u.init.opaque);
13024 } else {
13025 JS_DumpValueShort(rt, pr->u.value);
13026 }
13027 is_first = FALSE;
13028 }
13029 }
13030 printf(" }");
13031 }
13032
13033 if (js_class_has_bytecode(p->class_id)) {
13034 JSFunctionBytecode *b = p->u.func.function_bytecode;
13035 JSVarRef **var_refs;
13036 if (b->closure_var_count) {
13037 var_refs = p->u.func.var_refs;
13038 printf(" Closure:");
13039 for(i = 0; i < b->closure_var_count; i++) {
13040 printf(" ");
13041 JS_DumpValueShort(rt, var_refs[i]->value);
13042 }
13043 if (p->u.func.home_object) {
13044 printf(" HomeObject: ");
13045 JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
13046 }
13047 }
13048 }
13049 printf("\n");
13050}
13051
13052static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
13053{
13054 if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
13055 JS_DumpObject(rt, (JSObject *)p);
13056 } else {
13057 printf("%14p %4d ",
13058 (void *)p,
13059 p->ref_count);
13060 switch(p->gc_obj_type) {
13061 case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
13062 printf("[function bytecode]");
13063 break;
13064 case JS_GC_OBJ_TYPE_SHAPE:
13065 printf("[shape]");
13066 break;
13067 case JS_GC_OBJ_TYPE_VAR_REF:
13068 printf("[var_ref]");
13069 break;
13070 case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
13071 printf("[async_function]");
13072 break;
13073 case JS_GC_OBJ_TYPE_JS_CONTEXT:
13074 printf("[js_context]");
13075 break;
13076 default:
13077 printf("[unknown %d]", p->gc_obj_type);
13078 break;
13079 }
13080 printf("\n");
13081 }
13082}
13083
13084static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
13085 JSValueConst val)
13086{
13087 uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
13088 const char *str;
13089
13090 switch(tag) {
13091 case JS_TAG_INT:
13092 printf("%d", JS_VALUE_GET_INT(val));
13093 break;
13094 case JS_TAG_BOOL:
13095 if (JS_VALUE_GET_BOOL(val))
13096 str = "true";
13097 else
13098 str = "false";
13099 goto print_str;
13100 case JS_TAG_NULL:
13101 str = "null";
13102 goto print_str;
13103 case JS_TAG_EXCEPTION:
13104 str = "exception";
13105 goto print_str;
13106 case JS_TAG_UNINITIALIZED:
13107 str = "uninitialized";
13108 goto print_str;
13109 case JS_TAG_UNDEFINED:
13110 str = "undefined";
13111 print_str:
13112 printf("%s", str);
13113 break;
13114 case JS_TAG_FLOAT64:
13115 printf("%.14g", JS_VALUE_GET_FLOAT64(val));
13116 break;
13117 case JS_TAG_SHORT_BIG_INT:
13118 printf("%" PRId64 "n", (int64_t)JS_VALUE_GET_SHORT_BIG_INT(val));
13119 break;
13120 case JS_TAG_BIG_INT:
13121 {
13122 JSBigInt *p = JS_VALUE_GET_PTR(val);
13123 int sgn, i;
13124 /* In order to avoid allocations we just dump the limbs */
13125 sgn = js_bigint_sign(p);
13126 if (sgn)
13127 printf("BigInt.asIntN(%d,", p->len * JS_LIMB_BITS);
13128 printf("0x");
13129 for(i = p->len - 1; i >= 0; i--) {
13130 if (i != p->len - 1)
13131 printf("_");
13132#if JS_LIMB_BITS == 32
13133 printf("%08x", p->tab[i]);
13134#else
13135 printf("%016" PRIx64, p->tab[i]);
13136#endif
13137 }
13138 printf("n");
13139 if (sgn)
13140 printf(")");
13141 }
13142 break;
13143 case JS_TAG_STRING:
13144 {
13145 JSString *p;
13146 p = JS_VALUE_GET_STRING(val);
13147 JS_DumpString(rt, p);
13148 }
13149 break;
13150 case JS_TAG_STRING_ROPE:
13151 {
13152 JSStringRope *r = JS_VALUE_GET_STRING_ROPE(val);
13153 printf("[rope len=%d depth=%d]", r->len, r->depth);
13154 }
13155 break;
13156 case JS_TAG_FUNCTION_BYTECODE:
13157 {
13158 JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
13159 char buf[ATOM_GET_STR_BUF_SIZE];
13160 printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
13161 }
13162 break;
13163 case JS_TAG_OBJECT:
13164 {
13165 JSObject *p = JS_VALUE_GET_OBJ(val);
13166 JSAtom atom = rt->class_array[p->class_id].class_name;
13167 char atom_buf[ATOM_GET_STR_BUF_SIZE];
13168 printf("[%s %p]",
13169 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
13170 }
13171 break;
13172 case JS_TAG_SYMBOL:
13173 {
13174 JSAtomStruct *p = JS_VALUE_GET_PTR(val);
13175 char atom_buf[ATOM_GET_STR_BUF_SIZE];
13176 printf("Symbol(%s)",
13177 JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
13178 }
13179 break;
13180 case JS_TAG_MODULE:
13181 printf("[module]");
13182 break;
13183 default:
13184 printf("[unknown tag %d]", tag);
13185 break;
13186 }
13187}
13188
13189static __maybe_unused void JS_DumpValue(JSContext *ctx,
13190 JSValueConst val)
13191{
13192 JS_DumpValueShort(ctx->rt, val);
13193}
13194
13195static __maybe_unused void JS_PrintValue(JSContext *ctx,
13196 const char *str,
13197 JSValueConst val)
13198{
13199 printf("%s=", str);
13200 JS_DumpValueShort(ctx->rt, val);
13201 printf("\n");
13202}
13203
13204/* return -1 if exception (proxy case) or TRUE/FALSE */
13205// TODO: should take flags to make proxy resolution and exceptions optional
13206int JS_IsArray(JSContext *ctx, JSValueConst val)
13207{
13208 if (js_resolve_proxy(ctx, &val, TRUE))
13209 return -1;
13210 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
13211 JSObject *p = JS_VALUE_GET_OBJ(val);
13212 return p->class_id == JS_CLASS_ARRAY;
13213 } else {
13214 return FALSE;
13215 }
13216}
13217
13218static double js_pow(double a, double b)
13219{
13220 if (unlikely(!isfinite(b)) && fabs(a) == 1) {
13221 /* not compatible with IEEE 754 */
13222 return JS_FLOAT64_NAN;
13223 } else {
13224 return pow(a, b);
13225 }
13226}
13227
13228JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
13229{
13230#if JS_SHORT_BIG_INT_BITS == 64
13231 return __JS_NewShortBigInt(ctx, v);
13232#else
13233 if (v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX) {
13234 return __JS_NewShortBigInt(ctx, v);
13235 } else {
13236 JSBigInt *p;
13237 p = js_bigint_new_si64(ctx, v);
13238 if (!p)
13239 return JS_EXCEPTION;
13240 return JS_MKPTR(JS_TAG_BIG_INT, p);
13241 }
13242#endif
13243}
13244
13245JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
13246{
13247 if (v <= JS_SHORT_BIG_INT_MAX) {
13248 return __JS_NewShortBigInt(ctx, v);
13249 } else {
13250 JSBigInt *p;
13251 p = js_bigint_new_ui64(ctx, v);
13252 if (!p)
13253 return JS_EXCEPTION;
13254 return JS_MKPTR(JS_TAG_BIG_INT, p);
13255 }
13256}
13257
13258/* return NaN if bad bigint literal */
13259static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
13260{
13261 const char *str, *p;
13262 size_t len;
13263 int flags;
13264
13265 str = JS_ToCStringLen(ctx, &len, val);
13266 JS_FreeValue(ctx, val);
13267 if (!str)
13268 return JS_EXCEPTION;
13269 p = str;
13270 p += skip_spaces(p);
13271 if ((p - str) == len) {
13272 val = JS_NewBigInt64(ctx, 0);
13273 } else {
13274 flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
13275 val = js_atof(ctx, p, &p, 0, flags);
13276 p += skip_spaces(p);
13277 if (!JS_IsException(val)) {
13278 if ((p - str) != len) {
13279 JS_FreeValue(ctx, val);
13280 val = JS_NAN;
13281 }
13282 }
13283 }
13284 JS_FreeCString(ctx, str);
13285 return val;
13286}
13287
13288static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
13289{
13290 val = JS_StringToBigInt(ctx, val);
13291 if (JS_VALUE_IS_NAN(val))
13292 return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
13293 return val;
13294}
13295
13296/* JS Numbers are not allowed */
13297static JSValue JS_ToBigIntFree(JSContext *ctx, JSValue val)
13298{
13299 uint32_t tag;
13300
13301 redo:
13302 tag = JS_VALUE_GET_NORM_TAG(val);
13303 switch(tag) {
13304 case JS_TAG_SHORT_BIG_INT:
13305 case JS_TAG_BIG_INT:
13306 break;
13307 case JS_TAG_INT:
13308 case JS_TAG_NULL:
13309 case JS_TAG_UNDEFINED:
13310 case JS_TAG_FLOAT64:
13311 goto fail;
13312 case JS_TAG_BOOL:
13313 val = __JS_NewShortBigInt(ctx, JS_VALUE_GET_INT(val));
13314 break;
13315 case JS_TAG_STRING:
13316 case JS_TAG_STRING_ROPE:
13317 val = JS_StringToBigIntErr(ctx, val);
13318 if (JS_IsException(val))
13319 return val;
13320 goto redo;
13321 case JS_TAG_OBJECT:
13322 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
13323 if (JS_IsException(val))
13324 return val;
13325 goto redo;
13326 default:
13327 fail:
13328 JS_FreeValue(ctx, val);
13329 return JS_ThrowTypeError(ctx, "cannot convert to bigint");
13330 }
13331 return val;
13332}
13333
13334static JSValue JS_ToBigInt(JSContext *ctx, JSValueConst val)
13335{
13336 return JS_ToBigIntFree(ctx, JS_DupValue(ctx, val));
13337}
13338
13339/* XXX: merge with JS_ToInt64Free with a specific flag ? */
13340static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
13341{
13342 uint64_t res;
13343
13344 val = JS_ToBigIntFree(ctx, val);
13345 if (JS_IsException(val)) {
13346 *pres = 0;
13347 return -1;
13348 }
13349 if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
13350 res = JS_VALUE_GET_SHORT_BIG_INT(val);
13351 } else {
13352 JSBigInt *p = JS_VALUE_GET_PTR(val);
13353 /* return the value mod 2^64 */
13354 res = p->tab[0];
13355#if JS_LIMB_BITS == 32
13356 if (p->len >= 2)
13357 res |= (uint64_t)p->tab[1] << 32;
13358#endif
13359 JS_FreeValue(ctx, val);
13360 }
13361 *pres = res;
13362 return 0;
13363}
13364
13365int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
13366{
13367 return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
13368}
13369
13370static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
13371 JSValue *sp,
13372 OPCodeEnum op)
13373{
13374 JSValue op1;
13375 int v;
13376 uint32_t tag;
13377 JSBigIntBuf buf1;
13378 JSBigInt *p1;
13379
13380 op1 = sp[-1];
13381 /* fast path for float64 */
13382 if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
13383 goto handle_float64;
13384 op1 = JS_ToNumericFree(ctx, op1);
13385 if (JS_IsException(op1))
13386 goto exception;
13387 tag = JS_VALUE_GET_TAG(op1);
13388 switch(tag) {
13389 case JS_TAG_INT:
13390 {
13391 int64_t v64;
13392 v64 = JS_VALUE_GET_INT(op1);
13393 switch(op) {
13394 case OP_inc:
13395 case OP_dec:
13396 v = 2 * (op - OP_dec) - 1;
13397 v64 += v;
13398 break;
13399 case OP_plus:
13400 break;
13401 case OP_neg:
13402 if (v64 == 0) {
13403 sp[-1] = __JS_NewFloat64(ctx, -0.0);
13404 return 0;
13405 } else {
13406 v64 = -v64;
13407 }
13408 break;
13409 default:
13410 abort();
13411 }
13412 sp[-1] = JS_NewInt64(ctx, v64);
13413 }
13414 break;
13415 case JS_TAG_SHORT_BIG_INT:
13416 {
13417 int64_t v;
13418 v = JS_VALUE_GET_SHORT_BIG_INT(op1);
13419 switch(op) {
13420 case OP_plus:
13421 JS_ThrowTypeError(ctx, "bigint argument with unary +");
13422 goto exception;
13423 case OP_inc:
13424 if (v == JS_SHORT_BIG_INT_MAX)
13425 goto bigint_slow_case;
13426 sp[-1] = __JS_NewShortBigInt(ctx, v + 1);
13427 break;
13428 case OP_dec:
13429 if (v == JS_SHORT_BIG_INT_MIN)
13430 goto bigint_slow_case;
13431 sp[-1] = __JS_NewShortBigInt(ctx, v - 1);
13432 break;
13433 case OP_neg:
13434 v = JS_VALUE_GET_SHORT_BIG_INT(op1);
13435 if (v == JS_SHORT_BIG_INT_MIN) {
13436 bigint_slow_case:
13437 p1 = js_bigint_set_short(&buf1, op1);
13438 goto bigint_slow_case1;
13439 }
13440 sp[-1] = __JS_NewShortBigInt(ctx, -v);
13441 break;
13442 default:
13443 abort();
13444 }
13445 }
13446 break;
13447 case JS_TAG_BIG_INT:
13448 {
13449 JSBigInt *r;
13450 p1 = JS_VALUE_GET_PTR(op1);
13451 bigint_slow_case1:
13452 switch(op) {
13453 case OP_plus:
13454 JS_ThrowTypeError(ctx, "bigint argument with unary +");
13455 JS_FreeValue(ctx, op1);
13456 goto exception;
13457 case OP_inc:
13458 case OP_dec:
13459 {
13460 JSBigIntBuf buf2;
13461 JSBigInt *p2;
13462 p2 = js_bigint_set_si(&buf2, 2 * (op - OP_dec) - 1);
13463 r = js_bigint_add(ctx, p1, p2, 0);
13464 }
13465 break;
13466 case OP_neg:
13467 r = js_bigint_neg(ctx, p1);
13468 break;
13469 case OP_not:
13470 r = js_bigint_not(ctx, p1);
13471 break;
13472 default:
13473 abort();
13474 }
13475 JS_FreeValue(ctx, op1);
13476 if (!r)
13477 goto exception;
13478 sp[-1] = JS_CompactBigInt(ctx, r);
13479 }
13480 break;
13481 default:
13482 handle_float64:
13483 {
13484 double d;
13485 d = JS_VALUE_GET_FLOAT64(op1);
13486 switch(op) {
13487 case OP_inc:
13488 case OP_dec:
13489 v = 2 * (op - OP_dec) - 1;
13490 d += v;
13491 break;
13492 case OP_plus:
13493 break;
13494 case OP_neg:
13495 d = -d;
13496 break;
13497 default:
13498 abort();
13499 }
13500 sp[-1] = __JS_NewFloat64(ctx, d);
13501 }
13502 break;
13503 }
13504 return 0;
13505 exception:
13506 sp[-1] = JS_UNDEFINED;
13507 return -1;
13508}
13509
13510static __exception int js_post_inc_slow(JSContext *ctx,
13511 JSValue *sp, OPCodeEnum op)
13512{
13513 JSValue op1;
13514
13515 /* XXX: allow custom operators */
13516 op1 = sp[-1];
13517 op1 = JS_ToNumericFree(ctx, op1);
13518 if (JS_IsException(op1)) {
13519 sp[-1] = JS_UNDEFINED;
13520 return -1;
13521 }
13522 sp[-1] = op1;
13523 sp[0] = JS_DupValue(ctx, op1);
13524 return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
13525}
13526
13527static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
13528{
13529 JSValue op1;
13530
13531 op1 = sp[-1];
13532 op1 = JS_ToNumericFree(ctx, op1);
13533 if (JS_IsException(op1))
13534 goto exception;
13535 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT) {
13536 sp[-1] = __JS_NewShortBigInt(ctx, ~JS_VALUE_GET_SHORT_BIG_INT(op1));
13537 } else if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
13538 JSBigInt *r;
13539 r = js_bigint_not(ctx, JS_VALUE_GET_PTR(op1));
13540 JS_FreeValue(ctx, op1);
13541 if (!r)
13542 goto exception;
13543 sp[-1] = JS_CompactBigInt(ctx, r);
13544 } else {
13545 int32_t v1;
13546 if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
13547 goto exception;
13548 sp[-1] = JS_NewInt32(ctx, ~v1);
13549 }
13550 return 0;
13551 exception:
13552 sp[-1] = JS_UNDEFINED;
13553 return -1;
13554}
13555
13556static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
13557 OPCodeEnum op)
13558{
13559 JSValue op1, op2;
13560 uint32_t tag1, tag2;
13561 double d1, d2;
13562
13563 op1 = sp[-2];
13564 op2 = sp[-1];
13565 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13566 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13567 /* fast path for float operations */
13568 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13569 d1 = JS_VALUE_GET_FLOAT64(op1);
13570 d2 = JS_VALUE_GET_FLOAT64(op2);
13571 goto handle_float64;
13572 }
13573 /* fast path for short big int operations */
13574 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
13575 js_slimb_t v1, v2;
13576 js_sdlimb_t v;
13577 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
13578 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
13579 switch(op) {
13580 case OP_sub:
13581 v = (js_sdlimb_t)v1 - (js_sdlimb_t)v2;
13582 break;
13583 case OP_mul:
13584 v = (js_sdlimb_t)v1 * (js_sdlimb_t)v2;
13585 break;
13586 case OP_div:
13587 if (v2 == 0 ||
13588 ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
13589 v2 == -1)) {
13590 goto slow_big_int;
13591 }
13592 sp[-2] = __JS_NewShortBigInt(ctx, v1 / v2);
13593 return 0;
13594 case OP_mod:
13595 if (v2 == 0 ||
13596 ((js_limb_t)v1 == (js_limb_t)1 << (JS_LIMB_BITS - 1) &&
13597 v2 == -1)) {
13598 goto slow_big_int;
13599 }
13600 sp[-2] = __JS_NewShortBigInt(ctx, v1 % v2);
13601 return 0;
13602 case OP_pow:
13603 goto slow_big_int;
13604 default:
13605 abort();
13606 }
13607 if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
13608 sp[-2] = __JS_NewShortBigInt(ctx, v);
13609 } else {
13610 JSBigInt *r = js_bigint_new_di(ctx, v);
13611 if (!r)
13612 goto exception;
13613 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
13614 }
13615 return 0;
13616 }
13617 op1 = JS_ToNumericFree(ctx, op1);
13618 if (JS_IsException(op1)) {
13619 JS_FreeValue(ctx, op2);
13620 goto exception;
13621 }
13622 op2 = JS_ToNumericFree(ctx, op2);
13623 if (JS_IsException(op2)) {
13624 JS_FreeValue(ctx, op1);
13625 goto exception;
13626 }
13627 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13628 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13629
13630 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13631 int32_t v1, v2;
13632 int64_t v;
13633 v1 = JS_VALUE_GET_INT(op1);
13634 v2 = JS_VALUE_GET_INT(op2);
13635 switch(op) {
13636 case OP_sub:
13637 v = (int64_t)v1 - (int64_t)v2;
13638 break;
13639 case OP_mul:
13640 v = (int64_t)v1 * (int64_t)v2;
13641 if (v == 0 && (v1 | v2) < 0) {
13642 sp[-2] = __JS_NewFloat64(ctx, -0.0);
13643 return 0;
13644 }
13645 break;
13646 case OP_div:
13647 sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
13648 return 0;
13649 case OP_mod:
13650 if (v1 < 0 || v2 <= 0) {
13651 sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
13652 return 0;
13653 } else {
13654 v = (int64_t)v1 % (int64_t)v2;
13655 }
13656 break;
13657 case OP_pow:
13658 sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
13659 return 0;
13660 default:
13661 abort();
13662 }
13663 sp[-2] = JS_NewInt64(ctx, v);
13664 } else if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_BIG_INT) &&
13665 (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_BIG_INT)) {
13666 JSBigInt *p1, *p2, *r;
13667 JSBigIntBuf buf1, buf2;
13668 slow_big_int:
13669 /* bigint result */
13670 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
13671 p1 = js_bigint_set_short(&buf1, op1);
13672 else
13673 p1 = JS_VALUE_GET_PTR(op1);
13674 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
13675 p2 = js_bigint_set_short(&buf2, op2);
13676 else
13677 p2 = JS_VALUE_GET_PTR(op2);
13678 switch(op) {
13679 case OP_add:
13680 r = js_bigint_add(ctx, p1, p2, 0);
13681 break;
13682 case OP_sub:
13683 r = js_bigint_add(ctx, p1, p2, 1);
13684 break;
13685 case OP_mul:
13686 r = js_bigint_mul(ctx, p1, p2);
13687 break;
13688 case OP_div:
13689 r = js_bigint_divrem(ctx, p1, p2, FALSE);
13690 break;
13691 case OP_mod:
13692 r = js_bigint_divrem(ctx, p1, p2, TRUE);
13693 break;
13694 case OP_pow:
13695 r = js_bigint_pow(ctx, p1, p2);
13696 break;
13697 default:
13698 abort();
13699 }
13700 JS_FreeValue(ctx, op1);
13701 JS_FreeValue(ctx, op2);
13702 if (!r)
13703 goto exception;
13704 sp[-2] = JS_CompactBigInt(ctx, r);
13705 } else {
13706 double dr;
13707 /* float64 result */
13708 if (JS_ToFloat64Free(ctx, &d1, op1)) {
13709 JS_FreeValue(ctx, op2);
13710 goto exception;
13711 }
13712 if (JS_ToFloat64Free(ctx, &d2, op2))
13713 goto exception;
13714 handle_float64:
13715 switch(op) {
13716 case OP_sub:
13717 dr = d1 - d2;
13718 break;
13719 case OP_mul:
13720 dr = d1 * d2;
13721 break;
13722 case OP_div:
13723 dr = d1 / d2;
13724 break;
13725 case OP_mod:
13726 dr = fmod(d1, d2);
13727 break;
13728 case OP_pow:
13729 dr = js_pow(d1, d2);
13730 break;
13731 default:
13732 abort();
13733 }
13734 sp[-2] = __JS_NewFloat64(ctx, dr);
13735 }
13736 return 0;
13737 exception:
13738 sp[-2] = JS_UNDEFINED;
13739 sp[-1] = JS_UNDEFINED;
13740 return -1;
13741}
13742
13743static inline BOOL tag_is_string(uint32_t tag)
13744{
13745 return tag == JS_TAG_STRING || tag == JS_TAG_STRING_ROPE;
13746}
13747
13748static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
13749{
13750 JSValue op1, op2;
13751 uint32_t tag1, tag2;
13752
13753 op1 = sp[-2];
13754 op2 = sp[-1];
13755
13756 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13757 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13758 /* fast path for float64 */
13759 if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
13760 double d1, d2;
13761 d1 = JS_VALUE_GET_FLOAT64(op1);
13762 d2 = JS_VALUE_GET_FLOAT64(op2);
13763 sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13764 return 0;
13765 }
13766 /* fast path for short bigint */
13767 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
13768 js_slimb_t v1, v2;
13769 js_sdlimb_t v;
13770 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
13771 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
13772 v = (js_sdlimb_t)v1 + (js_sdlimb_t)v2;
13773 if (likely(v >= JS_SHORT_BIG_INT_MIN && v <= JS_SHORT_BIG_INT_MAX)) {
13774 sp[-2] = __JS_NewShortBigInt(ctx, v);
13775 } else {
13776 JSBigInt *r = js_bigint_new_di(ctx, v);
13777 if (!r)
13778 goto exception;
13779 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
13780 }
13781 return 0;
13782 }
13783
13784 if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
13785 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
13786 if (JS_IsException(op1)) {
13787 JS_FreeValue(ctx, op2);
13788 goto exception;
13789 }
13790
13791 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
13792 if (JS_IsException(op2)) {
13793 JS_FreeValue(ctx, op1);
13794 goto exception;
13795 }
13796 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13797 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13798 }
13799
13800 if (tag_is_string(tag1) || tag_is_string(tag2)) {
13801 sp[-2] = JS_ConcatString(ctx, op1, op2);
13802 if (JS_IsException(sp[-2]))
13803 goto exception;
13804 return 0;
13805 }
13806
13807 op1 = JS_ToNumericFree(ctx, op1);
13808 if (JS_IsException(op1)) {
13809 JS_FreeValue(ctx, op2);
13810 goto exception;
13811 }
13812 op2 = JS_ToNumericFree(ctx, op2);
13813 if (JS_IsException(op2)) {
13814 JS_FreeValue(ctx, op1);
13815 goto exception;
13816 }
13817 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13818 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13819
13820 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
13821 int32_t v1, v2;
13822 int64_t v;
13823 v1 = JS_VALUE_GET_INT(op1);
13824 v2 = JS_VALUE_GET_INT(op2);
13825 v = (int64_t)v1 + (int64_t)v2;
13826 sp[-2] = JS_NewInt64(ctx, v);
13827 } else if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
13828 (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
13829 JSBigInt *p1, *p2, *r;
13830 JSBigIntBuf buf1, buf2;
13831 /* bigint result */
13832 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
13833 p1 = js_bigint_set_short(&buf1, op1);
13834 else
13835 p1 = JS_VALUE_GET_PTR(op1);
13836 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
13837 p2 = js_bigint_set_short(&buf2, op2);
13838 else
13839 p2 = JS_VALUE_GET_PTR(op2);
13840 r = js_bigint_add(ctx, p1, p2, 0);
13841 JS_FreeValue(ctx, op1);
13842 JS_FreeValue(ctx, op2);
13843 if (!r)
13844 goto exception;
13845 sp[-2] = JS_CompactBigInt(ctx, r);
13846 } else {
13847 double d1, d2;
13848 /* float64 result */
13849 if (JS_ToFloat64Free(ctx, &d1, op1)) {
13850 JS_FreeValue(ctx, op2);
13851 goto exception;
13852 }
13853 if (JS_ToFloat64Free(ctx, &d2, op2))
13854 goto exception;
13855 sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
13856 }
13857 return 0;
13858 exception:
13859 sp[-2] = JS_UNDEFINED;
13860 sp[-1] = JS_UNDEFINED;
13861 return -1;
13862}
13863
13864static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
13865 JSValue *sp,
13866 OPCodeEnum op)
13867{
13868 JSValue op1, op2;
13869 uint32_t tag1, tag2;
13870 uint32_t v1, v2, r;
13871
13872 op1 = sp[-2];
13873 op2 = sp[-1];
13874 tag1 = JS_VALUE_GET_NORM_TAG(op1);
13875 tag2 = JS_VALUE_GET_NORM_TAG(op2);
13876
13877 if (tag1 == JS_TAG_SHORT_BIG_INT && tag2 == JS_TAG_SHORT_BIG_INT) {
13878 js_slimb_t v1, v2, v;
13879 js_sdlimb_t vd;
13880 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
13881 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
13882 /* bigint fast path */
13883 switch(op) {
13884 case OP_and:
13885 v = v1 & v2;
13886 break;
13887 case OP_or:
13888 v = v1 | v2;
13889 break;
13890 case OP_xor:
13891 v = v1 ^ v2;
13892 break;
13893 case OP_sar:
13894 if (v2 > (JS_LIMB_BITS - 1)) {
13895 goto slow_big_int;
13896 } else if (v2 < 0) {
13897 if (v2 < -(JS_LIMB_BITS - 1))
13898 goto slow_big_int;
13899 v2 = -v2;
13900 goto bigint_shl;
13901 }
13902 bigint_sar:
13903 v = v1 >> v2;
13904 break;
13905 case OP_shl:
13906 if (v2 > (JS_LIMB_BITS - 1)) {
13907 goto slow_big_int;
13908 } else if (v2 < 0) {
13909 if (v2 < -(JS_LIMB_BITS - 1))
13910 goto slow_big_int;
13911 v2 = -v2;
13912 goto bigint_sar;
13913 }
13914 bigint_shl:
13915 vd = (js_dlimb_t)v1 << v2;
13916 if (likely(vd >= JS_SHORT_BIG_INT_MIN &&
13917 vd <= JS_SHORT_BIG_INT_MAX)) {
13918 v = vd;
13919 } else {
13920 JSBigInt *r = js_bigint_new_di(ctx, vd);
13921 if (!r)
13922 goto exception;
13923 sp[-2] = JS_MKPTR(JS_TAG_BIG_INT, r);
13924 return 0;
13925 }
13926 break;
13927 default:
13928 abort();
13929 }
13930 sp[-2] = __JS_NewShortBigInt(ctx, v);
13931 return 0;
13932 }
13933 op1 = JS_ToNumericFree(ctx, op1);
13934 if (JS_IsException(op1)) {
13935 JS_FreeValue(ctx, op2);
13936 goto exception;
13937 }
13938 op2 = JS_ToNumericFree(ctx, op2);
13939 if (JS_IsException(op2)) {
13940 JS_FreeValue(ctx, op1);
13941 goto exception;
13942 }
13943
13944 tag1 = JS_VALUE_GET_TAG(op1);
13945 tag2 = JS_VALUE_GET_TAG(op2);
13946 if ((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
13947 (tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT)) {
13948 JSBigInt *p1, *p2, *r;
13949 JSBigIntBuf buf1, buf2;
13950 slow_big_int:
13951 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
13952 p1 = js_bigint_set_short(&buf1, op1);
13953 else
13954 p1 = JS_VALUE_GET_PTR(op1);
13955 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
13956 p2 = js_bigint_set_short(&buf2, op2);
13957 else
13958 p2 = JS_VALUE_GET_PTR(op2);
13959 switch(op) {
13960 case OP_and:
13961 case OP_or:
13962 case OP_xor:
13963 r = js_bigint_logic(ctx, p1, p2, op);
13964 break;
13965 case OP_shl:
13966 case OP_sar:
13967 {
13968 js_slimb_t shift;
13969 shift = js_bigint_get_si_sat(p2);
13970 if (shift > INT32_MAX)
13971 shift = INT32_MAX;
13972 else if (shift < -INT32_MAX)
13973 shift = -INT32_MAX;
13974 if (op == OP_sar)
13975 shift = -shift;
13976 if (shift >= 0)
13977 r = js_bigint_shl(ctx, p1, shift);
13978 else
13979 r = js_bigint_shr(ctx, p1, -shift);
13980 }
13981 break;
13982 default:
13983 abort();
13984 }
13985 JS_FreeValue(ctx, op1);
13986 JS_FreeValue(ctx, op2);
13987 if (!r)
13988 goto exception;
13989 sp[-2] = JS_CompactBigInt(ctx, r);
13990 } else {
13991 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
13992 JS_FreeValue(ctx, op2);
13993 goto exception;
13994 }
13995 if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
13996 goto exception;
13997 switch(op) {
13998 case OP_shl:
13999 r = v1 << (v2 & 0x1f);
14000 break;
14001 case OP_sar:
14002 r = (int)v1 >> (v2 & 0x1f);
14003 break;
14004 case OP_and:
14005 r = v1 & v2;
14006 break;
14007 case OP_or:
14008 r = v1 | v2;
14009 break;
14010 case OP_xor:
14011 r = v1 ^ v2;
14012 break;
14013 default:
14014 abort();
14015 }
14016 sp[-2] = JS_NewInt32(ctx, r);
14017 }
14018 return 0;
14019 exception:
14020 sp[-2] = JS_UNDEFINED;
14021 sp[-1] = JS_UNDEFINED;
14022 return -1;
14023}
14024
14025/* op1 must be a bigint or int. */
14026static JSBigInt *JS_ToBigIntBuf(JSContext *ctx, JSBigIntBuf *buf1,
14027 JSValue op1)
14028{
14029 JSBigInt *p1;
14030
14031 switch(JS_VALUE_GET_TAG(op1)) {
14032 case JS_TAG_INT:
14033 p1 = js_bigint_set_si(buf1, JS_VALUE_GET_INT(op1));
14034 break;
14035 case JS_TAG_SHORT_BIG_INT:
14036 p1 = js_bigint_set_short(buf1, op1);
14037 break;
14038 case JS_TAG_BIG_INT:
14039 p1 = JS_VALUE_GET_PTR(op1);
14040 break;
14041 default:
14042 abort();
14043 }
14044 return p1;
14045}
14046
14047/* op1 and op2 must be numeric types and at least one must be a
14048 bigint. No exception is generated. */
14049static int js_compare_bigint(JSContext *ctx, OPCodeEnum op,
14050 JSValue op1, JSValue op2)
14051{
14052 int res, val, tag1, tag2;
14053 JSBigIntBuf buf1, buf2;
14054 JSBigInt *p1, *p2;
14055
14056 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14057 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14058 if ((tag1 == JS_TAG_SHORT_BIG_INT || tag1 == JS_TAG_INT) &&
14059 (tag2 == JS_TAG_SHORT_BIG_INT || tag2 == JS_TAG_INT)) {
14060 /* fast path */
14061 js_slimb_t v1, v2;
14062 if (tag1 == JS_TAG_INT)
14063 v1 = JS_VALUE_GET_INT(op1);
14064 else
14065 v1 = JS_VALUE_GET_SHORT_BIG_INT(op1);
14066 if (tag2 == JS_TAG_INT)
14067 v2 = JS_VALUE_GET_INT(op2);
14068 else
14069 v2 = JS_VALUE_GET_SHORT_BIG_INT(op2);
14070 val = (v1 > v2) - (v1 < v2);
14071 } else {
14072 if (tag1 == JS_TAG_FLOAT64) {
14073 p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
14074 val = js_bigint_float64_cmp(ctx, p2, JS_VALUE_GET_FLOAT64(op1));
14075 if (val == 2)
14076 goto unordered;
14077 val = -val;
14078 } else if (tag2 == JS_TAG_FLOAT64) {
14079 p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
14080 val = js_bigint_float64_cmp(ctx, p1, JS_VALUE_GET_FLOAT64(op2));
14081 if (val == 2) {
14082 unordered:
14083 JS_FreeValue(ctx, op1);
14084 JS_FreeValue(ctx, op2);
14085 return FALSE;
14086 }
14087 } else {
14088 p1 = JS_ToBigIntBuf(ctx, &buf1, op1);
14089 p2 = JS_ToBigIntBuf(ctx, &buf2, op2);
14090 val = js_bigint_cmp(ctx, p1, p2);
14091 }
14092 JS_FreeValue(ctx, op1);
14093 JS_FreeValue(ctx, op2);
14094 }
14095
14096 switch(op) {
14097 case OP_lt:
14098 res = val < 0;
14099 break;
14100 case OP_lte:
14101 res = val <= 0;
14102 break;
14103 case OP_gt:
14104 res = val > 0;
14105 break;
14106 case OP_gte:
14107 res = val >= 0;
14108 break;
14109 case OP_eq:
14110 res = val == 0;
14111 break;
14112 default:
14113 abort();
14114 }
14115 return res;
14116}
14117
14118static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
14119 OPCodeEnum op)
14120{
14121 JSValue op1, op2;
14122 int res;
14123 uint32_t tag1, tag2;
14124
14125 op1 = sp[-2];
14126 op2 = sp[-1];
14127 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14128 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14129 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
14130 if (JS_IsException(op1)) {
14131 JS_FreeValue(ctx, op2);
14132 goto exception;
14133 }
14134 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
14135 if (JS_IsException(op2)) {
14136 JS_FreeValue(ctx, op1);
14137 goto exception;
14138 }
14139 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14140 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14141
14142 if (tag_is_string(tag1) && tag_is_string(tag2)) {
14143 if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
14144 res = js_string_compare(ctx, JS_VALUE_GET_STRING(op1),
14145 JS_VALUE_GET_STRING(op2));
14146 } else {
14147 res = js_string_rope_compare(ctx, op1, op2, FALSE);
14148 }
14149 switch(op) {
14150 case OP_lt:
14151 res = (res < 0);
14152 break;
14153 case OP_lte:
14154 res = (res <= 0);
14155 break;
14156 case OP_gt:
14157 res = (res > 0);
14158 break;
14159 default:
14160 case OP_gte:
14161 res = (res >= 0);
14162 break;
14163 }
14164 JS_FreeValue(ctx, op1);
14165 JS_FreeValue(ctx, op2);
14166 } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
14167 (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
14168 /* fast path for float64/int */
14169 goto float64_compare;
14170 } else {
14171 if ((((tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT) &&
14172 tag_is_string(tag2)) ||
14173 ((tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) &&
14174 tag_is_string(tag1)))) {
14175 if (tag_is_string(tag1)) {
14176 op1 = JS_StringToBigInt(ctx, op1);
14177 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
14178 JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
14179 goto invalid_bigint_string;
14180 }
14181 if (tag_is_string(tag2)) {
14182 op2 = JS_StringToBigInt(ctx, op2);
14183 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
14184 JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT) {
14185 invalid_bigint_string:
14186 JS_FreeValue(ctx, op1);
14187 JS_FreeValue(ctx, op2);
14188 res = FALSE;
14189 goto done;
14190 }
14191 }
14192 } else {
14193 op1 = JS_ToNumericFree(ctx, op1);
14194 if (JS_IsException(op1)) {
14195 JS_FreeValue(ctx, op2);
14196 goto exception;
14197 }
14198 op2 = JS_ToNumericFree(ctx, op2);
14199 if (JS_IsException(op2)) {
14200 JS_FreeValue(ctx, op1);
14201 goto exception;
14202 }
14203 }
14204
14205 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14206 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14207
14208 if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
14209 tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
14210 res = js_compare_bigint(ctx, op, op1, op2);
14211 } else {
14212 double d1, d2;
14213
14214 float64_compare:
14215 /* can use floating point comparison */
14216 if (tag1 == JS_TAG_FLOAT64) {
14217 d1 = JS_VALUE_GET_FLOAT64(op1);
14218 } else {
14219 d1 = JS_VALUE_GET_INT(op1);
14220 }
14221 if (tag2 == JS_TAG_FLOAT64) {
14222 d2 = JS_VALUE_GET_FLOAT64(op2);
14223 } else {
14224 d2 = JS_VALUE_GET_INT(op2);
14225 }
14226 switch(op) {
14227 case OP_lt:
14228 res = (d1 < d2); /* if NaN return false */
14229 break;
14230 case OP_lte:
14231 res = (d1 <= d2); /* if NaN return false */
14232 break;
14233 case OP_gt:
14234 res = (d1 > d2); /* if NaN return false */
14235 break;
14236 default:
14237 case OP_gte:
14238 res = (d1 >= d2); /* if NaN return false */
14239 break;
14240 }
14241 }
14242 }
14243 done:
14244 sp[-2] = JS_NewBool(ctx, res);
14245 return 0;
14246 exception:
14247 sp[-2] = JS_UNDEFINED;
14248 sp[-1] = JS_UNDEFINED;
14249 return -1;
14250}
14251
14252static BOOL tag_is_number(uint32_t tag)
14253{
14254 return (tag == JS_TAG_INT ||
14255 tag == JS_TAG_FLOAT64 ||
14256 tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT);
14257}
14258
14259static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
14260 BOOL is_neq)
14261{
14262 JSValue op1, op2;
14263 int res;
14264 uint32_t tag1, tag2;
14265
14266 op1 = sp[-2];
14267 op2 = sp[-1];
14268 redo:
14269 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14270 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14271 if (tag_is_number(tag1) && tag_is_number(tag2)) {
14272 if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
14273 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
14274 } else if ((tag1 == JS_TAG_FLOAT64 &&
14275 (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
14276 (tag2 == JS_TAG_FLOAT64 &&
14277 (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
14278 double d1, d2;
14279 if (tag1 == JS_TAG_FLOAT64) {
14280 d1 = JS_VALUE_GET_FLOAT64(op1);
14281 } else {
14282 d1 = JS_VALUE_GET_INT(op1);
14283 }
14284 if (tag2 == JS_TAG_FLOAT64) {
14285 d2 = JS_VALUE_GET_FLOAT64(op2);
14286 } else {
14287 d2 = JS_VALUE_GET_INT(op2);
14288 }
14289 res = (d1 == d2);
14290 } else {
14291 res = js_compare_bigint(ctx, OP_eq, op1, op2);
14292 }
14293 } else if (tag1 == tag2) {
14294 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14295 } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
14296 (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
14297 res = TRUE;
14298 } else if (tag_is_string(tag1) && tag_is_string(tag2)) {
14299 /* needed when comparing strings and ropes */
14300 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14301 } else if ((tag_is_string(tag1) && tag_is_number(tag2)) ||
14302 (tag_is_string(tag2) && tag_is_number(tag1))) {
14303
14304 if (tag1 == JS_TAG_BIG_INT || tag1 == JS_TAG_SHORT_BIG_INT ||
14305 tag2 == JS_TAG_BIG_INT || tag2 == JS_TAG_SHORT_BIG_INT) {
14306 if (tag_is_string(tag1)) {
14307 op1 = JS_StringToBigInt(ctx, op1);
14308 if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT &&
14309 JS_VALUE_GET_TAG(op1) != JS_TAG_SHORT_BIG_INT)
14310 goto invalid_bigint_string;
14311 }
14312 if (tag_is_string(tag2)) {
14313 op2 = JS_StringToBigInt(ctx, op2);
14314 if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT &&
14315 JS_VALUE_GET_TAG(op2) != JS_TAG_SHORT_BIG_INT ) {
14316 invalid_bigint_string:
14317 JS_FreeValue(ctx, op1);
14318 JS_FreeValue(ctx, op2);
14319 res = FALSE;
14320 goto done;
14321 }
14322 }
14323 } else {
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 }
14335 res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
14336 } else if (tag1 == JS_TAG_BOOL) {
14337 op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
14338 goto redo;
14339 } else if (tag2 == JS_TAG_BOOL) {
14340 op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
14341 goto redo;
14342 } else if ((tag1 == JS_TAG_OBJECT &&
14343 (tag_is_number(tag2) || tag_is_string(tag2) || tag2 == JS_TAG_SYMBOL)) ||
14344 (tag2 == JS_TAG_OBJECT &&
14345 (tag_is_number(tag1) || tag_is_string(tag1) || tag1 == JS_TAG_SYMBOL))) {
14346 op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
14347 if (JS_IsException(op1)) {
14348 JS_FreeValue(ctx, op2);
14349 goto exception;
14350 }
14351 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
14352 if (JS_IsException(op2)) {
14353 JS_FreeValue(ctx, op1);
14354 goto exception;
14355 }
14356 goto redo;
14357 } else {
14358 /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
14359 if ((JS_IsHTMLDDA(ctx, op1) &&
14360 (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
14361 (JS_IsHTMLDDA(ctx, op2) &&
14362 (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
14363 res = TRUE;
14364 } else {
14365 res = FALSE;
14366 }
14367 JS_FreeValue(ctx, op1);
14368 JS_FreeValue(ctx, op2);
14369 }
14370 done:
14371 sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14372 return 0;
14373 exception:
14374 sp[-2] = JS_UNDEFINED;
14375 sp[-1] = JS_UNDEFINED;
14376 return -1;
14377}
14378
14379static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
14380{
14381 JSValue op1, op2;
14382 uint32_t v1, v2, r;
14383
14384 op1 = sp[-2];
14385 op2 = sp[-1];
14386 op1 = JS_ToNumericFree(ctx, op1);
14387 if (JS_IsException(op1)) {
14388 JS_FreeValue(ctx, op2);
14389 goto exception;
14390 }
14391 op2 = JS_ToNumericFree(ctx, op2);
14392 if (JS_IsException(op2)) {
14393 JS_FreeValue(ctx, op1);
14394 goto exception;
14395 }
14396 if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
14397 JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT ||
14398 JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT ||
14399 JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT) {
14400 JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
14401 JS_FreeValue(ctx, op1);
14402 JS_FreeValue(ctx, op2);
14403 goto exception;
14404 }
14405 /* cannot give an exception */
14406 JS_ToUint32Free(ctx, &v1, op1);
14407 JS_ToUint32Free(ctx, &v2, op2);
14408 r = v1 >> (v2 & 0x1f);
14409 sp[-2] = JS_NewUint32(ctx, r);
14410 return 0;
14411 exception:
14412 sp[-2] = JS_UNDEFINED;
14413 sp[-1] = JS_UNDEFINED;
14414 return -1;
14415}
14416
14417/* XXX: Should take JSValueConst arguments */
14418static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
14419 JSStrictEqModeEnum eq_mode)
14420{
14421 BOOL res;
14422 int tag1, tag2;
14423 double d1, d2;
14424
14425 tag1 = JS_VALUE_GET_NORM_TAG(op1);
14426 tag2 = JS_VALUE_GET_NORM_TAG(op2);
14427 switch(tag1) {
14428 case JS_TAG_BOOL:
14429 if (tag1 != tag2) {
14430 res = FALSE;
14431 } else {
14432 res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
14433 goto done_no_free;
14434 }
14435 break;
14436 case JS_TAG_NULL:
14437 case JS_TAG_UNDEFINED:
14438 res = (tag1 == tag2);
14439 break;
14440 case JS_TAG_STRING:
14441 case JS_TAG_STRING_ROPE:
14442 {
14443 if (!tag_is_string(tag2)) {
14444 res = FALSE;
14445 } else if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
14446 res = (js_string_compare(ctx, JS_VALUE_GET_STRING(op1),
14447 JS_VALUE_GET_STRING(op2)) == 0);
14448 } else {
14449 res = (js_string_rope_compare(ctx, op1, op2, TRUE) == 0);
14450 }
14451 }
14452 break;
14453 case JS_TAG_SYMBOL:
14454 {
14455 JSAtomStruct *p1, *p2;
14456 if (tag1 != tag2) {
14457 res = FALSE;
14458 } else {
14459 p1 = JS_VALUE_GET_PTR(op1);
14460 p2 = JS_VALUE_GET_PTR(op2);
14461 res = (p1 == p2);
14462 }
14463 }
14464 break;
14465 case JS_TAG_OBJECT:
14466 if (tag1 != tag2)
14467 res = FALSE;
14468 else
14469 res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
14470 break;
14471 case JS_TAG_INT:
14472 d1 = JS_VALUE_GET_INT(op1);
14473 if (tag2 == JS_TAG_INT) {
14474 d2 = JS_VALUE_GET_INT(op2);
14475 goto number_test;
14476 } else if (tag2 == JS_TAG_FLOAT64) {
14477 d2 = JS_VALUE_GET_FLOAT64(op2);
14478 goto number_test;
14479 } else {
14480 res = FALSE;
14481 }
14482 break;
14483 case JS_TAG_FLOAT64:
14484 d1 = JS_VALUE_GET_FLOAT64(op1);
14485 if (tag2 == JS_TAG_FLOAT64) {
14486 d2 = JS_VALUE_GET_FLOAT64(op2);
14487 } else if (tag2 == JS_TAG_INT) {
14488 d2 = JS_VALUE_GET_INT(op2);
14489 } else {
14490 res = FALSE;
14491 break;
14492 }
14493 number_test:
14494 if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
14495 JSFloat64Union u1, u2;
14496 /* NaN is not always normalized, so this test is necessary */
14497 if (isnan(d1) || isnan(d2)) {
14498 res = isnan(d1) == isnan(d2);
14499 } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
14500 res = (d1 == d2); /* +0 == -0 */
14501 } else {
14502 u1.d = d1;
14503 u2.d = d2;
14504 res = (u1.u64 == u2.u64); /* +0 != -0 */
14505 }
14506 } else {
14507 res = (d1 == d2); /* if NaN return false and +0 == -0 */
14508 }
14509 goto done_no_free;
14510 case JS_TAG_SHORT_BIG_INT:
14511 case JS_TAG_BIG_INT:
14512 {
14513 JSBigIntBuf buf1, buf2;
14514 JSBigInt *p1, *p2;
14515
14516 if (tag2 != JS_TAG_SHORT_BIG_INT &&
14517 tag2 != JS_TAG_BIG_INT) {
14518 res = FALSE;
14519 break;
14520 }
14521
14522 if (JS_VALUE_GET_TAG(op1) == JS_TAG_SHORT_BIG_INT)
14523 p1 = js_bigint_set_short(&buf1, op1);
14524 else
14525 p1 = JS_VALUE_GET_PTR(op1);
14526 if (JS_VALUE_GET_TAG(op2) == JS_TAG_SHORT_BIG_INT)
14527 p2 = js_bigint_set_short(&buf2, op2);
14528 else
14529 p2 = JS_VALUE_GET_PTR(op2);
14530 res = (js_bigint_cmp(ctx, p1, p2) == 0);
14531 }
14532 break;
14533 default:
14534 res = FALSE;
14535 break;
14536 }
14537 JS_FreeValue(ctx, op1);
14538 JS_FreeValue(ctx, op2);
14539 done_no_free:
14540 return res;
14541}
14542
14543static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14544{
14545 return js_strict_eq2(ctx,
14546 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14547 JS_EQ_STRICT);
14548}
14549
14550BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14551{
14552 return js_strict_eq(ctx, op1, op2);
14553}
14554
14555static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14556{
14557 return js_strict_eq2(ctx,
14558 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14559 JS_EQ_SAME_VALUE);
14560}
14561
14562BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14563{
14564 return js_same_value(ctx, op1, op2);
14565}
14566
14567static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14568{
14569 return js_strict_eq2(ctx,
14570 JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
14571 JS_EQ_SAME_VALUE_ZERO);
14572}
14573
14574BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
14575{
14576 return js_same_value_zero(ctx, op1, op2);
14577}
14578
14579static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
14580 BOOL is_neq)
14581{
14582 BOOL res;
14583 res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT);
14584 sp[-2] = JS_NewBool(ctx, res ^ is_neq);
14585 return 0;
14586}
14587
14588static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
14589{
14590 JSValue op1, op2;
14591 JSAtom atom;
14592 int ret;
14593
14594 op1 = sp[-2];
14595 op2 = sp[-1];
14596
14597 if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
14598 JS_ThrowTypeError(ctx, "invalid 'in' operand");
14599 return -1;
14600 }
14601 atom = JS_ValueToAtom(ctx, op1);
14602 if (unlikely(atom == JS_ATOM_NULL))
14603 return -1;
14604 ret = JS_HasProperty(ctx, op2, atom);
14605 JS_FreeAtom(ctx, atom);
14606 if (ret < 0)
14607 return -1;
14608 JS_FreeValue(ctx, op1);
14609 JS_FreeValue(ctx, op2);
14610 sp[-2] = JS_NewBool(ctx, ret);
14611 return 0;
14612}
14613
14614static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp)
14615{
14616 JSValue op1, op2;
14617 int ret;
14618
14619 op1 = sp[-2]; /* object */
14620 op2 = sp[-1]; /* field name or method function */
14621
14622 if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
14623 JS_ThrowTypeError(ctx, "invalid 'in' operand");
14624 return -1;
14625 }
14626 if (JS_IsObject(op2)) {
14627 /* method: use the brand */
14628 ret = JS_CheckBrand(ctx, op1, op2);
14629 if (ret < 0)
14630 return -1;
14631 } else {
14632 JSAtom atom;
14633 JSObject *p;
14634 JSShapeProperty *prs;
14635 JSProperty *pr;
14636 /* field */
14637 atom = JS_ValueToAtom(ctx, op2);
14638 if (unlikely(atom == JS_ATOM_NULL))
14639 return -1;
14640 p = JS_VALUE_GET_OBJ(op1);
14641 prs = find_own_property(&pr, p, atom);
14642 JS_FreeAtom(ctx, atom);
14643 ret = (prs != NULL);
14644 }
14645 JS_FreeValue(ctx, op1);
14646 JS_FreeValue(ctx, op2);
14647 sp[-2] = JS_NewBool(ctx, ret);
14648 return 0;
14649}
14650
14651static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
14652 JSAtom atom)
14653{
14654 JSValue arr, val;
14655 int ret;
14656
14657 arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
14658 if (JS_IsException(arr))
14659 return -1;
14660 ret = 0;
14661 if (JS_IsObject(arr)) {
14662 val = JS_GetProperty(ctx, arr, atom);
14663 ret = JS_ToBoolFree(ctx, val);
14664 }
14665 JS_FreeValue(ctx, arr);
14666 return ret;
14667}
14668
14669static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
14670{
14671 JSValue op1, op2;
14672 BOOL ret;
14673
14674 op1 = sp[-2];
14675 op2 = sp[-1];
14676 ret = JS_IsInstanceOf(ctx, op1, op2);
14677 if (ret < 0)
14678 return ret;
14679 JS_FreeValue(ctx, op1);
14680 JS_FreeValue(ctx, op2);
14681 sp[-2] = JS_NewBool(ctx, ret);
14682 return 0;
14683}
14684
14685static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
14686{
14687 JSAtom atom;
14688 uint32_t tag;
14689
14690 tag = JS_VALUE_GET_NORM_TAG(op1);
14691 switch(tag) {
14692 case JS_TAG_SHORT_BIG_INT:
14693 case JS_TAG_BIG_INT:
14694 atom = JS_ATOM_bigint;
14695 break;
14696 case JS_TAG_INT:
14697 case JS_TAG_FLOAT64:
14698 atom = JS_ATOM_number;
14699 break;
14700 case JS_TAG_UNDEFINED:
14701 atom = JS_ATOM_undefined;
14702 break;
14703 case JS_TAG_BOOL:
14704 atom = JS_ATOM_boolean;
14705 break;
14706 case JS_TAG_STRING:
14707 case JS_TAG_STRING_ROPE:
14708 atom = JS_ATOM_string;
14709 break;
14710 case JS_TAG_OBJECT:
14711 {
14712 JSObject *p;
14713 p = JS_VALUE_GET_OBJ(op1);
14714 if (unlikely(p->is_HTMLDDA))
14715 atom = JS_ATOM_undefined;
14716 else if (JS_IsFunction(ctx, op1))
14717 atom = JS_ATOM_function;
14718 else
14719 goto obj_type;
14720 }
14721 break;
14722 case JS_TAG_NULL:
14723 obj_type:
14724 atom = JS_ATOM_object;
14725 break;
14726 case JS_TAG_SYMBOL:
14727 atom = JS_ATOM_symbol;
14728 break;
14729 default:
14730 atom = JS_ATOM_unknown;
14731 break;
14732 }
14733 return atom;
14734}
14735
14736static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
14737{
14738 JSValue op1, op2;
14739 JSAtom atom;
14740 int ret;
14741
14742 op1 = sp[-2];
14743 op2 = sp[-1];
14744 atom = JS_ValueToAtom(ctx, op2);
14745 if (unlikely(atom == JS_ATOM_NULL))
14746 return -1;
14747 ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
14748 JS_FreeAtom(ctx, atom);
14749 if (unlikely(ret < 0))
14750 return -1;
14751 JS_FreeValue(ctx, op1);
14752 JS_FreeValue(ctx, op2);
14753 sp[-2] = JS_NewBool(ctx, ret);
14754 return 0;
14755}
14756
14757/* XXX: not 100% compatible, but mozilla seems to use a similar
14758 implementation to ensure that caller in non strict mode does not
14759 throw (ES5 compatibility) */
14760static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
14761 int argc, JSValueConst *argv)
14762{
14763 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14764 if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype || argc >= 1) {
14765 return JS_ThrowTypeError(ctx, "invalid property access");
14766 }
14767 return JS_UNDEFINED;
14768}
14769
14770static JSValue js_function_proto_fileName(JSContext *ctx,
14771 JSValueConst this_val)
14772{
14773 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14774 if (b && b->has_debug) {
14775 return JS_AtomToString(ctx, b->debug.filename);
14776 }
14777 return JS_UNDEFINED;
14778}
14779
14780static JSValue js_function_proto_lineNumber(JSContext *ctx,
14781 JSValueConst this_val, int is_col)
14782{
14783 JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
14784 if (b && b->has_debug) {
14785 int line_num, col_num;
14786 line_num = find_line_num(ctx, b, -1, &col_num);
14787 if (is_col)
14788 return JS_NewInt32(ctx, col_num);
14789 else
14790 return JS_NewInt32(ctx, line_num);
14791 }
14792 return JS_UNDEFINED;
14793}
14794
14795static int js_arguments_define_own_property(JSContext *ctx,
14796 JSValueConst this_obj,
14797 JSAtom prop, JSValueConst val,
14798 JSValueConst getter, JSValueConst setter, int flags)
14799{
14800 JSObject *p;
14801 uint32_t idx;
14802 p = JS_VALUE_GET_OBJ(this_obj);
14803 /* convert to normal array when redefining an existing numeric field */
14804 if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
14805 idx < p->u.array.count) {
14806 if (convert_fast_array_to_array(ctx, p))
14807 return -1;
14808 }
14809 /* run the default define own property */
14810 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
14811 flags | JS_PROP_NO_EXOTIC);
14812}
14813
14814static const JSClassExoticMethods js_arguments_exotic_methods = {
14815 .define_own_property = js_arguments_define_own_property,
14816};
14817
14818static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
14819{
14820 JSValue val, *tab;
14821 JSProperty *pr;
14822 JSObject *p;
14823 int i;
14824
14825 val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
14826 JS_CLASS_ARGUMENTS);
14827 if (JS_IsException(val))
14828 return val;
14829 p = JS_VALUE_GET_OBJ(val);
14830
14831 /* add the length field (cannot fail) */
14832 pr = add_property(ctx, p, JS_ATOM_length,
14833 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
14834 if (unlikely(!pr))
14835 goto fail;
14836 pr->u.value = JS_NewInt32(ctx, argc);
14837
14838 /* initialize the fast array part */
14839 tab = NULL;
14840 if (argc > 0) {
14841 tab = js_malloc(ctx, sizeof(tab[0]) * argc);
14842 if (!tab)
14843 goto fail;
14844 for(i = 0; i < argc; i++) {
14845 tab[i] = JS_DupValue(ctx, argv[i]);
14846 }
14847 }
14848 p->u.array.u.values = tab;
14849 p->u.array.count = argc;
14850
14851 JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
14852 JS_DupValue(ctx, ctx->array_proto_values),
14853 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
14854 /* add callee property to throw a TypeError in strict mode */
14855 JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
14856 ctx->throw_type_error, ctx->throw_type_error,
14857 JS_PROP_HAS_GET | JS_PROP_HAS_SET);
14858 return val;
14859 fail:
14860 JS_FreeValue(ctx, val);
14861 return JS_EXCEPTION;
14862}
14863
14864#define GLOBAL_VAR_OFFSET 0x40000000
14865#define ARGUMENT_VAR_OFFSET 0x20000000
14866
14867/* legacy arguments object: add references to the function arguments */
14868static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
14869 JSValueConst *argv,
14870 JSStackFrame *sf, int arg_count)
14871{
14872 JSValue val;
14873 JSProperty *pr;
14874 JSObject *p;
14875 int i;
14876
14877 val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
14878 JS_CLASS_MAPPED_ARGUMENTS);
14879 if (JS_IsException(val))
14880 return val;
14881 p = JS_VALUE_GET_OBJ(val);
14882
14883 /* add the length field (cannot fail) */
14884 pr = add_property(ctx, p, JS_ATOM_length,
14885 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
14886 if (unlikely(!pr))
14887 goto fail;
14888 pr->u.value = JS_NewInt32(ctx, argc);
14889
14890 for(i = 0; i < arg_count; i++) {
14891 JSVarRef *var_ref;
14892 var_ref = get_var_ref(ctx, sf, i, TRUE);
14893 if (!var_ref)
14894 goto fail;
14895 pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
14896 if (!pr) {
14897 free_var_ref(ctx->rt, var_ref);
14898 goto fail;
14899 }
14900 pr->u.var_ref = var_ref;
14901 }
14902
14903 /* the arguments not mapped to the arguments of the function can
14904 be normal properties */
14905 for(i = arg_count; i < argc; i++) {
14906 if (JS_DefinePropertyValueUint32(ctx, val, i,
14907 JS_DupValue(ctx, argv[i]),
14908 JS_PROP_C_W_E) < 0)
14909 goto fail;
14910 }
14911
14912 JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
14913 JS_DupValue(ctx, ctx->array_proto_values),
14914 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
14915 /* callee returns this function in non strict mode */
14916 JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
14917 JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
14918 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
14919 return val;
14920 fail:
14921 JS_FreeValue(ctx, val);
14922 return JS_EXCEPTION;
14923}
14924
14925static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
14926{
14927 JSValue val;
14928 int i, ret;
14929
14930 val = JS_NewArray(ctx);
14931 if (JS_IsException(val))
14932 return val;
14933 for (i = first; i < argc; i++) {
14934 ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
14935 JS_DupValue(ctx, argv[i]),
14936 JS_PROP_C_W_E);
14937 if (ret < 0) {
14938 JS_FreeValue(ctx, val);
14939 return JS_EXCEPTION;
14940 }
14941 }
14942 return val;
14943}
14944
14945static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
14946{
14947 JSObject *p, *p1;
14948 JSPropertyEnum *tab_atom;
14949 int i;
14950 JSValue enum_obj;
14951 JSForInIterator *it;
14952 uint32_t tag, tab_atom_count;
14953
14954 tag = JS_VALUE_GET_TAG(obj);
14955 if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
14956 obj = JS_ToObjectFree(ctx, obj);
14957 }
14958
14959 it = js_malloc(ctx, sizeof(*it));
14960 if (!it) {
14961 JS_FreeValue(ctx, obj);
14962 return JS_EXCEPTION;
14963 }
14964 enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
14965 if (JS_IsException(enum_obj)) {
14966 js_free(ctx, it);
14967 JS_FreeValue(ctx, obj);
14968 return JS_EXCEPTION;
14969 }
14970 it->is_array = FALSE;
14971 it->obj = obj;
14972 it->idx = 0;
14973 it->tab_atom = NULL;
14974 it->atom_count = 0;
14975 it->in_prototype_chain = FALSE;
14976 p1 = JS_VALUE_GET_OBJ(enum_obj);
14977 p1->u.for_in_iterator = it;
14978
14979 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
14980 return enum_obj;
14981
14982 p = JS_VALUE_GET_OBJ(obj);
14983 if (p->fast_array) {
14984 JSShape *sh;
14985 JSShapeProperty *prs;
14986 /* check that there are no enumerable normal fields */
14987 sh = p->shape;
14988 for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
14989 if (prs->flags & JS_PROP_ENUMERABLE)
14990 goto normal_case;
14991 }
14992 /* for fast arrays, we only store the number of elements */
14993 it->is_array = TRUE;
14994 it->atom_count = p->u.array.count;
14995 } else {
14996 normal_case:
14997 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
14998 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
14999 JS_FreeValue(ctx, enum_obj);
15000 return JS_EXCEPTION;
15001 }
15002 it->tab_atom = tab_atom;
15003 it->atom_count = tab_atom_count;
15004 }
15005 return enum_obj;
15006}
15007
15008/* obj -> enum_obj */
15009static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
15010{
15011 sp[-1] = build_for_in_iterator(ctx, sp[-1]);
15012 if (JS_IsException(sp[-1]))
15013 return -1;
15014 return 0;
15015}
15016
15017/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */
15018static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx,
15019 JSValueConst enum_obj)
15020{
15021 JSObject *p;
15022 JSForInIterator *it;
15023 JSPropertyEnum *tab_atom;
15024 uint32_t tab_atom_count, i;
15025 JSValue obj1;
15026
15027 p = JS_VALUE_GET_OBJ(enum_obj);
15028 it = p->u.for_in_iterator;
15029
15030 /* check if there are enumerable properties in the prototype chain (fast path) */
15031 obj1 = JS_DupValue(ctx, it->obj);
15032 for(;;) {
15033 obj1 = JS_GetPrototypeFree(ctx, obj1);
15034 if (JS_IsNull(obj1))
15035 break;
15036 if (JS_IsException(obj1))
15037 goto fail;
15038 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
15039 JS_VALUE_GET_OBJ(obj1),
15040 JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
15041 JS_FreeValue(ctx, obj1);
15042 goto fail;
15043 }
15044 js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15045 if (tab_atom_count != 0) {
15046 JS_FreeValue(ctx, obj1);
15047 goto slow_path;
15048 }
15049 /* must check for timeout to avoid infinite loop */
15050 if (js_poll_interrupts(ctx)) {
15051 JS_FreeValue(ctx, obj1);
15052 goto fail;
15053 }
15054 }
15055 JS_FreeValue(ctx, obj1);
15056 return 1;
15057
15058 slow_path:
15059 /* add the visited properties, even if they are not enumerable */
15060 if (it->is_array) {
15061 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
15062 JS_VALUE_GET_OBJ(it->obj),
15063 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
15064 goto fail;
15065 }
15066 it->is_array = FALSE;
15067 it->tab_atom = tab_atom;
15068 it->atom_count = tab_atom_count;
15069 }
15070
15071 for(i = 0; i < it->atom_count; i++) {
15072 if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0)
15073 goto fail;
15074 }
15075 return 0;
15076 fail:
15077 return -1;
15078}
15079
15080/* enum_obj -> enum_obj value done */
15081static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
15082{
15083 JSValueConst enum_obj;
15084 JSObject *p;
15085 JSAtom prop;
15086 JSForInIterator *it;
15087 JSPropertyEnum *tab_atom;
15088 uint32_t tab_atom_count;
15089 int ret;
15090
15091 enum_obj = sp[-1];
15092 /* fail safe */
15093 if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
15094 goto done;
15095 p = JS_VALUE_GET_OBJ(enum_obj);
15096 if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
15097 goto done;
15098 it = p->u.for_in_iterator;
15099
15100 for(;;) {
15101 if (it->idx >= it->atom_count) {
15102 if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj))
15103 goto done; /* not an object */
15104 /* no more property in the current object: look in the prototype */
15105 if (!it->in_prototype_chain) {
15106 ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj);
15107 if (ret < 0)
15108 return -1;
15109 if (ret)
15110 goto done;
15111 it->in_prototype_chain = TRUE;
15112 }
15113 it->obj = JS_GetPrototypeFree(ctx, it->obj);
15114 if (JS_IsException(it->obj))
15115 return -1;
15116 if (JS_IsNull(it->obj))
15117 goto done; /* no more prototype */
15118
15119 /* must check for timeout to avoid infinite loop */
15120 if (js_poll_interrupts(ctx))
15121 return -1;
15122
15123 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
15124 JS_VALUE_GET_OBJ(it->obj),
15125 JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
15126 return -1;
15127 }
15128 js_free_prop_enum(ctx, it->tab_atom, it->atom_count);
15129 it->tab_atom = tab_atom;
15130 it->atom_count = tab_atom_count;
15131 it->idx = 0;
15132 } else {
15133 if (it->is_array) {
15134 prop = __JS_AtomFromUInt32(it->idx);
15135 it->idx++;
15136 } else {
15137 BOOL is_enumerable;
15138 prop = it->tab_atom[it->idx].atom;
15139 is_enumerable = it->tab_atom[it->idx].is_enumerable;
15140 it->idx++;
15141 if (it->in_prototype_chain) {
15142 /* slow case: we are in the prototype chain */
15143 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop);
15144 if (ret < 0)
15145 return ret;
15146 if (ret)
15147 continue; /* already visited */
15148 /* add to the visited property list */
15149 if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL,
15150 JS_PROP_ENUMERABLE) < 0)
15151 return -1;
15152 }
15153 if (!is_enumerable)
15154 continue;
15155 }
15156 /* check if the property was deleted */
15157 ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop);
15158 if (ret < 0)
15159 return ret;
15160 if (ret)
15161 break;
15162 }
15163 }
15164 /* return the property */
15165 sp[0] = JS_AtomToValue(ctx, prop);
15166 sp[1] = JS_FALSE;
15167 return 0;
15168 done:
15169 /* return the end */
15170 sp[0] = JS_UNDEFINED;
15171 sp[1] = JS_TRUE;
15172 return 0;
15173}
15174
15175static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
15176 JSValueConst method)
15177{
15178 JSValue enum_obj;
15179
15180 enum_obj = JS_Call(ctx, method, obj, 0, NULL);
15181 if (JS_IsException(enum_obj))
15182 return enum_obj;
15183 if (!JS_IsObject(enum_obj)) {
15184 JS_FreeValue(ctx, enum_obj);
15185 return JS_ThrowTypeErrorNotAnObject(ctx);
15186 }
15187 return enum_obj;
15188}
15189
15190static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
15191{
15192 JSValue method, ret, sync_iter;
15193
15194 if (is_async) {
15195 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
15196 if (JS_IsException(method))
15197 return method;
15198 if (JS_IsUndefined(method) || JS_IsNull(method)) {
15199 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15200 if (JS_IsException(method))
15201 return method;
15202 sync_iter = JS_GetIterator2(ctx, obj, method);
15203 JS_FreeValue(ctx, method);
15204 if (JS_IsException(sync_iter))
15205 return sync_iter;
15206 ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
15207 JS_FreeValue(ctx, sync_iter);
15208 return ret;
15209 }
15210 } else {
15211 method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
15212 if (JS_IsException(method))
15213 return method;
15214 }
15215 if (!JS_IsFunction(ctx, method)) {
15216 JS_FreeValue(ctx, method);
15217 return JS_ThrowTypeError(ctx, "value is not iterable");
15218 }
15219 ret = JS_GetIterator2(ctx, obj, method);
15220 JS_FreeValue(ctx, method);
15221 return ret;
15222}
15223
15224/* return *pdone = 2 if the iterator object is not parsed */
15225static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
15226 JSValueConst method,
15227 int argc, JSValueConst *argv, int *pdone)
15228{
15229 JSValue obj;
15230
15231 /* fast path for the built-in iterators (avoid creating the
15232 intermediate result object) */
15233 if (JS_IsObject(method)) {
15234 JSObject *p = JS_VALUE_GET_OBJ(method);
15235 if (p->class_id == JS_CLASS_C_FUNCTION &&
15236 p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
15237 JSCFunctionType func;
15238 JSValueConst args[1];
15239
15240 /* in case the function expects one argument */
15241 if (argc == 0) {
15242 args[0] = JS_UNDEFINED;
15243 argv = args;
15244 }
15245 func = p->u.cfunc.c_function;
15246 return func.iterator_next(ctx, enum_obj, argc, argv,
15247 pdone, p->u.cfunc.magic);
15248 }
15249 }
15250 obj = JS_Call(ctx, method, enum_obj, argc, argv);
15251 if (JS_IsException(obj))
15252 goto fail;
15253 if (!JS_IsObject(obj)) {
15254 JS_FreeValue(ctx, obj);
15255 JS_ThrowTypeError(ctx, "iterator must return an object");
15256 goto fail;
15257 }
15258 *pdone = 2;
15259 return obj;
15260 fail:
15261 *pdone = FALSE;
15262 return JS_EXCEPTION;
15263}
15264
15265/* Note: always return JS_UNDEFINED when *pdone = TRUE. */
15266static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
15267 JSValueConst method,
15268 int argc, JSValueConst *argv, BOOL *pdone)
15269{
15270 JSValue obj, value, done_val;
15271 int done;
15272
15273 obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
15274 if (JS_IsException(obj))
15275 goto fail;
15276 if (likely(done == 0)) {
15277 *pdone = FALSE;
15278 return obj;
15279 } else if (done != 2) {
15280 JS_FreeValue(ctx, obj);
15281 *pdone = TRUE;
15282 return JS_UNDEFINED;
15283 } else {
15284 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15285 if (JS_IsException(done_val))
15286 goto fail;
15287 *pdone = JS_ToBoolFree(ctx, done_val);
15288 value = JS_UNDEFINED;
15289 if (!*pdone) {
15290 value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15291 }
15292 JS_FreeValue(ctx, obj);
15293 return value;
15294 }
15295 fail:
15296 JS_FreeValue(ctx, obj);
15297 *pdone = FALSE;
15298 return JS_EXCEPTION;
15299}
15300
15301/* return < 0 in case of exception */
15302static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
15303 BOOL is_exception_pending)
15304{
15305 JSValue method, ret, ex_obj;
15306 int res;
15307
15308 if (is_exception_pending) {
15309 ex_obj = ctx->rt->current_exception;
15310 ctx->rt->current_exception = JS_UNINITIALIZED;
15311 res = -1;
15312 } else {
15313 ex_obj = JS_UNDEFINED;
15314 res = 0;
15315 }
15316 method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
15317 if (JS_IsException(method)) {
15318 res = -1;
15319 goto done;
15320 }
15321 if (JS_IsUndefined(method) || JS_IsNull(method)) {
15322 goto done;
15323 }
15324 ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
15325 if (!is_exception_pending) {
15326 if (JS_IsException(ret)) {
15327 res = -1;
15328 } else if (!JS_IsObject(ret)) {
15329 JS_ThrowTypeErrorNotAnObject(ctx);
15330 res = -1;
15331 }
15332 }
15333 JS_FreeValue(ctx, ret);
15334 done:
15335 if (is_exception_pending) {
15336 JS_Throw(ctx, ex_obj);
15337 }
15338 return res;
15339}
15340
15341/* obj -> enum_rec (3 slots) */
15342static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
15343 BOOL is_async)
15344{
15345 JSValue op1, obj, method;
15346 op1 = sp[-1];
15347 obj = JS_GetIterator(ctx, op1, is_async);
15348 if (JS_IsException(obj))
15349 return -1;
15350 JS_FreeValue(ctx, op1);
15351 sp[-1] = obj;
15352 method = JS_GetProperty(ctx, obj, JS_ATOM_next);
15353 if (JS_IsException(method))
15354 return -1;
15355 sp[0] = method;
15356 return 0;
15357}
15358
15359/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
15360 objs. If 'done' is true or in case of exception, 'enum_rec' is set
15361 to undefined. If 'done' is true, 'value' is always set to
15362 undefined. */
15363static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
15364{
15365 JSValue value = JS_UNDEFINED;
15366 int done = 1;
15367
15368 if (likely(!JS_IsUndefined(sp[offset]))) {
15369 value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
15370 if (JS_IsException(value))
15371 done = -1;
15372 if (done) {
15373 /* value is JS_UNDEFINED or JS_EXCEPTION */
15374 /* replace the iteration object with undefined */
15375 JS_FreeValue(ctx, sp[offset]);
15376 sp[offset] = JS_UNDEFINED;
15377 if (done < 0) {
15378 return -1;
15379 } else {
15380 JS_FreeValue(ctx, value);
15381 value = JS_UNDEFINED;
15382 }
15383 }
15384 }
15385 sp[0] = value;
15386 sp[1] = JS_NewBool(ctx, done);
15387 return 0;
15388}
15389
15390static __exception int js_for_await_of_next(JSContext *ctx, JSValue *sp)
15391{
15392 JSValue obj, iter, next;
15393
15394 sp[-1] = JS_UNDEFINED; /* disable the catch offset so that
15395 exceptions do not close the iterator */
15396 iter = sp[-3];
15397 next = sp[-2];
15398 obj = JS_Call(ctx, next, iter, 0, NULL);
15399 if (JS_IsException(obj))
15400 return -1;
15401 sp[0] = obj;
15402 return 0;
15403}
15404
15405static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
15406 BOOL *pdone)
15407{
15408 JSValue done_val, value;
15409 BOOL done;
15410 done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
15411 if (JS_IsException(done_val))
15412 goto fail;
15413 done = JS_ToBoolFree(ctx, done_val);
15414 value = JS_GetProperty(ctx, obj, JS_ATOM_value);
15415 if (JS_IsException(value))
15416 goto fail;
15417 *pdone = done;
15418 return value;
15419 fail:
15420 *pdone = FALSE;
15421 return JS_EXCEPTION;
15422}
15423
15424static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
15425{
15426 JSValue obj, value;
15427 BOOL done;
15428 obj = sp[-1];
15429 if (!JS_IsObject(obj)) {
15430 JS_ThrowTypeError(ctx, "iterator must return an object");
15431 return -1;
15432 }
15433 value = JS_IteratorGetCompleteValue(ctx, obj, &done);
15434 if (JS_IsException(value))
15435 return -1;
15436 JS_FreeValue(ctx, obj);
15437 /* put again the catch offset so that exceptions close the
15438 iterator */
15439 sp[-2] = JS_NewCatchOffset(ctx, 0);
15440 sp[-1] = value;
15441 sp[0] = JS_NewBool(ctx, done);
15442 return 0;
15443}
15444
15445static JSValue js_create_iterator_result(JSContext *ctx,
15446 JSValue val,
15447 BOOL done)
15448{
15449 JSValue obj;
15450 obj = JS_NewObject(ctx);
15451 if (JS_IsException(obj)) {
15452 JS_FreeValue(ctx, val);
15453 return obj;
15454 }
15455 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
15456 val, JS_PROP_C_W_E) < 0) {
15457 goto fail;
15458 }
15459 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
15460 JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
15461 fail:
15462 JS_FreeValue(ctx, obj);
15463 return JS_EXCEPTION;
15464 }
15465 return obj;
15466}
15467
15468static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
15469 int argc, JSValueConst *argv,
15470 BOOL *pdone, int magic);
15471
15472static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
15473 int argc, JSValueConst *argv, int magic);
15474
15475static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
15476{
15477 /* Try and handle fast arrays explicitly */
15478 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15479 JSObject *p = JS_VALUE_GET_OBJ(obj);
15480 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15481 return TRUE;
15482 }
15483 }
15484 return FALSE;
15485}
15486
15487/* Access an Array's internal JSValue array if available */
15488static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
15489 JSValue **arrpp, uint32_t *countp)
15490{
15491 /* Try and handle fast arrays explicitly */
15492 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
15493 JSObject *p = JS_VALUE_GET_OBJ(obj);
15494 if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
15495 *countp = p->u.array.count;
15496 *arrpp = p->u.array.u.values;
15497 return TRUE;
15498 }
15499 }
15500 return FALSE;
15501}
15502
15503static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
15504{
15505 JSValue iterator, enumobj, method, value;
15506 int is_array_iterator;
15507 JSValue *arrp;
15508 uint32_t i, count32, pos;
15509
15510 if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
15511 JS_ThrowInternalError(ctx, "invalid index for append");
15512 return -1;
15513 }
15514
15515 pos = JS_VALUE_GET_INT(sp[-2]);
15516
15517 /* XXX: further optimisations:
15518 - use ctx->array_proto_values?
15519 - check if array_iterator_prototype next method is built-in and
15520 avoid constructing actual iterator object?
15521 - build this into js_for_of_start and use in all `for (x of o)` loops
15522 */
15523 iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
15524 if (JS_IsException(iterator))
15525 return -1;
15526 is_array_iterator = JS_IsCFunction(ctx, iterator,
15527 (JSCFunction *)js_create_array_iterator,
15528 JS_ITERATOR_KIND_VALUE);
15529 JS_FreeValue(ctx, iterator);
15530
15531 enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
15532 if (JS_IsException(enumobj))
15533 return -1;
15534 method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
15535 if (JS_IsException(method)) {
15536 JS_FreeValue(ctx, enumobj);
15537 return -1;
15538 }
15539 if (is_array_iterator
15540 && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
15541 && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
15542 uint32_t len;
15543 if (js_get_length32(ctx, &len, sp[-1]))
15544 goto exception;
15545 /* if len > count32, the elements >= count32 might be read in
15546 the prototypes and might have side effects */
15547 if (len != count32)
15548 goto general_case;
15549 /* Handle fast arrays explicitly */
15550 for (i = 0; i < count32; i++) {
15551 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
15552 JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
15553 goto exception;
15554 }
15555 } else {
15556 general_case:
15557 for (;;) {
15558 BOOL done;
15559 value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
15560 if (JS_IsException(value))
15561 goto exception;
15562 if (done) {
15563 /* value is JS_UNDEFINED */
15564 break;
15565 }
15566 if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
15567 goto exception;
15568 }
15569 }
15570 /* Note: could raise an error if too many elements */
15571 sp[-2] = JS_NewInt32(ctx, pos);
15572 JS_FreeValue(ctx, enumobj);
15573 JS_FreeValue(ctx, method);
15574 return 0;
15575
15576exception:
15577 JS_IteratorClose(ctx, enumobj, TRUE);
15578 JS_FreeValue(ctx, enumobj);
15579 JS_FreeValue(ctx, method);
15580 return -1;
15581}
15582
15583static __exception int JS_CopyDataProperties(JSContext *ctx,
15584 JSValueConst target,
15585 JSValueConst source,
15586 JSValueConst excluded,
15587 BOOL setprop)
15588{
15589 JSPropertyEnum *tab_atom;
15590 JSValue val;
15591 uint32_t i, tab_atom_count;
15592 JSObject *p;
15593 JSObject *pexcl = NULL;
15594 int ret, gpn_flags;
15595 JSPropertyDescriptor desc;
15596 BOOL is_enumerable;
15597
15598 if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
15599 return 0;
15600
15601 if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
15602 pexcl = JS_VALUE_GET_OBJ(excluded);
15603
15604 p = JS_VALUE_GET_OBJ(source);
15605
15606 gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
15607 if (p->is_exotic) {
15608 const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
15609 /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
15610 introduces a visible change */
15611 if (em && em->get_own_property_names) {
15612 gpn_flags &= ~JS_GPN_ENUM_ONLY;
15613 }
15614 }
15615 if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
15616 gpn_flags))
15617 return -1;
15618
15619 for (i = 0; i < tab_atom_count; i++) {
15620 if (pexcl) {
15621 ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
15622 if (ret) {
15623 if (ret < 0)
15624 goto exception;
15625 continue;
15626 }
15627 }
15628 if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
15629 /* test if the property is enumerable */
15630 ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
15631 if (ret < 0)
15632 goto exception;
15633 if (!ret)
15634 continue;
15635 is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
15636 js_free_desc(ctx, &desc);
15637 if (!is_enumerable)
15638 continue;
15639 }
15640 val = JS_GetProperty(ctx, source, tab_atom[i].atom);
15641 if (JS_IsException(val))
15642 goto exception;
15643 if (setprop)
15644 ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
15645 else
15646 ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
15647 JS_PROP_C_W_E);
15648 if (ret < 0)
15649 goto exception;
15650 }
15651 js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15652 return 0;
15653 exception:
15654 js_free_prop_enum(ctx, tab_atom, tab_atom_count);
15655 return -1;
15656}
15657
15658/* only valid inside C functions */
15659static JSValueConst JS_GetActiveFunction(JSContext *ctx)
15660{
15661 return ctx->rt->current_stack_frame->cur_func;
15662}
15663
15664static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
15665 int var_idx, BOOL is_arg)
15666{
15667 JSVarRef *var_ref;
15668 struct list_head *el;
15669 JSValue *pvalue;
15670
15671 if (is_arg)
15672 pvalue = &sf->arg_buf[var_idx];
15673 else
15674 pvalue = &sf->var_buf[var_idx];
15675
15676 list_for_each(el, &sf->var_ref_list) {
15677 var_ref = list_entry(el, JSVarRef, var_ref_link);
15678 if (var_ref->pvalue == pvalue) {
15679 var_ref->header.ref_count++;
15680 return var_ref;
15681 }
15682 }
15683 /* create a new one */
15684 var_ref = js_malloc(ctx, sizeof(JSVarRef));
15685 if (!var_ref)
15686 return NULL;
15687 var_ref->header.ref_count = 1;
15688 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
15689 var_ref->is_detached = FALSE;
15690 list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list);
15691 if (sf->js_mode & JS_MODE_ASYNC) {
15692 /* The stack frame is detached and may be destroyed at any
15693 time so its reference count must be increased. Calling
15694 close_var_refs() when destroying the stack frame is not
15695 possible because it would change the graph between the GC
15696 objects. Another solution could be to temporarily detach
15697 the JSVarRef of async functions during the GC. It would
15698 have the advantage of allowing the release of unused stack
15699 frames in a cycle. */
15700 var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame);
15701 var_ref->async_func->header.ref_count++;
15702 } else {
15703 var_ref->async_func = NULL;
15704 }
15705 var_ref->pvalue = pvalue;
15706 return var_ref;
15707}
15708
15709static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
15710 JSFunctionBytecode *b,
15711 JSVarRef **cur_var_refs,
15712 JSStackFrame *sf)
15713{
15714 JSObject *p;
15715 JSVarRef **var_refs;
15716 int i;
15717
15718 p = JS_VALUE_GET_OBJ(func_obj);
15719 p->u.func.function_bytecode = b;
15720 p->u.func.home_object = NULL;
15721 p->u.func.var_refs = NULL;
15722 if (b->closure_var_count) {
15723 var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
15724 if (!var_refs)
15725 goto fail;
15726 p->u.func.var_refs = var_refs;
15727 for(i = 0; i < b->closure_var_count; i++) {
15728 JSClosureVar *cv = &b->closure_var[i];
15729 JSVarRef *var_ref;
15730 if (cv->is_local) {
15731 /* reuse the existing variable reference if it already exists */
15732 var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
15733 if (!var_ref)
15734 goto fail;
15735 } else {
15736 var_ref = cur_var_refs[cv->var_idx];
15737 var_ref->header.ref_count++;
15738 }
15739 var_refs[i] = var_ref;
15740 }
15741 }
15742 return func_obj;
15743 fail:
15744 /* bfunc is freed when func_obj is freed */
15745 JS_FreeValue(ctx, func_obj);
15746 return JS_EXCEPTION;
15747}
15748
15749static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
15750{
15751 JSValue obj, this_val;
15752 int ret;
15753
15754 this_val = JS_MKPTR(JS_TAG_OBJECT, p);
15755 obj = JS_NewObject(ctx);
15756 if (JS_IsException(obj))
15757 return JS_EXCEPTION;
15758 set_cycle_flag(ctx, obj);
15759 set_cycle_flag(ctx, this_val);
15760 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
15761 JS_DupValue(ctx, this_val),
15762 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
15763 if (ret < 0) {
15764 JS_FreeValue(ctx, obj);
15765 return JS_EXCEPTION;
15766 }
15767 return obj;
15768}
15769
15770static const uint16_t func_kind_to_class_id[] = {
15771 [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
15772 [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
15773 [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
15774 [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
15775};
15776
15777static JSValue js_closure(JSContext *ctx, JSValue bfunc,
15778 JSVarRef **cur_var_refs,
15779 JSStackFrame *sf)
15780{
15781 JSFunctionBytecode *b;
15782 JSValue func_obj;
15783 JSAtom name_atom;
15784
15785 b = JS_VALUE_GET_PTR(bfunc);
15786 func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
15787 if (JS_IsException(func_obj)) {
15788 JS_FreeValue(ctx, bfunc);
15789 return JS_EXCEPTION;
15790 }
15791 func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
15792 if (JS_IsException(func_obj)) {
15793 /* bfunc has been freed */
15794 goto fail;
15795 }
15796 name_atom = b->func_name;
15797 if (name_atom == JS_ATOM_NULL)
15798 name_atom = JS_ATOM_empty_string;
15799 js_function_set_properties(ctx, func_obj, name_atom,
15800 b->defined_arg_count);
15801
15802 if (b->func_kind & JS_FUNC_GENERATOR) {
15803 JSValue proto;
15804 int proto_class_id;
15805 /* generators have a prototype field which is used as
15806 prototype for the generator object */
15807 if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
15808 proto_class_id = JS_CLASS_ASYNC_GENERATOR;
15809 else
15810 proto_class_id = JS_CLASS_GENERATOR;
15811 proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
15812 if (JS_IsException(proto))
15813 goto fail;
15814 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
15815 JS_PROP_WRITABLE);
15816 } else if (b->has_prototype) {
15817 /* add the 'prototype' property: delay instantiation to avoid
15818 creating cycles for every javascript function. The prototype
15819 object is created on the fly when first accessed */
15820 JS_SetConstructorBit(ctx, func_obj, TRUE);
15821 JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
15822 JS_AUTOINIT_ID_PROTOTYPE, NULL,
15823 JS_PROP_WRITABLE);
15824 }
15825 return func_obj;
15826 fail:
15827 /* bfunc is freed when func_obj is freed */
15828 JS_FreeValue(ctx, func_obj);
15829 return JS_EXCEPTION;
15830}
15831
15832#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)
15833
15834static int js_op_define_class(JSContext *ctx, JSValue *sp,
15835 JSAtom class_name, int class_flags,
15836 JSVarRef **cur_var_refs,
15837 JSStackFrame *sf, BOOL is_computed_name)
15838{
15839 JSValue bfunc, parent_class, proto = JS_UNDEFINED;
15840 JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
15841 JSFunctionBytecode *b;
15842
15843 parent_class = sp[-2];
15844 bfunc = sp[-1];
15845
15846 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
15847 if (JS_IsNull(parent_class)) {
15848 parent_proto = JS_NULL;
15849 parent_class = JS_DupValue(ctx, ctx->function_proto);
15850 } else {
15851 if (!JS_IsConstructor(ctx, parent_class)) {
15852 JS_ThrowTypeError(ctx, "parent class must be constructor");
15853 goto fail;
15854 }
15855 parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
15856 if (JS_IsException(parent_proto))
15857 goto fail;
15858 if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
15859 JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
15860 goto fail;
15861 }
15862 }
15863 } else {
15864 /* parent_class is JS_UNDEFINED in this case */
15865 parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
15866 parent_class = JS_DupValue(ctx, ctx->function_proto);
15867 }
15868 proto = JS_NewObjectProto(ctx, parent_proto);
15869 if (JS_IsException(proto))
15870 goto fail;
15871
15872 b = JS_VALUE_GET_PTR(bfunc);
15873 assert(b->func_kind == JS_FUNC_NORMAL);
15874 ctor = JS_NewObjectProtoClass(ctx, parent_class,
15875 JS_CLASS_BYTECODE_FUNCTION);
15876 if (JS_IsException(ctor))
15877 goto fail;
15878 ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
15879 bfunc = JS_UNDEFINED;
15880 if (JS_IsException(ctor))
15881 goto fail;
15882 js_method_set_home_object(ctx, ctor, proto);
15883 JS_SetConstructorBit(ctx, ctor, TRUE);
15884
15885 JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
15886 JS_NewInt32(ctx, b->defined_arg_count),
15887 JS_PROP_CONFIGURABLE);
15888
15889 if (is_computed_name) {
15890 if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
15891 JS_PROP_CONFIGURABLE) < 0)
15892 goto fail;
15893 } else {
15894 if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
15895 goto fail;
15896 }
15897
15898 /* the constructor property must be first. It can be overriden by
15899 computed property names */
15900 if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
15901 JS_DupValue(ctx, ctor),
15902 JS_PROP_CONFIGURABLE |
15903 JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
15904 goto fail;
15905 /* set the prototype property */
15906 if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
15907 JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
15908 goto fail;
15909 set_cycle_flag(ctx, ctor);
15910 set_cycle_flag(ctx, proto);
15911
15912 JS_FreeValue(ctx, parent_proto);
15913 JS_FreeValue(ctx, parent_class);
15914
15915 sp[-2] = ctor;
15916 sp[-1] = proto;
15917 return 0;
15918 fail:
15919 JS_FreeValue(ctx, parent_class);
15920 JS_FreeValue(ctx, parent_proto);
15921 JS_FreeValue(ctx, bfunc);
15922 JS_FreeValue(ctx, proto);
15923 JS_FreeValue(ctx, ctor);
15924 sp[-2] = JS_UNDEFINED;
15925 sp[-1] = JS_UNDEFINED;
15926 return -1;
15927}
15928
15929static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
15930{
15931 struct list_head *el, *el1;
15932 JSVarRef *var_ref;
15933
15934 list_for_each_safe(el, el1, &sf->var_ref_list) {
15935 var_ref = list_entry(el, JSVarRef, var_ref_link);
15936 /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */
15937 if (var_ref->async_func)
15938 async_func_free(rt, var_ref->async_func);
15939 var_ref->value = JS_DupValueRT(rt, *var_ref->pvalue);
15940 var_ref->pvalue = &var_ref->value;
15941 /* the reference is no longer to a local variable */
15942 var_ref->is_detached = TRUE;
15943 }
15944}
15945
15946static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int var_idx)
15947{
15948 JSValue *pvalue;
15949 struct list_head *el, *el1;
15950 JSVarRef *var_ref;
15951
15952 pvalue = &sf->var_buf[var_idx];
15953 list_for_each_safe(el, el1, &sf->var_ref_list) {
15954 var_ref = list_entry(el, JSVarRef, var_ref_link);
15955 if (var_ref->pvalue == pvalue) {
15956 list_del(&var_ref->var_ref_link);
15957 if (var_ref->async_func)
15958 async_func_free(ctx->rt, var_ref->async_func);
15959 var_ref->value = JS_DupValue(ctx, *var_ref->pvalue);
15960 var_ref->pvalue = &var_ref->value;
15961 /* the reference is no longer to a local variable */
15962 var_ref->is_detached = TRUE;
15963 }
15964 }
15965}
15966
15967#define JS_CALL_FLAG_COPY_ARGV (1 << 1)
15968#define JS_CALL_FLAG_GENERATOR (1 << 2)
15969
15970static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
15971 JSValueConst this_obj,
15972 int argc, JSValueConst *argv, int flags)
15973{
15974 JSRuntime *rt = ctx->rt;
15975 JSCFunctionType func;
15976 JSObject *p;
15977 JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
15978 JSValue ret_val;
15979 JSValueConst *arg_buf;
15980 int arg_count, i;
15981 JSCFunctionEnum cproto;
15982
15983 p = JS_VALUE_GET_OBJ(func_obj);
15984 cproto = p->u.cfunc.cproto;
15985 arg_count = p->u.cfunc.length;
15986
15987 /* better to always check stack overflow */
15988 if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
15989 return JS_ThrowStackOverflow(ctx);
15990
15991 prev_sf = rt->current_stack_frame;
15992 sf->prev_frame = prev_sf;
15993 rt->current_stack_frame = sf;
15994 ctx = p->u.cfunc.realm; /* change the current realm */
15995 sf->js_mode = 0;
15996 sf->cur_func = (JSValue)func_obj;
15997 sf->arg_count = argc;
15998 arg_buf = argv;
15999
16000 if (unlikely(argc < arg_count)) {
16001 /* ensure that at least argc_count arguments are readable */
16002 arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
16003 for(i = 0; i < argc; i++)
16004 arg_buf[i] = argv[i];
16005 for(i = argc; i < arg_count; i++)
16006 arg_buf[i] = JS_UNDEFINED;
16007 sf->arg_count = arg_count;
16008 }
16009 sf->arg_buf = (JSValue*)arg_buf;
16010
16011 func = p->u.cfunc.c_function;
16012 switch(cproto) {
16013 case JS_CFUNC_constructor:
16014 case JS_CFUNC_constructor_or_func:
16015 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16016 if (cproto == JS_CFUNC_constructor) {
16017 not_a_constructor:
16018 ret_val = JS_ThrowTypeError(ctx, "must be called with new");
16019 break;
16020 } else {
16021 this_obj = JS_UNDEFINED;
16022 }
16023 }
16024 /* here this_obj is new_target */
16025 /* fall thru */
16026 case JS_CFUNC_generic:
16027 ret_val = func.generic(ctx, this_obj, argc, arg_buf);
16028 break;
16029 case JS_CFUNC_constructor_magic:
16030 case JS_CFUNC_constructor_or_func_magic:
16031 if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
16032 if (cproto == JS_CFUNC_constructor_magic) {
16033 goto not_a_constructor;
16034 } else {
16035 this_obj = JS_UNDEFINED;
16036 }
16037 }
16038 /* fall thru */
16039 case JS_CFUNC_generic_magic:
16040 ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
16041 p->u.cfunc.magic);
16042 break;
16043 case JS_CFUNC_getter:
16044 ret_val = func.getter(ctx, this_obj);
16045 break;
16046 case JS_CFUNC_setter:
16047 ret_val = func.setter(ctx, this_obj, arg_buf[0]);
16048 break;
16049 case JS_CFUNC_getter_magic:
16050 ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
16051 break;
16052 case JS_CFUNC_setter_magic:
16053 ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
16054 break;
16055 case JS_CFUNC_f_f:
16056 {
16057 double d1;
16058
16059 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16060 ret_val = JS_EXCEPTION;
16061 break;
16062 }
16063 ret_val = JS_NewFloat64(ctx, func.f_f(d1));
16064 }
16065 break;
16066 case JS_CFUNC_f_f_f:
16067 {
16068 double d1, d2;
16069
16070 if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
16071 ret_val = JS_EXCEPTION;
16072 break;
16073 }
16074 if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
16075 ret_val = JS_EXCEPTION;
16076 break;
16077 }
16078 ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
16079 }
16080 break;
16081 case JS_CFUNC_iterator_next:
16082 {
16083 int done;
16084 ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
16085 &done, p->u.cfunc.magic);
16086 if (!JS_IsException(ret_val) && done != 2) {
16087 ret_val = js_create_iterator_result(ctx, ret_val, done);
16088 }
16089 }
16090 break;
16091 default:
16092 abort();
16093 }
16094
16095 rt->current_stack_frame = sf->prev_frame;
16096 return ret_val;
16097}
16098
16099static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
16100 JSValueConst this_obj,
16101 int argc, JSValueConst *argv, int flags)
16102{
16103 JSObject *p;
16104 JSBoundFunction *bf;
16105 JSValueConst *arg_buf, new_target;
16106 int arg_count, i;
16107
16108 p = JS_VALUE_GET_OBJ(func_obj);
16109 bf = p->u.bound_function;
16110 arg_count = bf->argc + argc;
16111 if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
16112 return JS_ThrowStackOverflow(ctx);
16113 arg_buf = alloca(sizeof(JSValue) * arg_count);
16114 for(i = 0; i < bf->argc; i++) {
16115 arg_buf[i] = bf->argv[i];
16116 }
16117 for(i = 0; i < argc; i++) {
16118 arg_buf[bf->argc + i] = argv[i];
16119 }
16120 if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
16121 new_target = this_obj;
16122 if (js_same_value(ctx, func_obj, new_target))
16123 new_target = bf->func_obj;
16124 return JS_CallConstructor2(ctx, bf->func_obj, new_target,
16125 arg_count, arg_buf);
16126 } else {
16127 return JS_Call(ctx, bf->func_obj, bf->this_val,
16128 arg_count, arg_buf);
16129 }
16130}
16131
16132/* argument of OP_special_object */
16133typedef enum {
16134 OP_SPECIAL_OBJECT_ARGUMENTS,
16135 OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
16136 OP_SPECIAL_OBJECT_THIS_FUNC,
16137 OP_SPECIAL_OBJECT_NEW_TARGET,
16138 OP_SPECIAL_OBJECT_HOME_OBJECT,
16139 OP_SPECIAL_OBJECT_VAR_OBJECT,
16140 OP_SPECIAL_OBJECT_IMPORT_META,
16141} OPSpecialObjectEnum;
16142
16143#define FUNC_RET_AWAIT 0
16144#define FUNC_RET_YIELD 1
16145#define FUNC_RET_YIELD_STAR 2
16146#define FUNC_RET_INITIAL_YIELD 3
16147
16148/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
16149static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
16150 JSValueConst this_obj, JSValueConst new_target,
16151 int argc, JSValue *argv, int flags)
16152{
16153 JSRuntime *rt = caller_ctx->rt;
16154 JSContext *ctx;
16155 JSObject *p;
16156 JSFunctionBytecode *b;
16157 JSStackFrame sf_s, *sf = &sf_s;
16158 const uint8_t *pc;
16159 int opcode, arg_allocated_size, i;
16160 JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
16161 JSVarRef **var_refs;
16162 size_t alloca_size;
16163
16164#if !DIRECT_DISPATCH
16165#define SWITCH(pc) switch (opcode = *pc++)
16166#define CASE(op) case op
16167#define DEFAULT default
16168#define BREAK break
16169#else
16170 static const void * const dispatch_table[256] = {
16171#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
16172#if SHORT_OPCODES
16173#define def(id, size, n_pop, n_push, f)
16174#else
16175#define def(id, size, n_pop, n_push, f) && case_default,
16176#endif
16177#include "quickjs-opcode.h"
16178 [ OP_COUNT ... 255 ] = &&case_default
16179 };
16180#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];
16181#define CASE(op) case_ ## op
16182#define DEFAULT case_default
16183#define BREAK SWITCH(pc)
16184#endif
16185
16186 if (js_poll_interrupts(caller_ctx))
16187 return JS_EXCEPTION;
16188 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
16189 if (flags & JS_CALL_FLAG_GENERATOR) {
16190 JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
16191 /* func_obj get contains a pointer to JSFuncAsyncState */
16192 /* the stack frame is already allocated */
16193 sf = &s->frame;
16194 p = JS_VALUE_GET_OBJ(sf->cur_func);
16195 b = p->u.func.function_bytecode;
16196 ctx = b->realm;
16197 var_refs = p->u.func.var_refs;
16198 local_buf = arg_buf = sf->arg_buf;
16199 var_buf = sf->var_buf;
16200 stack_buf = sf->var_buf + b->var_count;
16201 sp = sf->cur_sp;
16202 sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
16203 pc = sf->cur_pc;
16204 sf->prev_frame = rt->current_stack_frame;
16205 rt->current_stack_frame = sf;
16206 if (s->throw_flag)
16207 goto exception;
16208 else
16209 goto restart;
16210 } else {
16211 goto not_a_function;
16212 }
16213 }
16214 p = JS_VALUE_GET_OBJ(func_obj);
16215 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
16216 JSClassCall *call_func;
16217 call_func = rt->class_array[p->class_id].call;
16218 if (!call_func) {
16219 not_a_function:
16220 return JS_ThrowTypeError(caller_ctx, "not a function");
16221 }
16222 return call_func(caller_ctx, func_obj, this_obj, argc,
16223 (JSValueConst *)argv, flags);
16224 }
16225 b = p->u.func.function_bytecode;
16226
16227 if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
16228 arg_allocated_size = b->arg_count;
16229 } else {
16230 arg_allocated_size = 0;
16231 }
16232
16233 alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
16234 b->stack_size);
16235 if (js_check_stack_overflow(rt, alloca_size))
16236 return JS_ThrowStackOverflow(caller_ctx);
16237
16238 sf->js_mode = b->js_mode;
16239 arg_buf = argv;
16240 sf->arg_count = argc;
16241 sf->cur_func = (JSValue)func_obj;
16242 init_list_head(&sf->var_ref_list);
16243 var_refs = p->u.func.var_refs;
16244
16245 local_buf = alloca(alloca_size);
16246 if (unlikely(arg_allocated_size)) {
16247 int n = min_int(argc, b->arg_count);
16248 arg_buf = local_buf;
16249 for(i = 0; i < n; i++)
16250 arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
16251 for(; i < b->arg_count; i++)
16252 arg_buf[i] = JS_UNDEFINED;
16253 sf->arg_count = b->arg_count;
16254 }
16255 var_buf = local_buf + arg_allocated_size;
16256 sf->var_buf = var_buf;
16257 sf->arg_buf = arg_buf;
16258
16259 for(i = 0; i < b->var_count; i++)
16260 var_buf[i] = JS_UNDEFINED;
16261
16262 stack_buf = var_buf + b->var_count;
16263 sp = stack_buf;
16264 pc = b->byte_code_buf;
16265 sf->prev_frame = rt->current_stack_frame;
16266 rt->current_stack_frame = sf;
16267 ctx = b->realm; /* set the current realm */
16268
16269 restart:
16270 for(;;) {
16271 int call_argc;
16272 JSValue *call_argv;
16273
16274 SWITCH(pc) {
16275 CASE(OP_push_i32):
16276 *sp++ = JS_NewInt32(ctx, get_u32(pc));
16277 pc += 4;
16278 BREAK;
16279 CASE(OP_push_bigint_i32):
16280 *sp++ = __JS_NewShortBigInt(ctx, (int)get_u32(pc));
16281 pc += 4;
16282 BREAK;
16283 CASE(OP_push_const):
16284 *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16285 pc += 4;
16286 BREAK;
16287#if SHORT_OPCODES
16288 CASE(OP_push_minus1):
16289 CASE(OP_push_0):
16290 CASE(OP_push_1):
16291 CASE(OP_push_2):
16292 CASE(OP_push_3):
16293 CASE(OP_push_4):
16294 CASE(OP_push_5):
16295 CASE(OP_push_6):
16296 CASE(OP_push_7):
16297 *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
16298 BREAK;
16299 CASE(OP_push_i8):
16300 *sp++ = JS_NewInt32(ctx, get_i8(pc));
16301 pc += 1;
16302 BREAK;
16303 CASE(OP_push_i16):
16304 *sp++ = JS_NewInt32(ctx, get_i16(pc));
16305 pc += 2;
16306 BREAK;
16307 CASE(OP_push_const8):
16308 *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
16309 BREAK;
16310 CASE(OP_fclosure8):
16311 *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
16312 if (unlikely(JS_IsException(sp[-1])))
16313 goto exception;
16314 BREAK;
16315 CASE(OP_push_empty_string):
16316 *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
16317 BREAK;
16318 CASE(OP_get_length):
16319 {
16320 JSValue val;
16321
16322 sf->cur_pc = pc;
16323 val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
16324 if (unlikely(JS_IsException(val)))
16325 goto exception;
16326 JS_FreeValue(ctx, sp[-1]);
16327 sp[-1] = val;
16328 }
16329 BREAK;
16330#endif
16331 CASE(OP_push_atom_value):
16332 *sp++ = JS_AtomToValue(ctx, get_u32(pc));
16333 pc += 4;
16334 BREAK;
16335 CASE(OP_undefined):
16336 *sp++ = JS_UNDEFINED;
16337 BREAK;
16338 CASE(OP_null):
16339 *sp++ = JS_NULL;
16340 BREAK;
16341 CASE(OP_push_this):
16342 /* OP_push_this is only called at the start of a function */
16343 {
16344 JSValue val;
16345 if (!(b->js_mode & JS_MODE_STRICT)) {
16346 uint32_t tag = JS_VALUE_GET_TAG(this_obj);
16347 if (likely(tag == JS_TAG_OBJECT))
16348 goto normal_this;
16349 if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
16350 val = JS_DupValue(ctx, ctx->global_obj);
16351 } else {
16352 val = JS_ToObject(ctx, this_obj);
16353 if (JS_IsException(val))
16354 goto exception;
16355 }
16356 } else {
16357 normal_this:
16358 val = JS_DupValue(ctx, this_obj);
16359 }
16360 *sp++ = val;
16361 }
16362 BREAK;
16363 CASE(OP_push_false):
16364 *sp++ = JS_FALSE;
16365 BREAK;
16366 CASE(OP_push_true):
16367 *sp++ = JS_TRUE;
16368 BREAK;
16369 CASE(OP_object):
16370 *sp++ = JS_NewObject(ctx);
16371 if (unlikely(JS_IsException(sp[-1])))
16372 goto exception;
16373 BREAK;
16374 CASE(OP_special_object):
16375 {
16376 int arg = *pc++;
16377 switch(arg) {
16378 case OP_SPECIAL_OBJECT_ARGUMENTS:
16379 *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
16380 if (unlikely(JS_IsException(sp[-1])))
16381 goto exception;
16382 break;
16383 case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
16384 *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
16385 sf, min_int(argc, b->arg_count));
16386 if (unlikely(JS_IsException(sp[-1])))
16387 goto exception;
16388 break;
16389 case OP_SPECIAL_OBJECT_THIS_FUNC:
16390 *sp++ = JS_DupValue(ctx, sf->cur_func);
16391 break;
16392 case OP_SPECIAL_OBJECT_NEW_TARGET:
16393 *sp++ = JS_DupValue(ctx, new_target);
16394 break;
16395 case OP_SPECIAL_OBJECT_HOME_OBJECT:
16396 {
16397 JSObject *p1;
16398 p1 = p->u.func.home_object;
16399 if (unlikely(!p1))
16400 *sp++ = JS_UNDEFINED;
16401 else
16402 *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
16403 }
16404 break;
16405 case OP_SPECIAL_OBJECT_VAR_OBJECT:
16406 *sp++ = JS_NewObjectProto(ctx, JS_NULL);
16407 if (unlikely(JS_IsException(sp[-1])))
16408 goto exception;
16409 break;
16410 case OP_SPECIAL_OBJECT_IMPORT_META:
16411 *sp++ = js_import_meta(ctx);
16412 if (unlikely(JS_IsException(sp[-1])))
16413 goto exception;
16414 break;
16415 default:
16416 abort();
16417 }
16418 }
16419 BREAK;
16420 CASE(OP_rest):
16421 {
16422 int first = get_u16(pc);
16423 pc += 2;
16424 *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
16425 if (unlikely(JS_IsException(sp[-1])))
16426 goto exception;
16427 }
16428 BREAK;
16429
16430 CASE(OP_drop):
16431 JS_FreeValue(ctx, sp[-1]);
16432 sp--;
16433 BREAK;
16434 CASE(OP_nip):
16435 JS_FreeValue(ctx, sp[-2]);
16436 sp[-2] = sp[-1];
16437 sp--;
16438 BREAK;
16439 CASE(OP_nip1): /* a b c -> b c */
16440 JS_FreeValue(ctx, sp[-3]);
16441 sp[-3] = sp[-2];
16442 sp[-2] = sp[-1];
16443 sp--;
16444 BREAK;
16445 CASE(OP_dup):
16446 sp[0] = JS_DupValue(ctx, sp[-1]);
16447 sp++;
16448 BREAK;
16449 CASE(OP_dup2): /* a b -> a b a b */
16450 sp[0] = JS_DupValue(ctx, sp[-2]);
16451 sp[1] = JS_DupValue(ctx, sp[-1]);
16452 sp += 2;
16453 BREAK;
16454 CASE(OP_dup3): /* a b c -> a b c a b c */
16455 sp[0] = JS_DupValue(ctx, sp[-3]);
16456 sp[1] = JS_DupValue(ctx, sp[-2]);
16457 sp[2] = JS_DupValue(ctx, sp[-1]);
16458 sp += 3;
16459 BREAK;
16460 CASE(OP_dup1): /* a b -> a a b */
16461 sp[0] = sp[-1];
16462 sp[-1] = JS_DupValue(ctx, sp[-2]);
16463 sp++;
16464 BREAK;
16465 CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
16466 sp[0] = sp[-1];
16467 sp[-1] = sp[-2];
16468 sp[-2] = JS_DupValue(ctx, sp[0]);
16469 sp++;
16470 BREAK;
16471 CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
16472 sp[0] = sp[-1];
16473 sp[-1] = sp[-2];
16474 sp[-2] = sp[-3];
16475 sp[-3] = JS_DupValue(ctx, sp[0]);
16476 sp++;
16477 BREAK;
16478 CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
16479 sp[0] = sp[-1];
16480 sp[-1] = sp[-2];
16481 sp[-2] = sp[-3];
16482 sp[-3] = sp[-4];
16483 sp[-4] = JS_DupValue(ctx, sp[0]);
16484 sp++;
16485 BREAK;
16486 CASE(OP_perm3): /* obj a b -> a obj b (213) */
16487 {
16488 JSValue tmp;
16489 tmp = sp[-2];
16490 sp[-2] = sp[-3];
16491 sp[-3] = tmp;
16492 }
16493 BREAK;
16494 CASE(OP_rot3l): /* x a b -> a b x (231) */
16495 {
16496 JSValue tmp;
16497 tmp = sp[-3];
16498 sp[-3] = sp[-2];
16499 sp[-2] = sp[-1];
16500 sp[-1] = tmp;
16501 }
16502 BREAK;
16503 CASE(OP_rot4l): /* x a b c -> a b c x */
16504 {
16505 JSValue tmp;
16506 tmp = sp[-4];
16507 sp[-4] = sp[-3];
16508 sp[-3] = sp[-2];
16509 sp[-2] = sp[-1];
16510 sp[-1] = tmp;
16511 }
16512 BREAK;
16513 CASE(OP_rot5l): /* x a b c d -> a b c d x */
16514 {
16515 JSValue tmp;
16516 tmp = sp[-5];
16517 sp[-5] = sp[-4];
16518 sp[-4] = sp[-3];
16519 sp[-3] = sp[-2];
16520 sp[-2] = sp[-1];
16521 sp[-1] = tmp;
16522 }
16523 BREAK;
16524 CASE(OP_rot3r): /* a b x -> x a b (312) */
16525 {
16526 JSValue tmp;
16527 tmp = sp[-1];
16528 sp[-1] = sp[-2];
16529 sp[-2] = sp[-3];
16530 sp[-3] = tmp;
16531 }
16532 BREAK;
16533 CASE(OP_perm4): /* obj prop a b -> a obj prop b */
16534 {
16535 JSValue tmp;
16536 tmp = sp[-2];
16537 sp[-2] = sp[-3];
16538 sp[-3] = sp[-4];
16539 sp[-4] = tmp;
16540 }
16541 BREAK;
16542 CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
16543 {
16544 JSValue tmp;
16545 tmp = sp[-2];
16546 sp[-2] = sp[-3];
16547 sp[-3] = sp[-4];
16548 sp[-4] = sp[-5];
16549 sp[-5] = tmp;
16550 }
16551 BREAK;
16552 CASE(OP_swap): /* a b -> b a */
16553 {
16554 JSValue tmp;
16555 tmp = sp[-2];
16556 sp[-2] = sp[-1];
16557 sp[-1] = tmp;
16558 }
16559 BREAK;
16560 CASE(OP_swap2): /* a b c d -> c d a b */
16561 {
16562 JSValue tmp1, tmp2;
16563 tmp1 = sp[-4];
16564 tmp2 = sp[-3];
16565 sp[-4] = sp[-2];
16566 sp[-3] = sp[-1];
16567 sp[-2] = tmp1;
16568 sp[-1] = tmp2;
16569 }
16570 BREAK;
16571
16572 CASE(OP_fclosure):
16573 {
16574 JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
16575 pc += 4;
16576 *sp++ = js_closure(ctx, bfunc, var_refs, sf);
16577 if (unlikely(JS_IsException(sp[-1])))
16578 goto exception;
16579 }
16580 BREAK;
16581#if SHORT_OPCODES
16582 CASE(OP_call0):
16583 CASE(OP_call1):
16584 CASE(OP_call2):
16585 CASE(OP_call3):
16586 call_argc = opcode - OP_call0;
16587 goto has_call_argc;
16588#endif
16589 CASE(OP_call):
16590 CASE(OP_tail_call):
16591 {
16592 call_argc = get_u16(pc);
16593 pc += 2;
16594 goto has_call_argc;
16595 has_call_argc:
16596 call_argv = sp - call_argc;
16597 sf->cur_pc = pc;
16598 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16599 JS_UNDEFINED, call_argc, call_argv, 0);
16600 if (unlikely(JS_IsException(ret_val)))
16601 goto exception;
16602 if (opcode == OP_tail_call)
16603 goto done;
16604 for(i = -1; i < call_argc; i++)
16605 JS_FreeValue(ctx, call_argv[i]);
16606 sp -= call_argc + 1;
16607 *sp++ = ret_val;
16608 }
16609 BREAK;
16610 CASE(OP_call_constructor):
16611 {
16612 call_argc = get_u16(pc);
16613 pc += 2;
16614 call_argv = sp - call_argc;
16615 sf->cur_pc = pc;
16616 ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
16617 call_argv[-1],
16618 call_argc, call_argv, 0);
16619 if (unlikely(JS_IsException(ret_val)))
16620 goto exception;
16621 for(i = -2; i < call_argc; i++)
16622 JS_FreeValue(ctx, call_argv[i]);
16623 sp -= call_argc + 2;
16624 *sp++ = ret_val;
16625 }
16626 BREAK;
16627 CASE(OP_call_method):
16628 CASE(OP_tail_call_method):
16629 {
16630 call_argc = get_u16(pc);
16631 pc += 2;
16632 call_argv = sp - call_argc;
16633 sf->cur_pc = pc;
16634 ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
16635 JS_UNDEFINED, call_argc, call_argv, 0);
16636 if (unlikely(JS_IsException(ret_val)))
16637 goto exception;
16638 if (opcode == OP_tail_call_method)
16639 goto done;
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_array_from):
16647 {
16648 int i, ret;
16649
16650 call_argc = get_u16(pc);
16651 pc += 2;
16652 ret_val = JS_NewArray(ctx);
16653 if (unlikely(JS_IsException(ret_val)))
16654 goto exception;
16655 call_argv = sp - call_argc;
16656 for(i = 0; i < call_argc; i++) {
16657 ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
16658 JS_PROP_C_W_E | JS_PROP_THROW);
16659 call_argv[i] = JS_UNDEFINED;
16660 if (ret < 0) {
16661 JS_FreeValue(ctx, ret_val);
16662 goto exception;
16663 }
16664 }
16665 sp -= call_argc;
16666 *sp++ = ret_val;
16667 }
16668 BREAK;
16669
16670 CASE(OP_apply):
16671 {
16672 int magic;
16673 magic = get_u16(pc);
16674 pc += 2;
16675 sf->cur_pc = pc;
16676
16677 ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
16678 if (unlikely(JS_IsException(ret_val)))
16679 goto exception;
16680 JS_FreeValue(ctx, sp[-3]);
16681 JS_FreeValue(ctx, sp[-2]);
16682 JS_FreeValue(ctx, sp[-1]);
16683 sp -= 3;
16684 *sp++ = ret_val;
16685 }
16686 BREAK;
16687 CASE(OP_return):
16688 ret_val = *--sp;
16689 goto done;
16690 CASE(OP_return_undef):
16691 ret_val = JS_UNDEFINED;
16692 goto done;
16693
16694 CASE(OP_check_ctor_return):
16695 /* return TRUE if 'this' should be returned */
16696 if (!JS_IsObject(sp[-1])) {
16697 if (!JS_IsUndefined(sp[-1])) {
16698 JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
16699 goto exception;
16700 }
16701 sp[0] = JS_TRUE;
16702 } else {
16703 sp[0] = JS_FALSE;
16704 }
16705 sp++;
16706 BREAK;
16707 CASE(OP_check_ctor):
16708 if (JS_IsUndefined(new_target)) {
16709 non_ctor_call:
16710 JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
16711 goto exception;
16712 }
16713 BREAK;
16714 CASE(OP_init_ctor):
16715 {
16716 JSValue super, ret;
16717 sf->cur_pc = pc;
16718 if (JS_IsUndefined(new_target))
16719 goto non_ctor_call;
16720 super = JS_GetPrototype(ctx, func_obj);
16721 if (JS_IsException(super))
16722 goto exception;
16723 ret = JS_CallConstructor2(ctx, super, new_target, argc, (JSValueConst *)argv);
16724 JS_FreeValue(ctx, super);
16725 if (JS_IsException(ret))
16726 goto exception;
16727 *sp++ = ret;
16728 }
16729 BREAK;
16730 CASE(OP_check_brand):
16731 {
16732 int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]);
16733 if (ret < 0)
16734 goto exception;
16735 if (!ret) {
16736 JS_ThrowTypeError(ctx, "invalid brand on object");
16737 goto exception;
16738 }
16739 }
16740 BREAK;
16741 CASE(OP_add_brand):
16742 if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
16743 goto exception;
16744 JS_FreeValue(ctx, sp[-2]);
16745 JS_FreeValue(ctx, sp[-1]);
16746 sp -= 2;
16747 BREAK;
16748
16749 CASE(OP_throw):
16750 JS_Throw(ctx, *--sp);
16751 goto exception;
16752
16753 CASE(OP_throw_error):
16754#define JS_THROW_VAR_RO 0
16755#define JS_THROW_VAR_REDECL 1
16756#define JS_THROW_VAR_UNINITIALIZED 2
16757#define JS_THROW_ERROR_DELETE_SUPER 3
16758#define JS_THROW_ERROR_ITERATOR_THROW 4
16759 {
16760 JSAtom atom;
16761 int type;
16762 atom = get_u32(pc);
16763 type = pc[4];
16764 pc += 5;
16765 if (type == JS_THROW_VAR_RO)
16766 JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
16767 else
16768 if (type == JS_THROW_VAR_REDECL)
16769 JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
16770 else
16771 if (type == JS_THROW_VAR_UNINITIALIZED)
16772 JS_ThrowReferenceErrorUninitialized(ctx, atom);
16773 else
16774 if (type == JS_THROW_ERROR_DELETE_SUPER)
16775 JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
16776 else
16777 if (type == JS_THROW_ERROR_ITERATOR_THROW)
16778 JS_ThrowTypeError(ctx, "iterator does not have a throw method");
16779 else
16780 JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
16781 }
16782 goto exception;
16783
16784 CASE(OP_eval):
16785 {
16786 JSValueConst obj;
16787 int scope_idx;
16788 call_argc = get_u16(pc);
16789 scope_idx = get_u16(pc + 2) + ARG_SCOPE_END;
16790 pc += 4;
16791 call_argv = sp - call_argc;
16792 sf->cur_pc = pc;
16793 if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
16794 if (call_argc >= 1)
16795 obj = call_argv[0];
16796 else
16797 obj = JS_UNDEFINED;
16798 ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
16799 JS_EVAL_TYPE_DIRECT, scope_idx);
16800 } else {
16801 ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
16802 JS_UNDEFINED, call_argc, call_argv, 0);
16803 }
16804 if (unlikely(JS_IsException(ret_val)))
16805 goto exception;
16806 for(i = -1; i < call_argc; i++)
16807 JS_FreeValue(ctx, call_argv[i]);
16808 sp -= call_argc + 1;
16809 *sp++ = ret_val;
16810 }
16811 BREAK;
16812 /* could merge with OP_apply */
16813 CASE(OP_apply_eval):
16814 {
16815 int scope_idx;
16816 uint32_t len;
16817 JSValue *tab;
16818 JSValueConst obj;
16819
16820 scope_idx = get_u16(pc) + ARG_SCOPE_END;
16821 pc += 2;
16822 sf->cur_pc = pc;
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 sf->cur_pc = pc;
16859 proto = JS_GetPrototype(ctx, sp[-1]);
16860 if (JS_IsException(proto))
16861 goto exception;
16862 JS_FreeValue(ctx, sp[-1]);
16863 sp[-1] = proto;
16864 }
16865 BREAK;
16866
16867 CASE(OP_import):
16868 {
16869 JSValue val;
16870 sf->cur_pc = pc;
16871 val = js_dynamic_import(ctx, sp[-1]);
16872 if (JS_IsException(val))
16873 goto exception;
16874 JS_FreeValue(ctx, sp[-1]);
16875 sp[-1] = val;
16876 }
16877 BREAK;
16878
16879 CASE(OP_check_var):
16880 {
16881 int ret;
16882 JSAtom atom;
16883 atom = get_u32(pc);
16884 pc += 4;
16885 sf->cur_pc = pc;
16886
16887 ret = JS_CheckGlobalVar(ctx, atom);
16888 if (ret < 0)
16889 goto exception;
16890 *sp++ = JS_NewBool(ctx, ret);
16891 }
16892 BREAK;
16893
16894 CASE(OP_get_var_undef):
16895 CASE(OP_get_var):
16896 {
16897 JSValue val;
16898 JSAtom atom;
16899 atom = get_u32(pc);
16900 pc += 4;
16901 sf->cur_pc = pc;
16902
16903 val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
16904 if (unlikely(JS_IsException(val)))
16905 goto exception;
16906 *sp++ = val;
16907 }
16908 BREAK;
16909
16910 CASE(OP_put_var):
16911 CASE(OP_put_var_init):
16912 {
16913 int ret;
16914 JSAtom atom;
16915 atom = get_u32(pc);
16916 pc += 4;
16917 sf->cur_pc = pc;
16918
16919 ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
16920 sp--;
16921 if (unlikely(ret < 0))
16922 goto exception;
16923 }
16924 BREAK;
16925
16926 CASE(OP_put_var_strict):
16927 {
16928 int ret;
16929 JSAtom atom;
16930 atom = get_u32(pc);
16931 pc += 4;
16932 sf->cur_pc = pc;
16933
16934 /* sp[-2] is JS_TRUE or JS_FALSE */
16935 if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
16936 JS_ThrowReferenceErrorNotDefined(ctx, atom);
16937 goto exception;
16938 }
16939 ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
16940 sp -= 2;
16941 if (unlikely(ret < 0))
16942 goto exception;
16943 }
16944 BREAK;
16945
16946 CASE(OP_check_define_var):
16947 {
16948 JSAtom atom;
16949 int flags;
16950 atom = get_u32(pc);
16951 flags = pc[4];
16952 pc += 5;
16953 sf->cur_pc = pc;
16954 if (JS_CheckDefineGlobalVar(ctx, atom, flags))
16955 goto exception;
16956 }
16957 BREAK;
16958 CASE(OP_define_var):
16959 {
16960 JSAtom atom;
16961 int flags;
16962 atom = get_u32(pc);
16963 flags = pc[4];
16964 pc += 5;
16965 sf->cur_pc = pc;
16966 if (JS_DefineGlobalVar(ctx, atom, flags))
16967 goto exception;
16968 }
16969 BREAK;
16970 CASE(OP_define_func):
16971 {
16972 JSAtom atom;
16973 int flags;
16974 atom = get_u32(pc);
16975 flags = pc[4];
16976 pc += 5;
16977 sf->cur_pc = pc;
16978 if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
16979 goto exception;
16980 JS_FreeValue(ctx, sp[-1]);
16981 sp--;
16982 }
16983 BREAK;
16984
16985 CASE(OP_get_loc):
16986 {
16987 int idx;
16988 idx = get_u16(pc);
16989 pc += 2;
16990 sp[0] = JS_DupValue(ctx, var_buf[idx]);
16991 sp++;
16992 }
16993 BREAK;
16994 CASE(OP_put_loc):
16995 {
16996 int idx;
16997 idx = get_u16(pc);
16998 pc += 2;
16999 set_value(ctx, &var_buf[idx], sp[-1]);
17000 sp--;
17001 }
17002 BREAK;
17003 CASE(OP_set_loc):
17004 {
17005 int idx;
17006 idx = get_u16(pc);
17007 pc += 2;
17008 set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
17009 }
17010 BREAK;
17011 CASE(OP_get_arg):
17012 {
17013 int idx;
17014 idx = get_u16(pc);
17015 pc += 2;
17016 sp[0] = JS_DupValue(ctx, arg_buf[idx]);
17017 sp++;
17018 }
17019 BREAK;
17020 CASE(OP_put_arg):
17021 {
17022 int idx;
17023 idx = get_u16(pc);
17024 pc += 2;
17025 set_value(ctx, &arg_buf[idx], sp[-1]);
17026 sp--;
17027 }
17028 BREAK;
17029 CASE(OP_set_arg):
17030 {
17031 int idx;
17032 idx = get_u16(pc);
17033 pc += 2;
17034 set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
17035 }
17036 BREAK;
17037
17038#if SHORT_OPCODES
17039 CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
17040 CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
17041 CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
17042
17043 CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
17044 CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
17045 CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
17046 CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
17047 CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
17048 CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
17049 CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
17050 CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
17051 CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17052 CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17053 CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17054 CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17055 CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
17056 CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
17057 CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
17058 CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
17059 CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
17060 CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
17061 CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
17062 CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
17063 CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
17064 CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
17065 CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
17066 CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
17067 CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
17068 CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
17069 CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
17070 CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
17071 CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
17072 CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
17073 CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
17074 CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
17075 CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17076 CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17077 CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17078 CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
17079#endif
17080
17081 CASE(OP_get_var_ref):
17082 {
17083 int idx;
17084 JSValue val;
17085 idx = get_u16(pc);
17086 pc += 2;
17087 val = *var_refs[idx]->pvalue;
17088 sp[0] = JS_DupValue(ctx, val);
17089 sp++;
17090 }
17091 BREAK;
17092 CASE(OP_put_var_ref):
17093 {
17094 int idx;
17095 idx = get_u16(pc);
17096 pc += 2;
17097 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17098 sp--;
17099 }
17100 BREAK;
17101 CASE(OP_set_var_ref):
17102 {
17103 int idx;
17104 idx = get_u16(pc);
17105 pc += 2;
17106 set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
17107 }
17108 BREAK;
17109 CASE(OP_get_var_ref_check):
17110 {
17111 int idx;
17112 JSValue val;
17113 idx = get_u16(pc);
17114 pc += 2;
17115 val = *var_refs[idx]->pvalue;
17116 if (unlikely(JS_IsUninitialized(val))) {
17117 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17118 goto exception;
17119 }
17120 sp[0] = JS_DupValue(ctx, val);
17121 sp++;
17122 }
17123 BREAK;
17124 CASE(OP_put_var_ref_check):
17125 {
17126 int idx;
17127 idx = get_u16(pc);
17128 pc += 2;
17129 if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17130 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17131 goto exception;
17132 }
17133 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17134 sp--;
17135 }
17136 BREAK;
17137 CASE(OP_put_var_ref_check_init):
17138 {
17139 int idx;
17140 idx = get_u16(pc);
17141 pc += 2;
17142 if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
17143 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
17144 goto exception;
17145 }
17146 set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
17147 sp--;
17148 }
17149 BREAK;
17150 CASE(OP_set_loc_uninitialized):
17151 {
17152 int idx;
17153 idx = get_u16(pc);
17154 pc += 2;
17155 set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
17156 }
17157 BREAK;
17158 CASE(OP_get_loc_check):
17159 {
17160 int idx;
17161 idx = get_u16(pc);
17162 pc += 2;
17163 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17164 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17165 goto exception;
17166 }
17167 sp[0] = JS_DupValue(ctx, var_buf[idx]);
17168 sp++;
17169 }
17170 BREAK;
17171 CASE(OP_get_loc_checkthis):
17172 {
17173 int idx;
17174 idx = get_u16(pc);
17175 pc += 2;
17176 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17177 JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE);
17178 goto exception;
17179 }
17180 sp[0] = JS_DupValue(ctx, var_buf[idx]);
17181 sp++;
17182 }
17183 BREAK;
17184 CASE(OP_put_loc_check):
17185 {
17186 int idx;
17187 idx = get_u16(pc);
17188 pc += 2;
17189 if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
17190 JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
17191 goto exception;
17192 }
17193 set_value(ctx, &var_buf[idx], sp[-1]);
17194 sp--;
17195 }
17196 BREAK;
17197 CASE(OP_put_loc_check_init):
17198 {
17199 int idx;
17200 idx = get_u16(pc);
17201 pc += 2;
17202 if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
17203 JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
17204 goto exception;
17205 }
17206 set_value(ctx, &var_buf[idx], sp[-1]);
17207 sp--;
17208 }
17209 BREAK;
17210 CASE(OP_close_loc):
17211 {
17212 int idx;
17213 idx = get_u16(pc);
17214 pc += 2;
17215 close_lexical_var(ctx, sf, idx);
17216 }
17217 BREAK;
17218
17219 CASE(OP_make_loc_ref):
17220 CASE(OP_make_arg_ref):
17221 CASE(OP_make_var_ref_ref):
17222 {
17223 JSVarRef *var_ref;
17224 JSProperty *pr;
17225 JSAtom atom;
17226 int idx;
17227 atom = get_u32(pc);
17228 idx = get_u16(pc + 4);
17229 pc += 6;
17230 *sp++ = JS_NewObjectProto(ctx, JS_NULL);
17231 if (unlikely(JS_IsException(sp[-1])))
17232 goto exception;
17233 if (opcode == OP_make_var_ref_ref) {
17234 var_ref = var_refs[idx];
17235 var_ref->header.ref_count++;
17236 } else {
17237 var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
17238 if (!var_ref)
17239 goto exception;
17240 }
17241 pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
17242 JS_PROP_WRITABLE | JS_PROP_VARREF);
17243 if (!pr) {
17244 free_var_ref(rt, var_ref);
17245 goto exception;
17246 }
17247 pr->u.var_ref = var_ref;
17248 *sp++ = JS_AtomToValue(ctx, atom);
17249 }
17250 BREAK;
17251 CASE(OP_make_var_ref):
17252 {
17253 JSAtom atom;
17254 atom = get_u32(pc);
17255 pc += 4;
17256 sf->cur_pc = pc;
17257
17258 if (JS_GetGlobalVarRef(ctx, atom, sp))
17259 goto exception;
17260 sp += 2;
17261 }
17262 BREAK;
17263
17264 CASE(OP_goto):
17265 pc += (int32_t)get_u32(pc);
17266 if (unlikely(js_poll_interrupts(ctx)))
17267 goto exception;
17268 BREAK;
17269#if SHORT_OPCODES
17270 CASE(OP_goto16):
17271 pc += (int16_t)get_u16(pc);
17272 if (unlikely(js_poll_interrupts(ctx)))
17273 goto exception;
17274 BREAK;
17275 CASE(OP_goto8):
17276 pc += (int8_t)pc[0];
17277 if (unlikely(js_poll_interrupts(ctx)))
17278 goto exception;
17279 BREAK;
17280#endif
17281 CASE(OP_if_true):
17282 {
17283 int res;
17284 JSValue op1;
17285
17286 op1 = sp[-1];
17287 pc += 4;
17288 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17289 res = JS_VALUE_GET_INT(op1);
17290 } else {
17291 res = JS_ToBoolFree(ctx, op1);
17292 }
17293 sp--;
17294 if (res) {
17295 pc += (int32_t)get_u32(pc - 4) - 4;
17296 }
17297 if (unlikely(js_poll_interrupts(ctx)))
17298 goto exception;
17299 }
17300 BREAK;
17301 CASE(OP_if_false):
17302 {
17303 int res;
17304 JSValue op1;
17305
17306 op1 = sp[-1];
17307 pc += 4;
17308 /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */
17309 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17310 res = JS_VALUE_GET_INT(op1);
17311 } else {
17312 res = JS_ToBoolFree(ctx, op1);
17313 }
17314 sp--;
17315 if (!res) {
17316 pc += (int32_t)get_u32(pc - 4) - 4;
17317 }
17318 if (unlikely(js_poll_interrupts(ctx)))
17319 goto exception;
17320 }
17321 BREAK;
17322#if SHORT_OPCODES
17323 CASE(OP_if_true8):
17324 {
17325 int res;
17326 JSValue op1;
17327
17328 op1 = sp[-1];
17329 pc += 1;
17330 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17331 res = JS_VALUE_GET_INT(op1);
17332 } else {
17333 res = JS_ToBoolFree(ctx, op1);
17334 }
17335 sp--;
17336 if (res) {
17337 pc += (int8_t)pc[-1] - 1;
17338 }
17339 if (unlikely(js_poll_interrupts(ctx)))
17340 goto exception;
17341 }
17342 BREAK;
17343 CASE(OP_if_false8):
17344 {
17345 int res;
17346 JSValue op1;
17347
17348 op1 = sp[-1];
17349 pc += 1;
17350 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17351 res = JS_VALUE_GET_INT(op1);
17352 } else {
17353 res = JS_ToBoolFree(ctx, op1);
17354 }
17355 sp--;
17356 if (!res) {
17357 pc += (int8_t)pc[-1] - 1;
17358 }
17359 if (unlikely(js_poll_interrupts(ctx)))
17360 goto exception;
17361 }
17362 BREAK;
17363#endif
17364 CASE(OP_catch):
17365 {
17366 int32_t diff;
17367 diff = get_u32(pc);
17368 sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
17369 sp++;
17370 pc += 4;
17371 }
17372 BREAK;
17373 CASE(OP_gosub):
17374 {
17375 int32_t diff;
17376 diff = get_u32(pc);
17377 /* XXX: should have a different tag to avoid security flaw */
17378 sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
17379 sp++;
17380 pc += diff;
17381 }
17382 BREAK;
17383 CASE(OP_ret):
17384 {
17385 JSValue op1;
17386 uint32_t pos;
17387 op1 = sp[-1];
17388 if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
17389 goto ret_fail;
17390 pos = JS_VALUE_GET_INT(op1);
17391 if (unlikely(pos >= b->byte_code_len)) {
17392 ret_fail:
17393 JS_ThrowInternalError(ctx, "invalid ret value");
17394 goto exception;
17395 }
17396 sp--;
17397 pc = b->byte_code_buf + pos;
17398 }
17399 BREAK;
17400
17401 CASE(OP_for_in_start):
17402 sf->cur_pc = pc;
17403 if (js_for_in_start(ctx, sp))
17404 goto exception;
17405 BREAK;
17406 CASE(OP_for_in_next):
17407 sf->cur_pc = pc;
17408 if (js_for_in_next(ctx, sp))
17409 goto exception;
17410 sp += 2;
17411 BREAK;
17412 CASE(OP_for_of_start):
17413 sf->cur_pc = pc;
17414 if (js_for_of_start(ctx, sp, FALSE))
17415 goto exception;
17416 sp += 1;
17417 *sp++ = JS_NewCatchOffset(ctx, 0);
17418 BREAK;
17419 CASE(OP_for_of_next):
17420 {
17421 int offset = -3 - pc[0];
17422 pc += 1;
17423 sf->cur_pc = pc;
17424 if (js_for_of_next(ctx, sp, offset))
17425 goto exception;
17426 sp += 2;
17427 }
17428 BREAK;
17429 CASE(OP_for_await_of_next):
17430 sf->cur_pc = pc;
17431 if (js_for_await_of_next(ctx, sp))
17432 goto exception;
17433 sp++;
17434 BREAK;
17435 CASE(OP_for_await_of_start):
17436 sf->cur_pc = pc;
17437 if (js_for_of_start(ctx, sp, TRUE))
17438 goto exception;
17439 sp += 1;
17440 *sp++ = JS_NewCatchOffset(ctx, 0);
17441 BREAK;
17442 CASE(OP_iterator_get_value_done):
17443 sf->cur_pc = pc;
17444 if (js_iterator_get_value_done(ctx, sp))
17445 goto exception;
17446 sp += 1;
17447 BREAK;
17448 CASE(OP_iterator_check_object):
17449 if (unlikely(!JS_IsObject(sp[-1]))) {
17450 JS_ThrowTypeError(ctx, "iterator must return an object");
17451 goto exception;
17452 }
17453 BREAK;
17454
17455 CASE(OP_iterator_close):
17456 /* iter_obj next catch_offset -> */
17457 sp--; /* drop the catch offset to avoid getting caught by exception */
17458 JS_FreeValue(ctx, sp[-1]); /* drop the next method */
17459 sp--;
17460 if (!JS_IsUndefined(sp[-1])) {
17461 sf->cur_pc = pc;
17462 if (JS_IteratorClose(ctx, sp[-1], FALSE))
17463 goto exception;
17464 JS_FreeValue(ctx, sp[-1]);
17465 }
17466 sp--;
17467 BREAK;
17468 CASE(OP_nip_catch):
17469 {
17470 JSValue ret_val;
17471 /* catch_offset ... ret_val -> ret_eval */
17472 ret_val = *--sp;
17473 while (sp > stack_buf &&
17474 JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
17475 JS_FreeValue(ctx, *--sp);
17476 }
17477 if (unlikely(sp == stack_buf)) {
17478 JS_ThrowInternalError(ctx, "nip_catch");
17479 JS_FreeValue(ctx, ret_val);
17480 goto exception;
17481 }
17482 sp[-1] = ret_val;
17483 }
17484 BREAK;
17485
17486 CASE(OP_iterator_next):
17487 /* stack: iter_obj next catch_offset val */
17488 {
17489 JSValue ret;
17490 sf->cur_pc = pc;
17491 ret = JS_Call(ctx, sp[-3], sp[-4],
17492 1, (JSValueConst *)(sp - 1));
17493 if (JS_IsException(ret))
17494 goto exception;
17495 JS_FreeValue(ctx, sp[-1]);
17496 sp[-1] = ret;
17497 }
17498 BREAK;
17499
17500 CASE(OP_iterator_call):
17501 /* stack: iter_obj next catch_offset val */
17502 {
17503 JSValue method, ret;
17504 BOOL ret_flag;
17505 int flags;
17506 flags = *pc++;
17507 sf->cur_pc = pc;
17508 method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
17509 JS_ATOM_throw : JS_ATOM_return);
17510 if (JS_IsException(method))
17511 goto exception;
17512 if (JS_IsUndefined(method) || JS_IsNull(method)) {
17513 ret_flag = TRUE;
17514 } else {
17515 if (flags & 2) {
17516 /* no argument */
17517 ret = JS_CallFree(ctx, method, sp[-4],
17518 0, NULL);
17519 } else {
17520 ret = JS_CallFree(ctx, method, sp[-4],
17521 1, (JSValueConst *)(sp - 1));
17522 }
17523 if (JS_IsException(ret))
17524 goto exception;
17525 JS_FreeValue(ctx, sp[-1]);
17526 sp[-1] = ret;
17527 ret_flag = FALSE;
17528 }
17529 sp[0] = JS_NewBool(ctx, ret_flag);
17530 sp += 1;
17531 }
17532 BREAK;
17533
17534 CASE(OP_lnot):
17535 {
17536 int res;
17537 JSValue op1;
17538
17539 op1 = sp[-1];
17540 if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
17541 res = JS_VALUE_GET_INT(op1) != 0;
17542 } else {
17543 res = JS_ToBoolFree(ctx, op1);
17544 }
17545 sp[-1] = JS_NewBool(ctx, !res);
17546 }
17547 BREAK;
17548
17549 CASE(OP_get_field):
17550 {
17551 JSValue val;
17552 JSAtom atom;
17553 atom = get_u32(pc);
17554 pc += 4;
17555
17556 sf->cur_pc = pc;
17557 val = JS_GetProperty(ctx, sp[-1], atom);
17558 if (unlikely(JS_IsException(val)))
17559 goto exception;
17560 JS_FreeValue(ctx, sp[-1]);
17561 sp[-1] = val;
17562 }
17563 BREAK;
17564
17565 CASE(OP_get_field2):
17566 {
17567 JSValue val;
17568 JSAtom atom;
17569 atom = get_u32(pc);
17570 pc += 4;
17571
17572 sf->cur_pc = pc;
17573 val = JS_GetProperty(ctx, sp[-1], atom);
17574 if (unlikely(JS_IsException(val)))
17575 goto exception;
17576 *sp++ = val;
17577 }
17578 BREAK;
17579
17580 CASE(OP_put_field):
17581 {
17582 int ret;
17583 JSAtom atom;
17584 atom = get_u32(pc);
17585 pc += 4;
17586 sf->cur_pc = pc;
17587
17588 ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2],
17589 JS_PROP_THROW_STRICT);
17590 JS_FreeValue(ctx, sp[-2]);
17591 sp -= 2;
17592 if (unlikely(ret < 0))
17593 goto exception;
17594 }
17595 BREAK;
17596
17597 CASE(OP_private_symbol):
17598 {
17599 JSAtom atom;
17600 JSValue val;
17601
17602 atom = get_u32(pc);
17603 pc += 4;
17604 val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
17605 if (JS_IsException(val))
17606 goto exception;
17607 *sp++ = val;
17608 }
17609 BREAK;
17610
17611 CASE(OP_get_private_field):
17612 {
17613 JSValue val;
17614
17615 val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
17616 JS_FreeValue(ctx, sp[-1]);
17617 JS_FreeValue(ctx, sp[-2]);
17618 sp[-2] = val;
17619 sp--;
17620 if (unlikely(JS_IsException(val)))
17621 goto exception;
17622 }
17623 BREAK;
17624
17625 CASE(OP_put_private_field):
17626 {
17627 int ret;
17628 ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
17629 JS_FreeValue(ctx, sp[-3]);
17630 JS_FreeValue(ctx, sp[-1]);
17631 sp -= 3;
17632 if (unlikely(ret < 0))
17633 goto exception;
17634 }
17635 BREAK;
17636
17637 CASE(OP_define_private_field):
17638 {
17639 int ret;
17640 ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
17641 JS_FreeValue(ctx, sp[-2]);
17642 sp -= 2;
17643 if (unlikely(ret < 0))
17644 goto exception;
17645 }
17646 BREAK;
17647
17648 CASE(OP_define_field):
17649 {
17650 int ret;
17651 JSAtom atom;
17652 atom = get_u32(pc);
17653 pc += 4;
17654
17655 ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
17656 JS_PROP_C_W_E | JS_PROP_THROW);
17657 sp--;
17658 if (unlikely(ret < 0))
17659 goto exception;
17660 }
17661 BREAK;
17662
17663 CASE(OP_set_name):
17664 {
17665 int ret;
17666 JSAtom atom;
17667 atom = get_u32(pc);
17668 pc += 4;
17669
17670 ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
17671 if (unlikely(ret < 0))
17672 goto exception;
17673 }
17674 BREAK;
17675 CASE(OP_set_name_computed):
17676 {
17677 int ret;
17678 ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
17679 if (unlikely(ret < 0))
17680 goto exception;
17681 }
17682 BREAK;
17683 CASE(OP_set_proto):
17684 {
17685 JSValue proto;
17686 sf->cur_pc = pc;
17687 proto = sp[-1];
17688 if (JS_IsObject(proto) || JS_IsNull(proto)) {
17689 if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
17690 goto exception;
17691 }
17692 JS_FreeValue(ctx, proto);
17693 sp--;
17694 }
17695 BREAK;
17696 CASE(OP_set_home_object):
17697 js_method_set_home_object(ctx, sp[-1], sp[-2]);
17698 BREAK;
17699 CASE(OP_define_method):
17700 CASE(OP_define_method_computed):
17701 {
17702 JSValue getter, setter, value;
17703 JSValueConst obj;
17704 JSAtom atom;
17705 int flags, ret, op_flags;
17706 BOOL is_computed;
17707#define OP_DEFINE_METHOD_METHOD 0
17708#define OP_DEFINE_METHOD_GETTER 1
17709#define OP_DEFINE_METHOD_SETTER 2
17710#define OP_DEFINE_METHOD_ENUMERABLE 4
17711
17712 is_computed = (opcode == OP_define_method_computed);
17713 if (is_computed) {
17714 atom = JS_ValueToAtom(ctx, sp[-2]);
17715 if (unlikely(atom == JS_ATOM_NULL))
17716 goto exception;
17717 opcode += OP_define_method - OP_define_method_computed;
17718 } else {
17719 atom = get_u32(pc);
17720 pc += 4;
17721 }
17722 op_flags = *pc++;
17723
17724 obj = sp[-2 - is_computed];
17725 flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
17726 JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
17727 if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
17728 flags |= JS_PROP_ENUMERABLE;
17729 op_flags &= 3;
17730 value = JS_UNDEFINED;
17731 getter = JS_UNDEFINED;
17732 setter = JS_UNDEFINED;
17733 if (op_flags == OP_DEFINE_METHOD_METHOD) {
17734 value = sp[-1];
17735 flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
17736 } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
17737 getter = sp[-1];
17738 flags |= JS_PROP_HAS_GET;
17739 } else {
17740 setter = sp[-1];
17741 flags |= JS_PROP_HAS_SET;
17742 }
17743 ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
17744 if (ret >= 0) {
17745 ret = JS_DefineProperty(ctx, obj, atom, value,
17746 getter, setter, flags);
17747 }
17748 JS_FreeValue(ctx, sp[-1]);
17749 if (is_computed) {
17750 JS_FreeAtom(ctx, atom);
17751 JS_FreeValue(ctx, sp[-2]);
17752 }
17753 sp -= 1 + is_computed;
17754 if (unlikely(ret < 0))
17755 goto exception;
17756 }
17757 BREAK;
17758
17759 CASE(OP_define_class):
17760 CASE(OP_define_class_computed):
17761 {
17762 int class_flags;
17763 JSAtom atom;
17764
17765 atom = get_u32(pc);
17766 class_flags = pc[4];
17767 pc += 5;
17768 if (js_op_define_class(ctx, sp, atom, class_flags,
17769 var_refs, sf,
17770 (opcode == OP_define_class_computed)) < 0)
17771 goto exception;
17772 }
17773 BREAK;
17774
17775 CASE(OP_get_array_el):
17776 {
17777 JSValue val;
17778
17779 sf->cur_pc = pc;
17780 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17781 JS_FreeValue(ctx, sp[-2]);
17782 sp[-2] = val;
17783 sp--;
17784 if (unlikely(JS_IsException(val)))
17785 goto exception;
17786 }
17787 BREAK;
17788
17789 CASE(OP_get_array_el2):
17790 {
17791 JSValue val;
17792
17793 sf->cur_pc = pc;
17794 val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
17795 sp[-1] = val;
17796 if (unlikely(JS_IsException(val)))
17797 goto exception;
17798 }
17799 BREAK;
17800
17801 CASE(OP_get_ref_value):
17802 {
17803 JSValue val;
17804 JSAtom atom;
17805 int ret;
17806
17807 sf->cur_pc = pc;
17808 atom = JS_ValueToAtom(ctx, sp[-1]);
17809 if (atom == JS_ATOM_NULL)
17810 goto exception;
17811 if (unlikely(JS_IsUndefined(sp[-2]))) {
17812 JS_ThrowReferenceErrorNotDefined(ctx, atom);
17813 JS_FreeAtom(ctx, atom);
17814 goto exception;
17815 }
17816 ret = JS_HasProperty(ctx, sp[-2], atom);
17817 if (unlikely(ret <= 0)) {
17818 if (ret < 0) {
17819 JS_FreeAtom(ctx, atom);
17820 goto exception;
17821 }
17822 if (is_strict_mode(ctx)) {
17823 JS_ThrowReferenceErrorNotDefined(ctx, atom);
17824 JS_FreeAtom(ctx, atom);
17825 goto exception;
17826 }
17827 val = JS_UNDEFINED;
17828 } else {
17829 val = JS_GetProperty(ctx, sp[-2], atom);
17830 }
17831 JS_FreeAtom(ctx, atom);
17832 if (unlikely(JS_IsException(val)))
17833 goto exception;
17834 sp[0] = val;
17835 sp++;
17836 }
17837 BREAK;
17838
17839 CASE(OP_get_super_value):
17840 {
17841 JSValue val;
17842 JSAtom atom;
17843 sf->cur_pc = pc;
17844 atom = JS_ValueToAtom(ctx, sp[-1]);
17845 if (unlikely(atom == JS_ATOM_NULL))
17846 goto exception;
17847 val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
17848 JS_FreeAtom(ctx, atom);
17849 if (unlikely(JS_IsException(val)))
17850 goto exception;
17851 JS_FreeValue(ctx, sp[-1]);
17852 JS_FreeValue(ctx, sp[-2]);
17853 JS_FreeValue(ctx, sp[-3]);
17854 sp[-3] = val;
17855 sp -= 2;
17856 }
17857 BREAK;
17858
17859 CASE(OP_put_array_el):
17860 {
17861 int ret;
17862
17863 sf->cur_pc = pc;
17864 ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
17865 JS_FreeValue(ctx, sp[-3]);
17866 sp -= 3;
17867 if (unlikely(ret < 0))
17868 goto exception;
17869 }
17870 BREAK;
17871
17872 CASE(OP_put_ref_value):
17873 {
17874 int ret;
17875 JSAtom atom;
17876 sf->cur_pc = pc;
17877 atom = JS_ValueToAtom(ctx, sp[-2]);
17878 if (unlikely(atom == JS_ATOM_NULL))
17879 goto exception;
17880 if (unlikely(JS_IsUndefined(sp[-3]))) {
17881 if (is_strict_mode(ctx)) {
17882 JS_ThrowReferenceErrorNotDefined(ctx, atom);
17883 JS_FreeAtom(ctx, atom);
17884 goto exception;
17885 } else {
17886 sp[-3] = JS_DupValue(ctx, ctx->global_obj);
17887 }
17888 }
17889 ret = JS_HasProperty(ctx, sp[-3], atom);
17890 if (unlikely(ret <= 0)) {
17891 if (unlikely(ret < 0)) {
17892 JS_FreeAtom(ctx, atom);
17893 goto exception;
17894 }
17895 if (is_strict_mode(ctx)) {
17896 JS_ThrowReferenceErrorNotDefined(ctx, atom);
17897 JS_FreeAtom(ctx, atom);
17898 goto exception;
17899 }
17900 }
17901 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-3], JS_PROP_THROW_STRICT);
17902 JS_FreeAtom(ctx, atom);
17903 JS_FreeValue(ctx, sp[-2]);
17904 JS_FreeValue(ctx, sp[-3]);
17905 sp -= 3;
17906 if (unlikely(ret < 0))
17907 goto exception;
17908 }
17909 BREAK;
17910
17911 CASE(OP_put_super_value):
17912 {
17913 int ret;
17914 JSAtom atom;
17915 sf->cur_pc = pc;
17916 if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
17917 JS_ThrowTypeErrorNotAnObject(ctx);
17918 goto exception;
17919 }
17920 atom = JS_ValueToAtom(ctx, sp[-2]);
17921 if (unlikely(atom == JS_ATOM_NULL))
17922 goto exception;
17923 ret = JS_SetPropertyInternal(ctx, sp[-3], atom, sp[-1], sp[-4],
17924 JS_PROP_THROW_STRICT);
17925 JS_FreeAtom(ctx, atom);
17926 JS_FreeValue(ctx, sp[-4]);
17927 JS_FreeValue(ctx, sp[-3]);
17928 JS_FreeValue(ctx, sp[-2]);
17929 sp -= 4;
17930 if (ret < 0)
17931 goto exception;
17932 }
17933 BREAK;
17934
17935 CASE(OP_define_array_el):
17936 {
17937 int ret;
17938 ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
17939 JS_PROP_C_W_E | JS_PROP_THROW);
17940 sp -= 1;
17941 if (unlikely(ret < 0))
17942 goto exception;
17943 }
17944 BREAK;
17945
17946 CASE(OP_append): /* array pos enumobj -- array pos */
17947 {
17948 sf->cur_pc = pc;
17949 if (js_append_enumerate(ctx, sp))
17950 goto exception;
17951 JS_FreeValue(ctx, *--sp);
17952 }
17953 BREAK;
17954
17955 CASE(OP_copy_data_properties): /* target source excludeList */
17956 {
17957 /* stack offsets (-1 based):
17958 2 bits for target,
17959 3 bits for source,
17960 2 bits for exclusionList */
17961 int mask;
17962
17963 mask = *pc++;
17964 sf->cur_pc = pc;
17965 if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
17966 sp[-1 - ((mask >> 2) & 7)],
17967 sp[-1 - ((mask >> 5) & 7)], 0))
17968 goto exception;
17969 }
17970 BREAK;
17971
17972 CASE(OP_add):
17973 {
17974 JSValue op1, op2;
17975 op1 = sp[-2];
17976 op2 = sp[-1];
17977 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
17978 int64_t r;
17979 r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
17980 if (unlikely((int)r != r))
17981 goto add_slow;
17982 sp[-2] = JS_NewInt32(ctx, r);
17983 sp--;
17984 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
17985 sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
17986 JS_VALUE_GET_FLOAT64(op2));
17987 sp--;
17988 } else if (JS_IsString(op1) && JS_IsString(op2)) {
17989 sp[-2] = JS_ConcatString(ctx, op1, op2);
17990 sp--;
17991 if (JS_IsException(sp[-1]))
17992 goto exception;
17993 } else {
17994 add_slow:
17995 sf->cur_pc = pc;
17996 if (js_add_slow(ctx, sp))
17997 goto exception;
17998 sp--;
17999 }
18000 }
18001 BREAK;
18002 CASE(OP_add_loc):
18003 {
18004 JSValue op2;
18005 JSValue *pv;
18006 int idx;
18007 idx = *pc;
18008 pc += 1;
18009
18010 op2 = sp[-1];
18011 pv = &var_buf[idx];
18012 if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) {
18013 int64_t r;
18014 r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2);
18015 if (unlikely((int)r != r))
18016 goto add_loc_slow;
18017 *pv = JS_NewInt32(ctx, r);
18018 sp--;
18019 } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) {
18020 *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) +
18021 JS_VALUE_GET_FLOAT64(op2));
18022 sp--;
18023 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
18024 sp--;
18025 sf->cur_pc = pc;
18026 op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
18027 if (JS_IsException(op2))
18028 goto exception;
18029 if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) {
18030 JS_FreeValue(ctx, op2);
18031 } else {
18032 op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2);
18033 if (JS_IsException(op2))
18034 goto exception;
18035 set_value(ctx, pv, op2);
18036 }
18037 } else {
18038 JSValue ops[2];
18039 add_loc_slow:
18040 /* In case of exception, js_add_slow frees ops[0]
18041 and ops[1], so we must duplicate *pv */
18042 sf->cur_pc = pc;
18043 ops[0] = JS_DupValue(ctx, *pv);
18044 ops[1] = op2;
18045 sp--;
18046 if (js_add_slow(ctx, ops + 2))
18047 goto exception;
18048 set_value(ctx, pv, ops[0]);
18049 }
18050 }
18051 BREAK;
18052 CASE(OP_sub):
18053 {
18054 JSValue op1, op2;
18055 op1 = sp[-2];
18056 op2 = sp[-1];
18057 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18058 int64_t r;
18059 r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
18060 if (unlikely((int)r != r))
18061 goto binary_arith_slow;
18062 sp[-2] = JS_NewInt32(ctx, r);
18063 sp--;
18064 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
18065 sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
18066 JS_VALUE_GET_FLOAT64(op2));
18067 sp--;
18068 } else {
18069 goto binary_arith_slow;
18070 }
18071 }
18072 BREAK;
18073 CASE(OP_mul):
18074 {
18075 JSValue op1, op2;
18076 double d;
18077 op1 = sp[-2];
18078 op2 = sp[-1];
18079 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18080 int32_t v1, v2;
18081 int64_t r;
18082 v1 = JS_VALUE_GET_INT(op1);
18083 v2 = JS_VALUE_GET_INT(op2);
18084 r = (int64_t)v1 * v2;
18085 if (unlikely((int)r != r)) {
18086 d = (double)r;
18087 goto mul_fp_res;
18088 }
18089 /* need to test zero case for -0 result */
18090 if (unlikely(r == 0 && (v1 | v2) < 0)) {
18091 d = -0.0;
18092 goto mul_fp_res;
18093 }
18094 sp[-2] = JS_NewInt32(ctx, r);
18095 sp--;
18096 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
18097 d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
18098 mul_fp_res:
18099 sp[-2] = __JS_NewFloat64(ctx, d);
18100 sp--;
18101 } else {
18102 goto binary_arith_slow;
18103 }
18104 }
18105 BREAK;
18106 CASE(OP_div):
18107 {
18108 JSValue op1, op2;
18109 op1 = sp[-2];
18110 op2 = sp[-1];
18111 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18112 int v1, v2;
18113 v1 = JS_VALUE_GET_INT(op1);
18114 v2 = JS_VALUE_GET_INT(op2);
18115 sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
18116 sp--;
18117 } else {
18118 goto binary_arith_slow;
18119 }
18120 }
18121 BREAK;
18122 CASE(OP_mod):
18123 {
18124 JSValue op1, op2;
18125 op1 = sp[-2];
18126 op2 = sp[-1];
18127 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18128 int v1, v2, r;
18129 v1 = JS_VALUE_GET_INT(op1);
18130 v2 = JS_VALUE_GET_INT(op2);
18131 /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
18132 -1 and the cases where the result is -0. */
18133 if (unlikely(v1 < 0 || v2 <= 0))
18134 goto binary_arith_slow;
18135 r = v1 % v2;
18136 sp[-2] = JS_NewInt32(ctx, r);
18137 sp--;
18138 } else {
18139 goto binary_arith_slow;
18140 }
18141 }
18142 BREAK;
18143 CASE(OP_pow):
18144 binary_arith_slow:
18145 sf->cur_pc = pc;
18146 if (js_binary_arith_slow(ctx, sp, opcode))
18147 goto exception;
18148 sp--;
18149 BREAK;
18150
18151 CASE(OP_plus):
18152 {
18153 JSValue op1;
18154 uint32_t tag;
18155 op1 = sp[-1];
18156 tag = JS_VALUE_GET_TAG(op1);
18157 if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
18158 } else {
18159 sf->cur_pc = pc;
18160 if (js_unary_arith_slow(ctx, sp, opcode))
18161 goto exception;
18162 }
18163 }
18164 BREAK;
18165 CASE(OP_neg):
18166 {
18167 JSValue op1;
18168 uint32_t tag;
18169 int val;
18170 double d;
18171 op1 = sp[-1];
18172 tag = JS_VALUE_GET_TAG(op1);
18173 if (tag == JS_TAG_INT) {
18174 val = JS_VALUE_GET_INT(op1);
18175 /* Note: -0 cannot be expressed as integer */
18176 if (unlikely(val == 0)) {
18177 d = -0.0;
18178 goto neg_fp_res;
18179 }
18180 if (unlikely(val == INT32_MIN)) {
18181 d = -(double)val;
18182 goto neg_fp_res;
18183 }
18184 sp[-1] = JS_NewInt32(ctx, -val);
18185 } else if (JS_TAG_IS_FLOAT64(tag)) {
18186 d = -JS_VALUE_GET_FLOAT64(op1);
18187 neg_fp_res:
18188 sp[-1] = __JS_NewFloat64(ctx, d);
18189 } else {
18190 sf->cur_pc = pc;
18191 if (js_unary_arith_slow(ctx, sp, opcode))
18192 goto exception;
18193 }
18194 }
18195 BREAK;
18196 CASE(OP_inc):
18197 {
18198 JSValue op1;
18199 int val;
18200 op1 = sp[-1];
18201 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18202 val = JS_VALUE_GET_INT(op1);
18203 if (unlikely(val == INT32_MAX))
18204 goto inc_slow;
18205 sp[-1] = JS_NewInt32(ctx, val + 1);
18206 } else {
18207 inc_slow:
18208 sf->cur_pc = pc;
18209 if (js_unary_arith_slow(ctx, sp, opcode))
18210 goto exception;
18211 }
18212 }
18213 BREAK;
18214 CASE(OP_dec):
18215 {
18216 JSValue op1;
18217 int val;
18218 op1 = sp[-1];
18219 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18220 val = JS_VALUE_GET_INT(op1);
18221 if (unlikely(val == INT32_MIN))
18222 goto dec_slow;
18223 sp[-1] = JS_NewInt32(ctx, val - 1);
18224 } else {
18225 dec_slow:
18226 sf->cur_pc = pc;
18227 if (js_unary_arith_slow(ctx, sp, opcode))
18228 goto exception;
18229 }
18230 }
18231 BREAK;
18232 CASE(OP_post_inc):
18233 CASE(OP_post_dec):
18234 sf->cur_pc = pc;
18235 if (js_post_inc_slow(ctx, sp, opcode))
18236 goto exception;
18237 sp++;
18238 BREAK;
18239 CASE(OP_inc_loc):
18240 {
18241 JSValue op1;
18242 int val;
18243 int idx;
18244 idx = *pc;
18245 pc += 1;
18246
18247 op1 = var_buf[idx];
18248 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18249 val = JS_VALUE_GET_INT(op1);
18250 if (unlikely(val == INT32_MAX))
18251 goto inc_loc_slow;
18252 var_buf[idx] = JS_NewInt32(ctx, val + 1);
18253 } else {
18254 inc_loc_slow:
18255 sf->cur_pc = pc;
18256 /* must duplicate otherwise the variable value may
18257 be destroyed before JS code accesses it */
18258 op1 = JS_DupValue(ctx, op1);
18259 if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
18260 goto exception;
18261 set_value(ctx, &var_buf[idx], op1);
18262 }
18263 }
18264 BREAK;
18265 CASE(OP_dec_loc):
18266 {
18267 JSValue op1;
18268 int val;
18269 int idx;
18270 idx = *pc;
18271 pc += 1;
18272
18273 op1 = var_buf[idx];
18274 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18275 val = JS_VALUE_GET_INT(op1);
18276 if (unlikely(val == INT32_MIN))
18277 goto dec_loc_slow;
18278 var_buf[idx] = JS_NewInt32(ctx, val - 1);
18279 } else {
18280 dec_loc_slow:
18281 sf->cur_pc = pc;
18282 /* must duplicate otherwise the variable value may
18283 be destroyed before JS code accesses it */
18284 op1 = JS_DupValue(ctx, op1);
18285 if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
18286 goto exception;
18287 set_value(ctx, &var_buf[idx], op1);
18288 }
18289 }
18290 BREAK;
18291 CASE(OP_not):
18292 {
18293 JSValue op1;
18294 op1 = sp[-1];
18295 if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
18296 sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
18297 } else {
18298 sf->cur_pc = pc;
18299 if (js_not_slow(ctx, sp))
18300 goto exception;
18301 }
18302 }
18303 BREAK;
18304
18305 CASE(OP_shl):
18306 {
18307 JSValue op1, op2;
18308 op1 = sp[-2];
18309 op2 = sp[-1];
18310 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18311 uint32_t v1, v2;
18312 v1 = JS_VALUE_GET_INT(op1);
18313 v2 = JS_VALUE_GET_INT(op2);
18314 v2 &= 0x1f;
18315 sp[-2] = JS_NewInt32(ctx, v1 << v2);
18316 sp--;
18317 } else {
18318 sf->cur_pc = pc;
18319 if (js_binary_logic_slow(ctx, sp, opcode))
18320 goto exception;
18321 sp--;
18322 }
18323 }
18324 BREAK;
18325 CASE(OP_shr):
18326 {
18327 JSValue op1, op2;
18328 op1 = sp[-2];
18329 op2 = sp[-1];
18330 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18331 uint32_t v2;
18332 v2 = JS_VALUE_GET_INT(op2);
18333 v2 &= 0x1f;
18334 sp[-2] = JS_NewUint32(ctx,
18335 (uint32_t)JS_VALUE_GET_INT(op1) >>
18336 v2);
18337 sp--;
18338 } else {
18339 sf->cur_pc = pc;
18340 if (js_shr_slow(ctx, sp))
18341 goto exception;
18342 sp--;
18343 }
18344 }
18345 BREAK;
18346 CASE(OP_sar):
18347 {
18348 JSValue op1, op2;
18349 op1 = sp[-2];
18350 op2 = sp[-1];
18351 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18352 uint32_t v2;
18353 v2 = JS_VALUE_GET_INT(op2);
18354 v2 &= 0x1f;
18355 sp[-2] = JS_NewInt32(ctx,
18356 (int)JS_VALUE_GET_INT(op1) >> v2);
18357 sp--;
18358 } else {
18359 sf->cur_pc = pc;
18360 if (js_binary_logic_slow(ctx, sp, opcode))
18361 goto exception;
18362 sp--;
18363 }
18364 }
18365 BREAK;
18366 CASE(OP_and):
18367 {
18368 JSValue op1, op2;
18369 op1 = sp[-2];
18370 op2 = sp[-1];
18371 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18372 sp[-2] = JS_NewInt32(ctx,
18373 JS_VALUE_GET_INT(op1) &
18374 JS_VALUE_GET_INT(op2));
18375 sp--;
18376 } else {
18377 sf->cur_pc = pc;
18378 if (js_binary_logic_slow(ctx, sp, opcode))
18379 goto exception;
18380 sp--;
18381 }
18382 }
18383 BREAK;
18384 CASE(OP_or):
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_NewInt32(ctx,
18391 JS_VALUE_GET_INT(op1) |
18392 JS_VALUE_GET_INT(op2));
18393 sp--;
18394 } else {
18395 sf->cur_pc = pc;
18396 if (js_binary_logic_slow(ctx, sp, opcode))
18397 goto exception;
18398 sp--;
18399 }
18400 }
18401 BREAK;
18402 CASE(OP_xor):
18403 {
18404 JSValue op1, op2;
18405 op1 = sp[-2];
18406 op2 = sp[-1];
18407 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
18408 sp[-2] = JS_NewInt32(ctx,
18409 JS_VALUE_GET_INT(op1) ^
18410 JS_VALUE_GET_INT(op2));
18411 sp--;
18412 } else {
18413 sf->cur_pc = pc;
18414 if (js_binary_logic_slow(ctx, sp, opcode))
18415 goto exception;
18416 sp--;
18417 }
18418 }
18419 BREAK;
18420
18421
18422#define OP_CMP(opcode, binary_op, slow_call) \
18423 CASE(opcode): \
18424 { \
18425 JSValue op1, op2; \
18426 op1 = sp[-2]; \
18427 op2 = sp[-1]; \
18428 if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \
18429 sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
18430 sp--; \
18431 } else { \
18432 sf->cur_pc = pc; \
18433 if (slow_call) \
18434 goto exception; \
18435 sp--; \
18436 } \
18437 } \
18438 BREAK
18439
18440 OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
18441 OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
18442 OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
18443 OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
18444 OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
18445 OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
18446 OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
18447 OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
18448
18449 CASE(OP_in):
18450 sf->cur_pc = pc;
18451 if (js_operator_in(ctx, sp))
18452 goto exception;
18453 sp--;
18454 BREAK;
18455 CASE(OP_private_in):
18456 sf->cur_pc = pc;
18457 if (js_operator_private_in(ctx, sp))
18458 goto exception;
18459 sp--;
18460 BREAK;
18461 CASE(OP_instanceof):
18462 sf->cur_pc = pc;
18463 if (js_operator_instanceof(ctx, sp))
18464 goto exception;
18465 sp--;
18466 BREAK;
18467 CASE(OP_typeof):
18468 {
18469 JSValue op1;
18470 JSAtom atom;
18471
18472 op1 = sp[-1];
18473 atom = js_operator_typeof(ctx, op1);
18474 JS_FreeValue(ctx, op1);
18475 sp[-1] = JS_AtomToString(ctx, atom);
18476 }
18477 BREAK;
18478 CASE(OP_delete):
18479 sf->cur_pc = pc;
18480 if (js_operator_delete(ctx, sp))
18481 goto exception;
18482 sp--;
18483 BREAK;
18484 CASE(OP_delete_var):
18485 {
18486 JSAtom atom;
18487 int ret;
18488
18489 atom = get_u32(pc);
18490 pc += 4;
18491 sf->cur_pc = pc;
18492
18493 ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
18494 if (unlikely(ret < 0))
18495 goto exception;
18496 *sp++ = JS_NewBool(ctx, ret);
18497 }
18498 BREAK;
18499
18500 CASE(OP_to_object):
18501 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
18502 sf->cur_pc = pc;
18503 ret_val = JS_ToObject(ctx, sp[-1]);
18504 if (JS_IsException(ret_val))
18505 goto exception;
18506 JS_FreeValue(ctx, sp[-1]);
18507 sp[-1] = ret_val;
18508 }
18509 BREAK;
18510
18511 CASE(OP_to_propkey):
18512 switch (JS_VALUE_GET_TAG(sp[-1])) {
18513 case JS_TAG_INT:
18514 case JS_TAG_STRING:
18515 case JS_TAG_SYMBOL:
18516 break;
18517 default:
18518 sf->cur_pc = pc;
18519 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18520 if (JS_IsException(ret_val))
18521 goto exception;
18522 JS_FreeValue(ctx, sp[-1]);
18523 sp[-1] = ret_val;
18524 break;
18525 }
18526 BREAK;
18527
18528 CASE(OP_to_propkey2):
18529 /* must be tested first */
18530 if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
18531 JS_ThrowTypeError(ctx, "value has no property");
18532 goto exception;
18533 }
18534 switch (JS_VALUE_GET_TAG(sp[-1])) {
18535 case JS_TAG_INT:
18536 case JS_TAG_STRING:
18537 case JS_TAG_SYMBOL:
18538 break;
18539 default:
18540 sf->cur_pc = pc;
18541 ret_val = JS_ToPropertyKey(ctx, sp[-1]);
18542 if (JS_IsException(ret_val))
18543 goto exception;
18544 JS_FreeValue(ctx, sp[-1]);
18545 sp[-1] = ret_val;
18546 break;
18547 }
18548 BREAK;
18549#if 0
18550 CASE(OP_to_string):
18551 if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
18552 ret_val = JS_ToString(ctx, sp[-1]);
18553 if (JS_IsException(ret_val))
18554 goto exception;
18555 JS_FreeValue(ctx, sp[-1]);
18556 sp[-1] = ret_val;
18557 }
18558 BREAK;
18559#endif
18560 CASE(OP_with_get_var):
18561 CASE(OP_with_put_var):
18562 CASE(OP_with_delete_var):
18563 CASE(OP_with_make_ref):
18564 CASE(OP_with_get_ref):
18565 {
18566 JSAtom atom;
18567 int32_t diff;
18568 JSValue obj, val;
18569 int ret, is_with;
18570 atom = get_u32(pc);
18571 diff = get_u32(pc + 4);
18572 is_with = pc[8];
18573 pc += 9;
18574 sf->cur_pc = pc;
18575
18576 obj = sp[-1];
18577 ret = JS_HasProperty(ctx, obj, atom);
18578 if (unlikely(ret < 0))
18579 goto exception;
18580 if (ret) {
18581 if (is_with) {
18582 ret = js_has_unscopable(ctx, obj, atom);
18583 if (unlikely(ret < 0))
18584 goto exception;
18585 if (ret)
18586 goto no_with;
18587 }
18588 switch (opcode) {
18589 case OP_with_get_var:
18590 /* in Object Environment Records, GetBindingValue() calls HasProperty() */
18591 ret = JS_HasProperty(ctx, obj, atom);
18592 if (unlikely(ret <= 0)) {
18593 if (ret < 0)
18594 goto exception;
18595 if (is_strict_mode(ctx)) {
18596 JS_ThrowReferenceErrorNotDefined(ctx, atom);
18597 goto exception;
18598 }
18599 val = JS_UNDEFINED;
18600 } else {
18601 val = JS_GetProperty(ctx, obj, atom);
18602 if (unlikely(JS_IsException(val)))
18603 goto exception;
18604 }
18605 set_value(ctx, &sp[-1], val);
18606 break;
18607 case OP_with_put_var: /* used e.g. in for in/of */
18608 /* in Object Environment Records, SetMutableBinding() calls HasProperty() */
18609 ret = JS_HasProperty(ctx, obj, atom);
18610 if (unlikely(ret <= 0)) {
18611 if (ret < 0)
18612 goto exception;
18613 if (is_strict_mode(ctx)) {
18614 JS_ThrowReferenceErrorNotDefined(ctx, atom);
18615 goto exception;
18616 }
18617 }
18618 ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj,
18619 JS_PROP_THROW_STRICT);
18620 JS_FreeValue(ctx, sp[-1]);
18621 sp -= 2;
18622 if (unlikely(ret < 0))
18623 goto exception;
18624 break;
18625 case OP_with_delete_var:
18626 ret = JS_DeleteProperty(ctx, obj, atom, 0);
18627 if (unlikely(ret < 0))
18628 goto exception;
18629 JS_FreeValue(ctx, sp[-1]);
18630 sp[-1] = JS_NewBool(ctx, ret);
18631 break;
18632 case OP_with_make_ref:
18633 /* produce a pair object/propname on the stack */
18634 *sp++ = JS_AtomToValue(ctx, atom);
18635 break;
18636 case OP_with_get_ref:
18637 /* produce a pair object/method on the stack */
18638 /* in Object Environment Records, GetBindingValue() calls HasProperty() */
18639 ret = JS_HasProperty(ctx, obj, atom);
18640 if (unlikely(ret < 0))
18641 goto exception;
18642 if (!ret) {
18643 val = JS_UNDEFINED;
18644 } else {
18645 val = JS_GetProperty(ctx, obj, atom);
18646 if (unlikely(JS_IsException(val)))
18647 goto exception;
18648 }
18649 *sp++ = val;
18650 break;
18651 }
18652 pc += diff - 5;
18653 } else {
18654 no_with:
18655 /* if not jumping, drop the object argument */
18656 JS_FreeValue(ctx, sp[-1]);
18657 sp--;
18658 }
18659 }
18660 BREAK;
18661
18662 CASE(OP_await):
18663 ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
18664 goto done_generator;
18665 CASE(OP_yield):
18666 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
18667 goto done_generator;
18668 CASE(OP_yield_star):
18669 CASE(OP_async_yield_star):
18670 ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
18671 goto done_generator;
18672 CASE(OP_return_async):
18673 ret_val = JS_UNDEFINED;
18674 goto done_generator;
18675 CASE(OP_initial_yield):
18676 ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD);
18677 goto done_generator;
18678
18679 CASE(OP_nop):
18680 BREAK;
18681 CASE(OP_is_undefined_or_null):
18682 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
18683 JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18684 goto set_true;
18685 } else {
18686 goto free_and_set_false;
18687 }
18688#if SHORT_OPCODES
18689 CASE(OP_is_undefined):
18690 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
18691 goto set_true;
18692 } else {
18693 goto free_and_set_false;
18694 }
18695 CASE(OP_is_null):
18696 if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
18697 goto set_true;
18698 } else {
18699 goto free_and_set_false;
18700 }
18701 /* XXX: could merge to a single opcode */
18702 CASE(OP_typeof_is_undefined):
18703 /* different from OP_is_undefined because of isHTMLDDA */
18704 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
18705 goto free_and_set_true;
18706 } else {
18707 goto free_and_set_false;
18708 }
18709 CASE(OP_typeof_is_function):
18710 if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
18711 goto free_and_set_true;
18712 } else {
18713 goto free_and_set_false;
18714 }
18715 free_and_set_true:
18716 JS_FreeValue(ctx, sp[-1]);
18717#endif
18718 set_true:
18719 sp[-1] = JS_TRUE;
18720 BREAK;
18721 free_and_set_false:
18722 JS_FreeValue(ctx, sp[-1]);
18723 sp[-1] = JS_FALSE;
18724 BREAK;
18725 CASE(OP_invalid):
18726 DEFAULT:
18727 JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
18728 (int)(pc - b->byte_code_buf - 1), opcode);
18729 goto exception;
18730 }
18731 }
18732 exception:
18733 if (is_backtrace_needed(ctx, rt->current_exception)) {
18734 /* add the backtrace information now (it is not done
18735 before if the exception happens in a bytecode
18736 operation */
18737 sf->cur_pc = pc;
18738 build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0);
18739 }
18740 if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
18741 while (sp > stack_buf) {
18742 JSValue val = *--sp;
18743 JS_FreeValue(ctx, val);
18744 if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
18745 int pos = JS_VALUE_GET_INT(val);
18746 if (pos == 0) {
18747 /* enumerator: close it with a throw */
18748 JS_FreeValue(ctx, sp[-1]); /* drop the next method */
18749 sp--;
18750 JS_IteratorClose(ctx, sp[-1], TRUE);
18751 } else {
18752 *sp++ = rt->current_exception;
18753 rt->current_exception = JS_UNINITIALIZED;
18754 pc = b->byte_code_buf + pos;
18755 goto restart;
18756 }
18757 }
18758 }
18759 }
18760 ret_val = JS_EXCEPTION;
18761 /* the local variables are freed by the caller in the generator
18762 case. Hence the label 'done' should never be reached in a
18763 generator function. */
18764 if (b->func_kind != JS_FUNC_NORMAL) {
18765 done_generator:
18766 sf->cur_pc = pc;
18767 sf->cur_sp = sp;
18768 } else {
18769 done:
18770 if (unlikely(!list_empty(&sf->var_ref_list))) {
18771 /* variable references reference the stack: must close them */
18772 close_var_refs(rt, sf);
18773 }
18774 /* free the local variables and stack */
18775 for(pval = local_buf; pval < sp; pval++) {
18776 JS_FreeValue(ctx, *pval);
18777 }
18778 }
18779 rt->current_stack_frame = sf->prev_frame;
18780 return ret_val;
18781}
18782
18783JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
18784 int argc, JSValueConst *argv)
18785{
18786 return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18787 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18788}
18789
18790static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
18791 int argc, JSValueConst *argv)
18792{
18793 JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
18794 argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
18795 JS_FreeValue(ctx, func_obj);
18796 return res;
18797}
18798
18799/* warning: the refcount of the context is not incremented. Return
18800 NULL in case of exception (case of revoked proxy only) */
18801static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
18802{
18803 JSObject *p;
18804 JSContext *realm;
18805
18806 if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
18807 return ctx;
18808 p = JS_VALUE_GET_OBJ(func_obj);
18809 switch(p->class_id) {
18810 case JS_CLASS_C_FUNCTION:
18811 realm = p->u.cfunc.realm;
18812 break;
18813 case JS_CLASS_BYTECODE_FUNCTION:
18814 case JS_CLASS_GENERATOR_FUNCTION:
18815 case JS_CLASS_ASYNC_FUNCTION:
18816 case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
18817 {
18818 JSFunctionBytecode *b;
18819 b = p->u.func.function_bytecode;
18820 realm = b->realm;
18821 }
18822 break;
18823 case JS_CLASS_PROXY:
18824 {
18825 JSProxyData *s = p->u.opaque;
18826 if (!s)
18827 return ctx;
18828 if (s->is_revoked) {
18829 JS_ThrowTypeErrorRevokedProxy(ctx);
18830 return NULL;
18831 } else {
18832 realm = JS_GetFunctionRealm(ctx, s->target);
18833 }
18834 }
18835 break;
18836 case JS_CLASS_BOUND_FUNCTION:
18837 {
18838 JSBoundFunction *bf = p->u.bound_function;
18839 realm = JS_GetFunctionRealm(ctx, bf->func_obj);
18840 }
18841 break;
18842 default:
18843 realm = ctx;
18844 break;
18845 }
18846 return realm;
18847}
18848
18849static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
18850 int class_id)
18851{
18852 JSValue proto, obj;
18853 JSContext *realm;
18854
18855 if (JS_IsUndefined(ctor)) {
18856 proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
18857 } else {
18858 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
18859 if (JS_IsException(proto))
18860 return proto;
18861 if (!JS_IsObject(proto)) {
18862 JS_FreeValue(ctx, proto);
18863 realm = JS_GetFunctionRealm(ctx, ctor);
18864 if (!realm)
18865 return JS_EXCEPTION;
18866 proto = JS_DupValue(ctx, realm->class_proto[class_id]);
18867 }
18868 }
18869 obj = JS_NewObjectProtoClass(ctx, proto, class_id);
18870 JS_FreeValue(ctx, proto);
18871 return obj;
18872}
18873
18874/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
18875static JSValue JS_CallConstructorInternal(JSContext *ctx,
18876 JSValueConst func_obj,
18877 JSValueConst new_target,
18878 int argc, JSValue *argv, int flags)
18879{
18880 JSObject *p;
18881 JSFunctionBytecode *b;
18882
18883 if (js_poll_interrupts(ctx))
18884 return JS_EXCEPTION;
18885 flags |= JS_CALL_FLAG_CONSTRUCTOR;
18886 if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
18887 goto not_a_function;
18888 p = JS_VALUE_GET_OBJ(func_obj);
18889 if (unlikely(!p->is_constructor))
18890 return JS_ThrowTypeError(ctx, "not a constructor");
18891 if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
18892 JSClassCall *call_func;
18893 call_func = ctx->rt->class_array[p->class_id].call;
18894 if (!call_func) {
18895 not_a_function:
18896 return JS_ThrowTypeError(ctx, "not a function");
18897 }
18898 return call_func(ctx, func_obj, new_target, argc,
18899 (JSValueConst *)argv, flags);
18900 }
18901
18902 b = p->u.func.function_bytecode;
18903 if (b->is_derived_class_constructor) {
18904 return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
18905 } else {
18906 JSValue obj, ret;
18907 /* legacy constructor behavior */
18908 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
18909 if (JS_IsException(obj))
18910 return JS_EXCEPTION;
18911 ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
18912 if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
18913 JS_IsException(ret)) {
18914 JS_FreeValue(ctx, obj);
18915 return ret;
18916 } else {
18917 JS_FreeValue(ctx, ret);
18918 return obj;
18919 }
18920 }
18921}
18922
18923JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
18924 JSValueConst new_target,
18925 int argc, JSValueConst *argv)
18926{
18927 return JS_CallConstructorInternal(ctx, func_obj, new_target,
18928 argc, (JSValue *)argv,
18929 JS_CALL_FLAG_COPY_ARGV);
18930}
18931
18932JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
18933 int argc, JSValueConst *argv)
18934{
18935 return JS_CallConstructorInternal(ctx, func_obj, func_obj,
18936 argc, (JSValue *)argv,
18937 JS_CALL_FLAG_COPY_ARGV);
18938}
18939
18940JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
18941 int argc, JSValueConst *argv)
18942{
18943 JSValue func_obj;
18944 func_obj = JS_GetProperty(ctx, this_val, atom);
18945 if (JS_IsException(func_obj))
18946 return func_obj;
18947 return JS_CallFree(ctx, func_obj, this_val, argc, argv);
18948}
18949
18950static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
18951 int argc, JSValueConst *argv)
18952{
18953 JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
18954 JS_FreeValue(ctx, this_val);
18955 return res;
18956}
18957
18958/* JSAsyncFunctionState (used by generator and async functions) */
18959static JSAsyncFunctionState *async_func_init(JSContext *ctx,
18960 JSValueConst func_obj, JSValueConst this_obj,
18961 int argc, JSValueConst *argv)
18962{
18963 JSAsyncFunctionState *s;
18964 JSObject *p;
18965 JSFunctionBytecode *b;
18966 JSStackFrame *sf;
18967 int local_count, i, arg_buf_len, n;
18968
18969 s = js_mallocz(ctx, sizeof(*s));
18970 if (!s)
18971 return NULL;
18972 s->header.ref_count = 1;
18973 add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
18974
18975 sf = &s->frame;
18976 init_list_head(&sf->var_ref_list);
18977 p = JS_VALUE_GET_OBJ(func_obj);
18978 b = p->u.func.function_bytecode;
18979 sf->js_mode = b->js_mode | JS_MODE_ASYNC;
18980 sf->cur_pc = b->byte_code_buf;
18981 arg_buf_len = max_int(b->arg_count, argc);
18982 local_count = arg_buf_len + b->var_count + b->stack_size;
18983 sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
18984 if (!sf->arg_buf) {
18985 js_free(ctx, s);
18986 return NULL;
18987 }
18988 sf->cur_func = JS_DupValue(ctx, func_obj);
18989 s->this_val = JS_DupValue(ctx, this_obj);
18990 s->argc = argc;
18991 sf->arg_count = arg_buf_len;
18992 sf->var_buf = sf->arg_buf + arg_buf_len;
18993 sf->cur_sp = sf->var_buf + b->var_count;
18994 for(i = 0; i < argc; i++)
18995 sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
18996 n = arg_buf_len + b->var_count;
18997 for(i = argc; i < n; i++)
18998 sf->arg_buf[i] = JS_UNDEFINED;
18999 s->resolving_funcs[0] = JS_UNDEFINED;
19000 s->resolving_funcs[1] = JS_UNDEFINED;
19001 s->is_completed = FALSE;
19002 return s;
19003}
19004
19005static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s)
19006{
19007 JSStackFrame *sf = &s->frame;
19008 JSValue *sp;
19009
19010 if (sf->arg_buf) {
19011 /* cannot free the function if it is running */
19012 assert(sf->cur_sp != NULL);
19013 for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
19014 JS_FreeValueRT(rt, *sp);
19015 }
19016 js_free_rt(rt, sf->arg_buf);
19017 sf->arg_buf = NULL;
19018 }
19019 JS_FreeValueRT(rt, sf->cur_func);
19020 JS_FreeValueRT(rt, s->this_val);
19021}
19022
19023static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
19024{
19025 JSRuntime *rt = ctx->rt;
19026 JSStackFrame *sf = &s->frame;
19027 JSValue func_obj, ret;
19028
19029 assert(!s->is_completed);
19030 if (js_check_stack_overflow(ctx->rt, 0)) {
19031 ret = JS_ThrowStackOverflow(ctx);
19032 } else {
19033 /* the tag does not matter provided it is not an object */
19034 func_obj = JS_MKPTR(JS_TAG_INT, s);
19035 ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
19036 s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR);
19037 }
19038 if (JS_IsException(ret) || JS_IsUndefined(ret)) {
19039 if (JS_IsUndefined(ret)) {
19040 ret = sf->cur_sp[-1];
19041 sf->cur_sp[-1] = JS_UNDEFINED;
19042 }
19043 /* end of execution */
19044 s->is_completed = TRUE;
19045
19046 /* close the closure variables. */
19047 close_var_refs(rt, sf);
19048
19049 async_func_free_frame(rt, s);
19050 }
19051 return ret;
19052}
19053
19054static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
19055{
19056 /* cannot close the closure variables here because it would
19057 potentially modify the object graph */
19058 if (!s->is_completed) {
19059 async_func_free_frame(rt, s);
19060 }
19061
19062 JS_FreeValueRT(rt, s->resolving_funcs[0]);
19063 JS_FreeValueRT(rt, s->resolving_funcs[1]);
19064
19065 remove_gc_object(&s->header);
19066 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) {
19067 list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list);
19068 } else {
19069 js_free_rt(rt, s);
19070 }
19071}
19072
19073static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
19074{
19075 if (--s->header.ref_count == 0) {
19076 if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
19077 list_del(&s->header.link);
19078 list_add(&s->header.link, &rt->gc_zero_ref_count_list);
19079 if (rt->gc_phase == JS_GC_PHASE_NONE) {
19080 free_zero_refcount(rt);
19081 }
19082 }
19083 }
19084}
19085
19086/* Generators */
19087
19088typedef enum JSGeneratorStateEnum {
19089 JS_GENERATOR_STATE_SUSPENDED_START,
19090 JS_GENERATOR_STATE_SUSPENDED_YIELD,
19091 JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
19092 JS_GENERATOR_STATE_EXECUTING,
19093 JS_GENERATOR_STATE_COMPLETED,
19094} JSGeneratorStateEnum;
19095
19096typedef struct JSGeneratorData {
19097 JSGeneratorStateEnum state;
19098 JSAsyncFunctionState *func_state;
19099} JSGeneratorData;
19100
19101static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
19102{
19103 if (s->state == JS_GENERATOR_STATE_COMPLETED)
19104 return;
19105 if (s->func_state) {
19106 async_func_free(rt, s->func_state);
19107 s->func_state = NULL;
19108 }
19109 s->state = JS_GENERATOR_STATE_COMPLETED;
19110}
19111
19112static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
19113{
19114 JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
19115
19116 if (s) {
19117 free_generator_stack_rt(rt, s);
19118 js_free_rt(rt, s);
19119 }
19120}
19121
19122static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
19123{
19124 free_generator_stack_rt(ctx->rt, s);
19125}
19126
19127static void js_generator_mark(JSRuntime *rt, JSValueConst val,
19128 JS_MarkFunc *mark_func)
19129{
19130 JSObject *p = JS_VALUE_GET_OBJ(val);
19131 JSGeneratorData *s = p->u.generator_data;
19132
19133 if (!s || !s->func_state)
19134 return;
19135 mark_func(rt, &s->func_state->header);
19136}
19137
19138/* XXX: use enum */
19139#define GEN_MAGIC_NEXT 0
19140#define GEN_MAGIC_RETURN 1
19141#define GEN_MAGIC_THROW 2
19142
19143static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
19144 int argc, JSValueConst *argv,
19145 BOOL *pdone, int magic)
19146{
19147 JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
19148 JSStackFrame *sf;
19149 JSValue ret, func_ret;
19150
19151 *pdone = TRUE;
19152 if (!s)
19153 return JS_ThrowTypeError(ctx, "not a generator");
19154 switch(s->state) {
19155 default:
19156 case JS_GENERATOR_STATE_SUSPENDED_START:
19157 sf = &s->func_state->frame;
19158 if (magic == GEN_MAGIC_NEXT) {
19159 goto exec_no_arg;
19160 } else {
19161 free_generator_stack(ctx, s);
19162 goto done;
19163 }
19164 break;
19165 case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19166 case JS_GENERATOR_STATE_SUSPENDED_YIELD:
19167 sf = &s->func_state->frame;
19168 /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
19169 ret = JS_DupValue(ctx, argv[0]);
19170 if (magic == GEN_MAGIC_THROW &&
19171 s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
19172 JS_Throw(ctx, ret);
19173 s->func_state->throw_flag = TRUE;
19174 } else {
19175 sf->cur_sp[-1] = ret;
19176 sf->cur_sp[0] = JS_NewInt32(ctx, magic);
19177 sf->cur_sp++;
19178 exec_no_arg:
19179 s->func_state->throw_flag = FALSE;
19180 }
19181 s->state = JS_GENERATOR_STATE_EXECUTING;
19182 func_ret = async_func_resume(ctx, s->func_state);
19183 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
19184 if (s->func_state->is_completed) {
19185 /* finalize the execution in case of exception or normal return */
19186 free_generator_stack(ctx, s);
19187 return func_ret;
19188 } else {
19189 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
19190 /* get the returned yield value at the top of the stack */
19191 ret = sf->cur_sp[-1];
19192 sf->cur_sp[-1] = JS_UNDEFINED;
19193 if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
19194 s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19195 /* return (value, done) object */
19196 *pdone = 2;
19197 } else {
19198 *pdone = FALSE;
19199 }
19200 }
19201 break;
19202 case JS_GENERATOR_STATE_COMPLETED:
19203 done:
19204 /* execution is finished */
19205 switch(magic) {
19206 default:
19207 case GEN_MAGIC_NEXT:
19208 ret = JS_UNDEFINED;
19209 break;
19210 case GEN_MAGIC_RETURN:
19211 ret = JS_DupValue(ctx, argv[0]);
19212 break;
19213 case GEN_MAGIC_THROW:
19214 ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
19215 break;
19216 }
19217 break;
19218 case JS_GENERATOR_STATE_EXECUTING:
19219 ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
19220 break;
19221 }
19222 return ret;
19223}
19224
19225static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19226 JSValueConst this_obj,
19227 int argc, JSValueConst *argv,
19228 int flags)
19229{
19230 JSValue obj, func_ret;
19231 JSGeneratorData *s;
19232
19233 s = js_mallocz(ctx, sizeof(*s));
19234 if (!s)
19235 return JS_EXCEPTION;
19236 s->state = JS_GENERATOR_STATE_SUSPENDED_START;
19237 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
19238 if (!s->func_state) {
19239 s->state = JS_GENERATOR_STATE_COMPLETED;
19240 goto fail;
19241 }
19242
19243 /* execute the function up to 'OP_initial_yield' */
19244 func_ret = async_func_resume(ctx, s->func_state);
19245 if (JS_IsException(func_ret))
19246 goto fail;
19247 JS_FreeValue(ctx, func_ret);
19248
19249 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
19250 if (JS_IsException(obj))
19251 goto fail;
19252 JS_SetOpaque(obj, s);
19253 return obj;
19254 fail:
19255 free_generator_stack_rt(ctx->rt, s);
19256 js_free(ctx, s);
19257 return JS_EXCEPTION;
19258}
19259
19260/* AsyncFunction */
19261
19262static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
19263{
19264 JSObject *p = JS_VALUE_GET_OBJ(val);
19265 JSAsyncFunctionState *s = p->u.async_function_data;
19266 if (s) {
19267 async_func_free(rt, s);
19268 }
19269}
19270
19271static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
19272 JS_MarkFunc *mark_func)
19273{
19274 JSObject *p = JS_VALUE_GET_OBJ(val);
19275 JSAsyncFunctionState *s = p->u.async_function_data;
19276 if (s) {
19277 mark_func(rt, &s->header);
19278 }
19279}
19280
19281static int js_async_function_resolve_create(JSContext *ctx,
19282 JSAsyncFunctionState *s,
19283 JSValue *resolving_funcs)
19284{
19285 int i;
19286 JSObject *p;
19287
19288 for(i = 0; i < 2; i++) {
19289 resolving_funcs[i] =
19290 JS_NewObjectProtoClass(ctx, ctx->function_proto,
19291 JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
19292 if (JS_IsException(resolving_funcs[i])) {
19293 if (i == 1)
19294 JS_FreeValue(ctx, resolving_funcs[0]);
19295 return -1;
19296 }
19297 p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
19298 s->header.ref_count++;
19299 p->u.async_function_data = s;
19300 }
19301 return 0;
19302}
19303
19304static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *s)
19305{
19306 JSValue func_ret, ret2;
19307
19308 func_ret = async_func_resume(ctx, s);
19309 if (s->is_completed) {
19310 if (JS_IsException(func_ret)) {
19311 JSValue error;
19312 fail:
19313 error = JS_GetException(ctx);
19314 ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
19315 1, (JSValueConst *)&error);
19316 JS_FreeValue(ctx, error);
19317 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19318 } else {
19319 /* normal return */
19320 ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
19321 1, (JSValueConst *)&func_ret);
19322 JS_FreeValue(ctx, func_ret);
19323 JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
19324 }
19325 } else {
19326 JSValue value, promise, resolving_funcs[2], resolving_funcs1[2];
19327 int i, res;
19328
19329 value = s->frame.cur_sp[-1];
19330 s->frame.cur_sp[-1] = JS_UNDEFINED;
19331
19332 /* await */
19333 JS_FreeValue(ctx, func_ret); /* not used */
19334 promise = js_promise_resolve(ctx, ctx->promise_ctor,
19335 1, (JSValueConst *)&value, 0);
19336 JS_FreeValue(ctx, value);
19337 if (JS_IsException(promise))
19338 goto fail;
19339 if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
19340 JS_FreeValue(ctx, promise);
19341 goto fail;
19342 }
19343
19344 /* Note: no need to create 'thrownawayCapability' as in
19345 the spec */
19346 for(i = 0; i < 2; i++)
19347 resolving_funcs1[i] = JS_UNDEFINED;
19348 res = perform_promise_then(ctx, promise,
19349 (JSValueConst *)resolving_funcs,
19350 (JSValueConst *)resolving_funcs1);
19351 JS_FreeValue(ctx, promise);
19352 for(i = 0; i < 2; i++)
19353 JS_FreeValue(ctx, resolving_funcs[i]);
19354 if (res)
19355 goto fail;
19356 }
19357}
19358
19359static JSValue js_async_function_resolve_call(JSContext *ctx,
19360 JSValueConst func_obj,
19361 JSValueConst this_obj,
19362 int argc, JSValueConst *argv,
19363 int flags)
19364{
19365 JSObject *p = JS_VALUE_GET_OBJ(func_obj);
19366 JSAsyncFunctionState *s = p->u.async_function_data;
19367 BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
19368 JSValueConst arg;
19369
19370 if (argc > 0)
19371 arg = argv[0];
19372 else
19373 arg = JS_UNDEFINED;
19374 s->throw_flag = is_reject;
19375 if (is_reject) {
19376 JS_Throw(ctx, JS_DupValue(ctx, arg));
19377 } else {
19378 /* return value of await */
19379 s->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19380 }
19381 js_async_function_resume(ctx, s);
19382 return JS_UNDEFINED;
19383}
19384
19385static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
19386 JSValueConst this_obj,
19387 int argc, JSValueConst *argv, int flags)
19388{
19389 JSValue promise;
19390 JSAsyncFunctionState *s;
19391
19392 s = async_func_init(ctx, func_obj, this_obj, argc, argv);
19393 if (!s)
19394 return JS_EXCEPTION;
19395
19396 promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
19397 if (JS_IsException(promise)) {
19398 async_func_free(ctx->rt, s);
19399 return JS_EXCEPTION;
19400 }
19401
19402 js_async_function_resume(ctx, s);
19403
19404 async_func_free(ctx->rt, s);
19405
19406 return promise;
19407}
19408
19409/* AsyncGenerator */
19410
19411typedef enum JSAsyncGeneratorStateEnum {
19412 JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
19413 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
19414 JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
19415 JS_ASYNC_GENERATOR_STATE_EXECUTING,
19416 JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
19417 JS_ASYNC_GENERATOR_STATE_COMPLETED,
19418} JSAsyncGeneratorStateEnum;
19419
19420typedef struct JSAsyncGeneratorRequest {
19421 struct list_head link;
19422 /* completion */
19423 int completion_type; /* GEN_MAGIC_x */
19424 JSValue result;
19425 /* promise capability */
19426 JSValue promise;
19427 JSValue resolving_funcs[2];
19428} JSAsyncGeneratorRequest;
19429
19430typedef struct JSAsyncGeneratorData {
19431 JSObject *generator; /* back pointer to the object (const) */
19432 JSAsyncGeneratorStateEnum state;
19433 /* func_state is NULL is state AWAITING_RETURN and COMPLETED */
19434 JSAsyncFunctionState *func_state;
19435 struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
19436} JSAsyncGeneratorData;
19437
19438static void js_async_generator_free(JSRuntime *rt,
19439 JSAsyncGeneratorData *s)
19440{
19441 struct list_head *el, *el1;
19442 JSAsyncGeneratorRequest *req;
19443
19444 list_for_each_safe(el, el1, &s->queue) {
19445 req = list_entry(el, JSAsyncGeneratorRequest, link);
19446 JS_FreeValueRT(rt, req->result);
19447 JS_FreeValueRT(rt, req->promise);
19448 JS_FreeValueRT(rt, req->resolving_funcs[0]);
19449 JS_FreeValueRT(rt, req->resolving_funcs[1]);
19450 js_free_rt(rt, req);
19451 }
19452 if (s->func_state)
19453 async_func_free(rt, s->func_state);
19454 js_free_rt(rt, s);
19455}
19456
19457static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
19458{
19459 JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
19460
19461 if (s) {
19462 js_async_generator_free(rt, s);
19463 }
19464}
19465
19466static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
19467 JS_MarkFunc *mark_func)
19468{
19469 JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
19470 struct list_head *el;
19471 JSAsyncGeneratorRequest *req;
19472 if (s) {
19473 list_for_each(el, &s->queue) {
19474 req = list_entry(el, JSAsyncGeneratorRequest, link);
19475 JS_MarkValue(rt, req->result, mark_func);
19476 JS_MarkValue(rt, req->promise, mark_func);
19477 JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
19478 JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
19479 }
19480 if (s->func_state) {
19481 mark_func(rt, &s->func_state->header);
19482 }
19483 }
19484}
19485
19486static JSValue js_async_generator_resolve_function(JSContext *ctx,
19487 JSValueConst this_obj,
19488 int argc, JSValueConst *argv,
19489 int magic, JSValue *func_data);
19490
19491static int js_async_generator_resolve_function_create(JSContext *ctx,
19492 JSValueConst generator,
19493 JSValue *resolving_funcs,
19494 BOOL is_resume_next)
19495{
19496 int i;
19497 JSValue func;
19498
19499 for(i = 0; i < 2; i++) {
19500 func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
19501 i + is_resume_next * 2, 1, &generator);
19502 if (JS_IsException(func)) {
19503 if (i == 1)
19504 JS_FreeValue(ctx, resolving_funcs[0]);
19505 return -1;
19506 }
19507 resolving_funcs[i] = func;
19508 }
19509 return 0;
19510}
19511
19512static int js_async_generator_await(JSContext *ctx,
19513 JSAsyncGeneratorData *s,
19514 JSValueConst value)
19515{
19516 JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19517 int i, res;
19518
19519 promise = js_promise_resolve(ctx, ctx->promise_ctor,
19520 1, &value, 0);
19521 if (JS_IsException(promise))
19522 goto fail;
19523
19524 if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
19525 resolving_funcs, FALSE)) {
19526 JS_FreeValue(ctx, promise);
19527 goto fail;
19528 }
19529
19530 /* Note: no need to create 'thrownawayCapability' as in
19531 the spec */
19532 for(i = 0; i < 2; i++)
19533 resolving_funcs1[i] = JS_UNDEFINED;
19534 res = perform_promise_then(ctx, promise,
19535 (JSValueConst *)resolving_funcs,
19536 (JSValueConst *)resolving_funcs1);
19537 JS_FreeValue(ctx, promise);
19538 for(i = 0; i < 2; i++)
19539 JS_FreeValue(ctx, resolving_funcs[i]);
19540 if (res)
19541 goto fail;
19542 return 0;
19543 fail:
19544 return -1;
19545}
19546
19547static void js_async_generator_resolve_or_reject(JSContext *ctx,
19548 JSAsyncGeneratorData *s,
19549 JSValueConst result,
19550 int is_reject)
19551{
19552 JSAsyncGeneratorRequest *next;
19553 JSValue ret;
19554
19555 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19556 list_del(&next->link);
19557 ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
19558 &result);
19559 JS_FreeValue(ctx, ret);
19560 JS_FreeValue(ctx, next->result);
19561 JS_FreeValue(ctx, next->promise);
19562 JS_FreeValue(ctx, next->resolving_funcs[0]);
19563 JS_FreeValue(ctx, next->resolving_funcs[1]);
19564 js_free(ctx, next);
19565}
19566
19567static void js_async_generator_resolve(JSContext *ctx,
19568 JSAsyncGeneratorData *s,
19569 JSValueConst value,
19570 BOOL done)
19571{
19572 JSValue result;
19573 result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
19574 /* XXX: better exception handling ? */
19575 js_async_generator_resolve_or_reject(ctx, s, result, 0);
19576 JS_FreeValue(ctx, result);
19577 }
19578
19579static void js_async_generator_reject(JSContext *ctx,
19580 JSAsyncGeneratorData *s,
19581 JSValueConst exception)
19582{
19583 js_async_generator_resolve_or_reject(ctx, s, exception, 1);
19584}
19585
19586static void js_async_generator_complete(JSContext *ctx,
19587 JSAsyncGeneratorData *s)
19588{
19589 if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
19590 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19591 async_func_free(ctx->rt, s->func_state);
19592 s->func_state = NULL;
19593 }
19594}
19595
19596static int js_async_generator_completed_return(JSContext *ctx,
19597 JSAsyncGeneratorData *s,
19598 JSValueConst value)
19599{
19600 JSValue promise, resolving_funcs[2], resolving_funcs1[2];
19601 int res;
19602
19603 // Can fail looking up JS_ATOM_constructor when is_reject==0.
19604 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value,
19605 /*is_reject*/0);
19606 // A poisoned .constructor property is observable and the resulting
19607 // exception should be delivered to the catch handler.
19608 if (JS_IsException(promise)) {
19609 JSValue err = JS_GetException(ctx);
19610 promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err,
19611 /*is_reject*/1);
19612 JS_FreeValue(ctx, err);
19613 if (JS_IsException(promise))
19614 return -1;
19615 }
19616 if (js_async_generator_resolve_function_create(ctx,
19617 JS_MKPTR(JS_TAG_OBJECT, s->generator),
19618 resolving_funcs1,
19619 TRUE)) {
19620 JS_FreeValue(ctx, promise);
19621 return -1;
19622 }
19623 resolving_funcs[0] = JS_UNDEFINED;
19624 resolving_funcs[1] = JS_UNDEFINED;
19625 res = perform_promise_then(ctx, promise,
19626 (JSValueConst *)resolving_funcs1,
19627 (JSValueConst *)resolving_funcs);
19628 JS_FreeValue(ctx, resolving_funcs1[0]);
19629 JS_FreeValue(ctx, resolving_funcs1[1]);
19630 JS_FreeValue(ctx, promise);
19631 return res;
19632}
19633
19634static void js_async_generator_resume_next(JSContext *ctx,
19635 JSAsyncGeneratorData *s)
19636{
19637 JSAsyncGeneratorRequest *next;
19638 JSValue func_ret, value;
19639
19640 for(;;) {
19641 if (list_empty(&s->queue))
19642 break;
19643 next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
19644 switch(s->state) {
19645 case JS_ASYNC_GENERATOR_STATE_EXECUTING:
19646 /* only happens when restarting execution after await() */
19647 goto resume_exec;
19648 case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
19649 goto done;
19650 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
19651 if (next->completion_type == GEN_MAGIC_NEXT) {
19652 goto exec_no_arg;
19653 } else {
19654 js_async_generator_complete(ctx, s);
19655 }
19656 break;
19657 case JS_ASYNC_GENERATOR_STATE_COMPLETED:
19658 if (next->completion_type == GEN_MAGIC_NEXT) {
19659 js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
19660 } else if (next->completion_type == GEN_MAGIC_RETURN) {
19661 s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
19662 js_async_generator_completed_return(ctx, s, next->result);
19663 } else {
19664 js_async_generator_reject(ctx, s, next->result);
19665 }
19666 goto done;
19667 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
19668 case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
19669 value = JS_DupValue(ctx, next->result);
19670 if (next->completion_type == GEN_MAGIC_THROW &&
19671 s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
19672 JS_Throw(ctx, value);
19673 s->func_state->throw_flag = TRUE;
19674 } else {
19675 /* 'yield' returns a value. 'yield *' also returns a value
19676 in case the 'throw' method is called */
19677 s->func_state->frame.cur_sp[-1] = value;
19678 s->func_state->frame.cur_sp[0] =
19679 JS_NewInt32(ctx, next->completion_type);
19680 s->func_state->frame.cur_sp++;
19681 exec_no_arg:
19682 s->func_state->throw_flag = FALSE;
19683 }
19684 s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
19685 resume_exec:
19686 func_ret = async_func_resume(ctx, s->func_state);
19687 if (s->func_state->is_completed) {
19688 if (JS_IsException(func_ret)) {
19689 value = JS_GetException(ctx);
19690 js_async_generator_complete(ctx, s);
19691 js_async_generator_reject(ctx, s, value);
19692 JS_FreeValue(ctx, value);
19693 } else {
19694 /* end of function */
19695 js_async_generator_complete(ctx, s);
19696 js_async_generator_resolve(ctx, s, func_ret, TRUE);
19697 JS_FreeValue(ctx, func_ret);
19698 }
19699 } else {
19700 int func_ret_code, ret;
19701 assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT);
19702 func_ret_code = JS_VALUE_GET_INT(func_ret);
19703 value = s->func_state->frame.cur_sp[-1];
19704 s->func_state->frame.cur_sp[-1] = JS_UNDEFINED;
19705 switch(func_ret_code) {
19706 case FUNC_RET_YIELD:
19707 case FUNC_RET_YIELD_STAR:
19708 if (func_ret_code == FUNC_RET_YIELD_STAR)
19709 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
19710 else
19711 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
19712 js_async_generator_resolve(ctx, s, value, FALSE);
19713 JS_FreeValue(ctx, value);
19714 break;
19715 case FUNC_RET_AWAIT:
19716 ret = js_async_generator_await(ctx, s, value);
19717 JS_FreeValue(ctx, value);
19718 if (ret < 0) {
19719 /* exception: throw it */
19720 s->func_state->throw_flag = TRUE;
19721 goto resume_exec;
19722 }
19723 goto done;
19724 default:
19725 abort();
19726 }
19727 }
19728 break;
19729 default:
19730 abort();
19731 }
19732 }
19733 done: ;
19734}
19735
19736static JSValue js_async_generator_resolve_function(JSContext *ctx,
19737 JSValueConst this_obj,
19738 int argc, JSValueConst *argv,
19739 int magic, JSValue *func_data)
19740{
19741 BOOL is_reject = magic & 1;
19742 JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
19743 JSValueConst arg = argv[0];
19744
19745 /* XXX: what if s == NULL */
19746
19747 if (magic >= 2) {
19748 /* resume next case in AWAITING_RETURN state */
19749 assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
19750 s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
19751 s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
19752 if (is_reject) {
19753 js_async_generator_reject(ctx, s, arg);
19754 } else {
19755 js_async_generator_resolve(ctx, s, arg, TRUE);
19756 }
19757 } else {
19758 /* restart function execution after await() */
19759 assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
19760 s->func_state->throw_flag = is_reject;
19761 if (is_reject) {
19762 JS_Throw(ctx, JS_DupValue(ctx, arg));
19763 } else {
19764 /* return value of await */
19765 s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg);
19766 }
19767 js_async_generator_resume_next(ctx, s);
19768 }
19769 return JS_UNDEFINED;
19770}
19771
19772/* magic = GEN_MAGIC_x */
19773static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
19774 int argc, JSValueConst *argv,
19775 int magic)
19776{
19777 JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
19778 JSValue promise, resolving_funcs[2];
19779 JSAsyncGeneratorRequest *req;
19780
19781 promise = JS_NewPromiseCapability(ctx, resolving_funcs);
19782 if (JS_IsException(promise))
19783 return JS_EXCEPTION;
19784 if (!s) {
19785 JSValue err, res2;
19786 JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
19787 err = JS_GetException(ctx);
19788 res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
19789 1, (JSValueConst *)&err);
19790 JS_FreeValue(ctx, err);
19791 JS_FreeValue(ctx, res2);
19792 JS_FreeValue(ctx, resolving_funcs[0]);
19793 JS_FreeValue(ctx, resolving_funcs[1]);
19794 return promise;
19795 }
19796 req = js_mallocz(ctx, sizeof(*req));
19797 if (!req)
19798 goto fail;
19799 req->completion_type = magic;
19800 req->result = JS_DupValue(ctx, argv[0]);
19801 req->promise = JS_DupValue(ctx, promise);
19802 req->resolving_funcs[0] = resolving_funcs[0];
19803 req->resolving_funcs[1] = resolving_funcs[1];
19804 list_add_tail(&req->link, &s->queue);
19805 if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
19806 js_async_generator_resume_next(ctx, s);
19807 }
19808 return promise;
19809 fail:
19810 JS_FreeValue(ctx, resolving_funcs[0]);
19811 JS_FreeValue(ctx, resolving_funcs[1]);
19812 JS_FreeValue(ctx, promise);
19813 return JS_EXCEPTION;
19814}
19815
19816static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
19817 JSValueConst this_obj,
19818 int argc, JSValueConst *argv,
19819 int flags)
19820{
19821 JSValue obj, func_ret;
19822 JSAsyncGeneratorData *s;
19823
19824 s = js_mallocz(ctx, sizeof(*s));
19825 if (!s)
19826 return JS_EXCEPTION;
19827 s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
19828 init_list_head(&s->queue);
19829 s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv);
19830 if (!s->func_state)
19831 goto fail;
19832 /* execute the function up to 'OP_initial_yield' (no yield nor
19833 await are possible) */
19834 func_ret = async_func_resume(ctx, s->func_state);
19835 if (JS_IsException(func_ret))
19836 goto fail;
19837 JS_FreeValue(ctx, func_ret);
19838
19839 obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
19840 if (JS_IsException(obj))
19841 goto fail;
19842 s->generator = JS_VALUE_GET_OBJ(obj);
19843 JS_SetOpaque(obj, s);
19844 return obj;
19845 fail:
19846 js_async_generator_free(ctx->rt, s);
19847 return JS_EXCEPTION;
19848}
19849
19850/* JS parser */
19851
19852enum {
19853 TOK_NUMBER = -128,
19854 TOK_STRING,
19855 TOK_TEMPLATE,
19856 TOK_IDENT,
19857 TOK_REGEXP,
19858 /* warning: order matters (see js_parse_assign_expr) */
19859 TOK_MUL_ASSIGN,
19860 TOK_DIV_ASSIGN,
19861 TOK_MOD_ASSIGN,
19862 TOK_PLUS_ASSIGN,
19863 TOK_MINUS_ASSIGN,
19864 TOK_SHL_ASSIGN,
19865 TOK_SAR_ASSIGN,
19866 TOK_SHR_ASSIGN,
19867 TOK_AND_ASSIGN,
19868 TOK_XOR_ASSIGN,
19869 TOK_OR_ASSIGN,
19870 TOK_POW_ASSIGN,
19871 TOK_LAND_ASSIGN,
19872 TOK_LOR_ASSIGN,
19873 TOK_DOUBLE_QUESTION_MARK_ASSIGN,
19874 TOK_DEC,
19875 TOK_INC,
19876 TOK_SHL,
19877 TOK_SAR,
19878 TOK_SHR,
19879 TOK_LT,
19880 TOK_LTE,
19881 TOK_GT,
19882 TOK_GTE,
19883 TOK_EQ,
19884 TOK_STRICT_EQ,
19885 TOK_NEQ,
19886 TOK_STRICT_NEQ,
19887 TOK_LAND,
19888 TOK_LOR,
19889 TOK_POW,
19890 TOK_ARROW,
19891 TOK_ELLIPSIS,
19892 TOK_DOUBLE_QUESTION_MARK,
19893 TOK_QUESTION_MARK_DOT,
19894 TOK_ERROR,
19895 TOK_PRIVATE_NAME,
19896 TOK_EOF,
19897 /* keywords: WARNING: same order as atoms */
19898 TOK_NULL, /* must be first */
19899 TOK_FALSE,
19900 TOK_TRUE,
19901 TOK_IF,
19902 TOK_ELSE,
19903 TOK_RETURN,
19904 TOK_VAR,
19905 TOK_THIS,
19906 TOK_DELETE,
19907 TOK_VOID,
19908 TOK_TYPEOF,
19909 TOK_NEW,
19910 TOK_IN,
19911 TOK_INSTANCEOF,
19912 TOK_DO,
19913 TOK_WHILE,
19914 TOK_FOR,
19915 TOK_BREAK,
19916 TOK_CONTINUE,
19917 TOK_SWITCH,
19918 TOK_CASE,
19919 TOK_DEFAULT,
19920 TOK_THROW,
19921 TOK_TRY,
19922 TOK_CATCH,
19923 TOK_FINALLY,
19924 TOK_FUNCTION,
19925 TOK_DEBUGGER,
19926 TOK_WITH,
19927 /* FutureReservedWord */
19928 TOK_CLASS,
19929 TOK_CONST,
19930 TOK_ENUM,
19931 TOK_EXPORT,
19932 TOK_EXTENDS,
19933 TOK_IMPORT,
19934 TOK_SUPER,
19935 /* FutureReservedWords when parsing strict mode code */
19936 TOK_IMPLEMENTS,
19937 TOK_INTERFACE,
19938 TOK_LET,
19939 TOK_PACKAGE,
19940 TOK_PRIVATE,
19941 TOK_PROTECTED,
19942 TOK_PUBLIC,
19943 TOK_STATIC,
19944 TOK_YIELD,
19945 TOK_AWAIT, /* must be last */
19946 TOK_OF, /* only used for js_parse_skip_parens_token() */
19947};
19948
19949#define TOK_FIRST_KEYWORD TOK_NULL
19950#define TOK_LAST_KEYWORD TOK_AWAIT
19951
19952/* unicode code points */
19953#define CP_NBSP 0x00a0
19954#define CP_BOM 0xfeff
19955
19956#define CP_LS 0x2028
19957#define CP_PS 0x2029
19958
19959typedef struct BlockEnv {
19960 struct BlockEnv *prev;
19961 JSAtom label_name; /* JS_ATOM_NULL if none */
19962 int label_break; /* -1 if none */
19963 int label_cont; /* -1 if none */
19964 int drop_count; /* number of stack elements to drop */
19965 int label_finally; /* -1 if none */
19966 int scope_level;
19967 uint8_t has_iterator : 1;
19968 uint8_t is_regular_stmt : 1; /* i.e. not a loop statement */
19969} BlockEnv;
19970
19971typedef struct JSGlobalVar {
19972 int cpool_idx; /* if >= 0, index in the constant pool for hoisted
19973 function defintion*/
19974 uint8_t force_init : 1; /* force initialization to undefined */
19975 uint8_t is_lexical : 1; /* global let/const definition */
19976 uint8_t is_const : 1; /* const definition */
19977 int scope_level; /* scope of definition */
19978 JSAtom var_name; /* variable name */
19979} JSGlobalVar;
19980
19981typedef struct RelocEntry {
19982 struct RelocEntry *next;
19983 uint32_t addr; /* address to patch */
19984 int size; /* address size: 1, 2 or 4 bytes */
19985} RelocEntry;
19986
19987typedef struct JumpSlot {
19988 int op;
19989 int size;
19990 int pos;
19991 int label;
19992} JumpSlot;
19993
19994typedef struct LabelSlot {
19995 int ref_count;
19996 int pos; /* phase 1 address, -1 means not resolved yet */
19997 int pos2; /* phase 2 address, -1 means not resolved yet */
19998 int addr; /* phase 3 address, -1 means not resolved yet */
19999 RelocEntry *first_reloc;
20000} LabelSlot;
20001
20002typedef struct LineNumberSlot {
20003 uint32_t pc;
20004 uint32_t source_pos;
20005} LineNumberSlot;
20006
20007typedef struct {
20008 /* last source position */
20009 const uint8_t *ptr;
20010 int line_num;
20011 int col_num;
20012 const uint8_t *buf_start;
20013} GetLineColCache;
20014
20015typedef enum JSParseFunctionEnum {
20016 JS_PARSE_FUNC_STATEMENT,
20017 JS_PARSE_FUNC_VAR,
20018 JS_PARSE_FUNC_EXPR,
20019 JS_PARSE_FUNC_ARROW,
20020 JS_PARSE_FUNC_GETTER,
20021 JS_PARSE_FUNC_SETTER,
20022 JS_PARSE_FUNC_METHOD,
20023 JS_PARSE_FUNC_CLASS_STATIC_INIT,
20024 JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
20025 JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
20026} JSParseFunctionEnum;
20027
20028typedef enum JSParseExportEnum {
20029 JS_PARSE_EXPORT_NONE,
20030 JS_PARSE_EXPORT_NAMED,
20031 JS_PARSE_EXPORT_DEFAULT,
20032} JSParseExportEnum;
20033
20034typedef struct JSFunctionDef {
20035 JSContext *ctx;
20036 struct JSFunctionDef *parent;
20037 int parent_cpool_idx; /* index in the constant pool of the parent
20038 or -1 if none */
20039 int parent_scope_level; /* scope level in parent at point of definition */
20040 struct list_head child_list; /* list of JSFunctionDef.link */
20041 struct list_head link;
20042
20043 BOOL is_eval; /* TRUE if eval code */
20044 int eval_type; /* only valid if is_eval = TRUE */
20045 BOOL is_global_var; /* TRUE if variables are not defined locally:
20046 eval global, eval module or non strict eval */
20047 BOOL is_func_expr; /* TRUE if function expression */
20048 BOOL has_home_object; /* TRUE if the home object is available */
20049 BOOL has_prototype; /* true if a prototype field is necessary */
20050 BOOL has_simple_parameter_list;
20051 BOOL has_parameter_expressions; /* if true, an argument scope is created */
20052 BOOL has_use_strict; /* to reject directive in special cases */
20053 BOOL has_eval_call; /* true if the function contains a call to eval() */
20054 BOOL has_arguments_binding; /* true if the 'arguments' binding is
20055 available in the function */
20056 BOOL has_this_binding; /* true if the 'this' and new.target binding are
20057 available in the function */
20058 BOOL new_target_allowed; /* true if the 'new.target' does not
20059 throw a syntax error */
20060 BOOL super_call_allowed; /* true if super() is allowed */
20061 BOOL super_allowed; /* true if super. or super[] is allowed */
20062 BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
20063 BOOL is_derived_class_constructor;
20064 BOOL in_function_body;
20065 JSFunctionKindEnum func_kind : 8;
20066 JSParseFunctionEnum func_type : 8;
20067 uint8_t js_mode; /* bitmap of JS_MODE_x */
20068 JSAtom func_name; /* JS_ATOM_NULL if no name */
20069
20070 JSVarDef *vars;
20071 int var_size; /* allocated size for vars[] */
20072 int var_count;
20073 JSVarDef *args;
20074 int arg_size; /* allocated size for args[] */
20075 int arg_count; /* number of arguments */
20076 int defined_arg_count;
20077 int var_object_idx; /* -1 if none */
20078 int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
20079 int arguments_var_idx; /* -1 if none */
20080 int arguments_arg_idx; /* argument variable definition in argument scope,
20081 -1 if none */
20082 int func_var_idx; /* variable containing the current function (-1
20083 if none, only used if is_func_expr is true) */
20084 int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
20085 int this_var_idx; /* variable containg the 'this' value, -1 if none */
20086 int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
20087 int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
20088 int home_object_var_idx;
20089 BOOL need_home_object;
20090
20091 int scope_level; /* index into fd->scopes if the current lexical scope */
20092 int scope_first; /* index into vd->vars of first lexically scoped variable */
20093 int scope_size; /* allocated size of fd->scopes array */
20094 int scope_count; /* number of entries used in the fd->scopes array */
20095 JSVarScope *scopes;
20096 JSVarScope def_scope_array[4];
20097 int body_scope; /* scope of the body of the function or eval */
20098
20099 int global_var_count;
20100 int global_var_size;
20101 JSGlobalVar *global_vars;
20102
20103 DynBuf byte_code;
20104 int last_opcode_pos; /* -1 if no last opcode */
20105 const uint8_t *last_opcode_source_ptr;
20106 BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
20107
20108 LabelSlot *label_slots;
20109 int label_size; /* allocated size for label_slots[] */
20110 int label_count;
20111 BlockEnv *top_break; /* break/continue label stack */
20112
20113 /* constant pool (strings, functions, numbers) */
20114 JSValue *cpool;
20115 int cpool_count;
20116 int cpool_size;
20117
20118 /* list of variables in the closure */
20119 int closure_var_count;
20120 int closure_var_size;
20121 JSClosureVar *closure_var;
20122
20123 JumpSlot *jump_slots;
20124 int jump_size;
20125 int jump_count;
20126
20127 LineNumberSlot *line_number_slots;
20128 int line_number_size;
20129 int line_number_count;
20130 int line_number_last;
20131 int line_number_last_pc;
20132
20133 /* pc2line table */
20134 BOOL strip_debug : 1; /* strip all debug info (implies strip_source = TRUE) */
20135 BOOL strip_source : 1; /* strip only source code */
20136 JSAtom filename;
20137 uint32_t source_pos; /* pointer in the eval() source */
20138 GetLineColCache *get_line_col_cache; /* XXX: could remove to save memory */
20139 DynBuf pc2line;
20140
20141 char *source; /* raw source, utf-8 encoded */
20142 int source_len;
20143
20144 JSModuleDef *module; /* != NULL when parsing a module */
20145 BOOL has_await; /* TRUE if await is used (used in module eval) */
20146} JSFunctionDef;
20147
20148typedef struct JSToken {
20149 int val;
20150 const uint8_t *ptr; /* position in the source */
20151 union {
20152 struct {
20153 JSValue str;
20154 int sep;
20155 } str;
20156 struct {
20157 JSValue val;
20158 } num;
20159 struct {
20160 JSAtom atom;
20161 BOOL has_escape;
20162 BOOL is_reserved;
20163 } ident;
20164 struct {
20165 JSValue body;
20166 JSValue flags;
20167 } regexp;
20168 } u;
20169} JSToken;
20170
20171typedef struct JSParseState {
20172 JSContext *ctx;
20173 const char *filename;
20174 JSToken token;
20175 BOOL got_lf; /* true if got line feed before the current token */
20176 const uint8_t *last_ptr;
20177 const uint8_t *buf_start;
20178 const uint8_t *buf_ptr;
20179 const uint8_t *buf_end;
20180
20181 /* current function code */
20182 JSFunctionDef *cur_func;
20183 BOOL is_module; /* parsing a module */
20184 BOOL allow_html_comments;
20185 BOOL ext_json; /* true if accepting JSON superset */
20186 GetLineColCache get_line_col_cache;
20187} JSParseState;
20188
20189typedef struct JSOpCode {
20190#ifdef DUMP_BYTECODE
20191 const char *name;
20192#endif
20193 uint8_t size; /* in bytes */
20194 /* the opcodes remove n_pop items from the top of the stack, then
20195 pushes n_push items */
20196 uint8_t n_pop;
20197 uint8_t n_push;
20198 uint8_t fmt;
20199} JSOpCode;
20200
20201static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
20202#define FMT(f)
20203#ifdef DUMP_BYTECODE
20204#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
20205#else
20206#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
20207#endif
20208#include "quickjs-opcode.h"
20209#undef DEF
20210#undef FMT
20211};
20212
20213#if SHORT_OPCODES
20214/* After the final compilation pass, short opcodes are used. Their
20215 opcodes overlap with the temporary opcodes which cannot appear in
20216 the final bytecode. Their description is after the temporary
20217 opcodes in opcode_info[]. */
20218#define short_opcode_info(op) \
20219 opcode_info[(op) >= OP_TEMP_START ? \
20220 (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
20221#else
20222#define short_opcode_info(op) opcode_info[op]
20223#endif
20224
20225static __exception int next_token(JSParseState *s);
20226
20227static void free_token(JSParseState *s, JSToken *token)
20228{
20229 switch(token->val) {
20230 case TOK_NUMBER:
20231 JS_FreeValue(s->ctx, token->u.num.val);
20232 break;
20233 case TOK_STRING:
20234 case TOK_TEMPLATE:
20235 JS_FreeValue(s->ctx, token->u.str.str);
20236 break;
20237 case TOK_REGEXP:
20238 JS_FreeValue(s->ctx, token->u.regexp.body);
20239 JS_FreeValue(s->ctx, token->u.regexp.flags);
20240 break;
20241 case TOK_IDENT:
20242 case TOK_PRIVATE_NAME:
20243 JS_FreeAtom(s->ctx, token->u.ident.atom);
20244 break;
20245 default:
20246 if (token->val >= TOK_FIRST_KEYWORD &&
20247 token->val <= TOK_LAST_KEYWORD) {
20248 JS_FreeAtom(s->ctx, token->u.ident.atom);
20249 }
20250 break;
20251 }
20252}
20253
20254static void __attribute((unused)) dump_token(JSParseState *s,
20255 const JSToken *token)
20256{
20257 switch(token->val) {
20258 case TOK_NUMBER:
20259 {
20260 double d;
20261 JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */
20262 printf("number: %.14g\n", d);
20263 }
20264 break;
20265 case TOK_IDENT:
20266 dump_atom:
20267 {
20268 char buf[ATOM_GET_STR_BUF_SIZE];
20269 printf("ident: '%s'\n",
20270 JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
20271 }
20272 break;
20273 case TOK_STRING:
20274 {
20275 const char *str;
20276 /* XXX: quote the string */
20277 str = JS_ToCString(s->ctx, token->u.str.str);
20278 printf("string: '%s'\n", str);
20279 JS_FreeCString(s->ctx, str);
20280 }
20281 break;
20282 case TOK_TEMPLATE:
20283 {
20284 const char *str;
20285 str = JS_ToCString(s->ctx, token->u.str.str);
20286 printf("template: `%s`\n", str);
20287 JS_FreeCString(s->ctx, str);
20288 }
20289 break;
20290 case TOK_REGEXP:
20291 {
20292 const char *str, *str2;
20293 str = JS_ToCString(s->ctx, token->u.regexp.body);
20294 str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
20295 printf("regexp: '%s' '%s'\n", str, str2);
20296 JS_FreeCString(s->ctx, str);
20297 JS_FreeCString(s->ctx, str2);
20298 }
20299 break;
20300 case TOK_EOF:
20301 printf("eof\n");
20302 break;
20303 default:
20304 if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
20305 goto dump_atom;
20306 } else if (s->token.val >= 256) {
20307 printf("token: %d\n", token->val);
20308 } else {
20309 printf("token: '%c'\n", token->val);
20310 }
20311 break;
20312 }
20313}
20314
20315/* return the zero based line and column number in the source. */
20316/* Note: we no longer support '\r' as line terminator */
20317static int get_line_col(int *pcol_num, const uint8_t *buf, size_t len)
20318{
20319 int line_num, col_num, c;
20320 size_t i;
20321
20322 line_num = 0;
20323 col_num = 0;
20324 for(i = 0; i < len; i++) {
20325 c = buf[i];
20326 if (c == '\n') {
20327 line_num++;
20328 col_num = 0;
20329 } else if (c < 0x80 || c >= 0xc0) {
20330 col_num++;
20331 }
20332 }
20333 *pcol_num = col_num;
20334 return line_num;
20335}
20336
20337static int get_line_col_cached(GetLineColCache *s, int *pcol_num, const uint8_t *ptr)
20338{
20339 int line_num, col_num;
20340 if (ptr >= s->ptr) {
20341 line_num = get_line_col(&col_num, s->ptr, ptr - s->ptr);
20342 if (line_num == 0) {
20343 s->col_num += col_num;
20344 } else {
20345 s->line_num += line_num;
20346 s->col_num = col_num;
20347 }
20348 } else {
20349 line_num = get_line_col(&col_num, ptr, s->ptr - ptr);
20350 if (line_num == 0) {
20351 s->col_num -= col_num;
20352 } else {
20353 const uint8_t *p;
20354 s->line_num -= line_num;
20355 /* find the absolute column position */
20356 col_num = 0;
20357 for(p = ptr - 1; p >= s->buf_start; p--) {
20358 if (*p == '\n') {
20359 break;
20360 } else if (*p < 0x80 || *p >= 0xc0) {
20361 col_num++;
20362 }
20363 }
20364 s->col_num = col_num;
20365 }
20366 }
20367 s->ptr = ptr;
20368 *pcol_num = s->col_num;
20369 return s->line_num;
20370}
20371
20372/* 'ptr' is the position of the error in the source */
20373static int js_parse_error_v(JSParseState *s, const uint8_t *ptr, const char *fmt, va_list ap)
20374{
20375 JSContext *ctx = s->ctx;
20376 int line_num, col_num;
20377 line_num = get_line_col(&col_num, s->buf_start, ptr - s->buf_start);
20378 JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
20379 build_backtrace(ctx, ctx->rt->current_exception, s->filename,
20380 line_num + 1, col_num + 1, 0);
20381 return -1;
20382}
20383
20384static __attribute__((format(printf, 3, 4))) int js_parse_error_pos(JSParseState *s, const uint8_t *ptr, const char *fmt, ...)
20385{
20386 va_list ap;
20387 int ret;
20388
20389 va_start(ap, fmt);
20390 ret = js_parse_error_v(s, ptr, fmt, ap);
20391 va_end(ap);
20392 return ret;
20393}
20394
20395static __attribute__((format(printf, 2, 3))) int js_parse_error(JSParseState *s, const char *fmt, ...)
20396{
20397 va_list ap;
20398 int ret;
20399
20400 va_start(ap, fmt);
20401 ret = js_parse_error_v(s, s->token.ptr, fmt, ap);
20402 va_end(ap);
20403 return ret;
20404}
20405
20406static int js_parse_expect(JSParseState *s, int tok)
20407{
20408 if (s->token.val != tok) {
20409 /* XXX: dump token correctly in all cases */
20410 return js_parse_error(s, "expecting '%c'", tok);
20411 }
20412 return next_token(s);
20413}
20414
20415static int js_parse_expect_semi(JSParseState *s)
20416{
20417 if (s->token.val != ';') {
20418 /* automatic insertion of ';' */
20419 if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
20420 return 0;
20421 }
20422 return js_parse_error(s, "expecting '%c'", ';');
20423 }
20424 return next_token(s);
20425}
20426
20427static int js_parse_error_reserved_identifier(JSParseState *s)
20428{
20429 char buf1[ATOM_GET_STR_BUF_SIZE];
20430 return js_parse_error(s, "'%s' is a reserved identifier",
20431 JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
20432 s->token.u.ident.atom));
20433}
20434
20435static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
20436{
20437 uint32_t c;
20438 StringBuffer b_s, *b = &b_s;
20439
20440 /* p points to the first byte of the template part */
20441 if (string_buffer_init(s->ctx, b, 32))
20442 goto fail;
20443 for(;;) {
20444 if (p >= s->buf_end)
20445 goto unexpected_eof;
20446 c = *p++;
20447 if (c == '`') {
20448 /* template end part */
20449 break;
20450 }
20451 if (c == '$' && *p == '{') {
20452 /* template start or middle part */
20453 p++;
20454 break;
20455 }
20456 if (c == '\\') {
20457 if (string_buffer_putc8(b, c))
20458 goto fail;
20459 if (p >= s->buf_end)
20460 goto unexpected_eof;
20461 c = *p++;
20462 }
20463 /* newline sequences are normalized as single '\n' bytes */
20464 if (c == '\r') {
20465 if (*p == '\n')
20466 p++;
20467 c = '\n';
20468 }
20469 if (c >= 0x80) {
20470 const uint8_t *p_next;
20471 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20472 if (c > 0x10FFFF) {
20473 js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence");
20474 goto fail;
20475 }
20476 p = p_next;
20477 }
20478 if (string_buffer_putc(b, c))
20479 goto fail;
20480 }
20481 s->token.val = TOK_TEMPLATE;
20482 s->token.u.str.sep = c;
20483 s->token.u.str.str = string_buffer_end(b);
20484 s->buf_ptr = p;
20485 return 0;
20486
20487 unexpected_eof:
20488 js_parse_error(s, "unexpected end of string");
20489 fail:
20490 string_buffer_free(b);
20491 return -1;
20492}
20493
20494static __exception int js_parse_string(JSParseState *s, int sep,
20495 BOOL do_throw, const uint8_t *p,
20496 JSToken *token, const uint8_t **pp)
20497{
20498 int ret;
20499 uint32_t c;
20500 StringBuffer b_s, *b = &b_s;
20501 const uint8_t *p_escape;
20502
20503 /* string */
20504 if (string_buffer_init(s->ctx, b, 32))
20505 goto fail;
20506 for(;;) {
20507 if (p >= s->buf_end)
20508 goto invalid_char;
20509 c = *p;
20510 if (c < 0x20) {
20511 if (!s->cur_func) {
20512 if (do_throw)
20513 js_parse_error_pos(s, p, "invalid character in a JSON string");
20514 goto fail;
20515 }
20516 if (sep == '`') {
20517 if (c == '\r') {
20518 if (p[1] == '\n')
20519 p++;
20520 c = '\n';
20521 }
20522 /* do not update s->line_num */
20523 } else if (c == '\n' || c == '\r')
20524 goto invalid_char;
20525 }
20526 p++;
20527 if (c == sep)
20528 break;
20529 if (c == '$' && *p == '{' && sep == '`') {
20530 /* template start or middle part */
20531 p++;
20532 break;
20533 }
20534 if (c == '\\') {
20535 p_escape = p - 1;
20536 c = *p;
20537 /* XXX: need a specific JSON case to avoid
20538 accepting invalid escapes */
20539 switch(c) {
20540 case '\0':
20541 if (p >= s->buf_end)
20542 goto invalid_char;
20543 p++;
20544 break;
20545 case '\'':
20546 case '\"':
20547 case '\\':
20548 p++;
20549 break;
20550 case '\r': /* accept DOS and MAC newline sequences */
20551 if (p[1] == '\n') {
20552 p++;
20553 }
20554 /* fall thru */
20555 case '\n':
20556 /* ignore escaped newline sequence */
20557 p++;
20558 continue;
20559 default:
20560 if (c >= '0' && c <= '9') {
20561 if (!s->cur_func)
20562 goto invalid_escape; /* JSON case */
20563 if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
20564 goto parse_escape;
20565 if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
20566 p++;
20567 c = '\0';
20568 } else {
20569 if (c >= '8' || sep == '`') {
20570 /* Note: according to ES2021, \8 and \9 are not
20571 accepted in strict mode or in templates. */
20572 goto invalid_escape;
20573 } else {
20574 if (do_throw)
20575 js_parse_error_pos(s, p_escape, "octal escape sequences are not allowed in strict mode");
20576 }
20577 goto fail;
20578 }
20579 } else if (c >= 0x80) {
20580 const uint8_t *p_next;
20581 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20582 if (c > 0x10FFFF) {
20583 goto invalid_utf8;
20584 }
20585 p = p_next;
20586 /* LS or PS are skipped */
20587 if (c == CP_LS || c == CP_PS)
20588 continue;
20589 } else {
20590 parse_escape:
20591 ret = lre_parse_escape(&p, TRUE);
20592 if (ret == -1) {
20593 invalid_escape:
20594 if (do_throw)
20595 js_parse_error_pos(s, p_escape, "malformed escape sequence in string literal");
20596 goto fail;
20597 } else if (ret < 0) {
20598 /* ignore the '\' (could output a warning) */
20599 p++;
20600 } else {
20601 c = ret;
20602 }
20603 }
20604 break;
20605 }
20606 } else if (c >= 0x80) {
20607 const uint8_t *p_next;
20608 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20609 if (c > 0x10FFFF)
20610 goto invalid_utf8;
20611 p = p_next;
20612 }
20613 if (string_buffer_putc(b, c))
20614 goto fail;
20615 }
20616 token->val = TOK_STRING;
20617 token->u.str.sep = c;
20618 token->u.str.str = string_buffer_end(b);
20619 *pp = p;
20620 return 0;
20621
20622 invalid_utf8:
20623 if (do_throw)
20624 js_parse_error(s, "invalid UTF-8 sequence");
20625 goto fail;
20626 invalid_char:
20627 if (do_throw)
20628 js_parse_error(s, "unexpected end of string");
20629 fail:
20630 string_buffer_free(b);
20631 return -1;
20632}
20633
20634static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
20635 return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
20636 !s->token.u.ident.has_escape;
20637}
20638
20639static __exception int js_parse_regexp(JSParseState *s)
20640{
20641 const uint8_t *p;
20642 BOOL in_class;
20643 StringBuffer b_s, *b = &b_s;
20644 StringBuffer b2_s, *b2 = &b2_s;
20645 uint32_t c;
20646
20647 p = s->buf_ptr;
20648 p++;
20649 in_class = FALSE;
20650 if (string_buffer_init(s->ctx, b, 32))
20651 return -1;
20652 if (string_buffer_init(s->ctx, b2, 1))
20653 goto fail;
20654 for(;;) {
20655 if (p >= s->buf_end) {
20656 eof_error:
20657 js_parse_error(s, "unexpected end of regexp");
20658 goto fail;
20659 }
20660 c = *p++;
20661 if (c == '\n' || c == '\r') {
20662 goto eol_error;
20663 } else if (c == '/') {
20664 if (!in_class)
20665 break;
20666 } else if (c == '[') {
20667 in_class = TRUE;
20668 } else if (c == ']') {
20669 /* XXX: incorrect as the first character in a class */
20670 in_class = FALSE;
20671 } else if (c == '\\') {
20672 if (string_buffer_putc8(b, c))
20673 goto fail;
20674 c = *p++;
20675 if (c == '\n' || c == '\r')
20676 goto eol_error;
20677 else if (c == '\0' && p >= s->buf_end)
20678 goto eof_error;
20679 else if (c >= 0x80) {
20680 const uint8_t *p_next;
20681 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20682 if (c > 0x10FFFF) {
20683 goto invalid_utf8;
20684 }
20685 p = p_next;
20686 if (c == CP_LS || c == CP_PS)
20687 goto eol_error;
20688 }
20689 } else if (c >= 0x80) {
20690 const uint8_t *p_next;
20691 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
20692 if (c > 0x10FFFF) {
20693 invalid_utf8:
20694 js_parse_error_pos(s, p - 1, "invalid UTF-8 sequence");
20695 goto fail;
20696 }
20697 /* LS or PS are considered as line terminator */
20698 if (c == CP_LS || c == CP_PS) {
20699 eol_error:
20700 js_parse_error_pos(s, p - 1, "unexpected line terminator in regexp");
20701 goto fail;
20702 }
20703 p = p_next;
20704 }
20705 if (string_buffer_putc(b, c))
20706 goto fail;
20707 }
20708
20709 /* flags */
20710 for(;;) {
20711 const uint8_t *p_next = p;
20712 c = *p_next++;
20713 if (c >= 0x80) {
20714 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
20715 if (c > 0x10FFFF) {
20716 p++;
20717 goto invalid_utf8;
20718 }
20719 }
20720 if (!lre_js_is_ident_next(c))
20721 break;
20722 if (string_buffer_putc(b2, c))
20723 goto fail;
20724 p = p_next;
20725 }
20726
20727 s->token.val = TOK_REGEXP;
20728 s->token.u.regexp.body = string_buffer_end(b);
20729 s->token.u.regexp.flags = string_buffer_end(b2);
20730 s->buf_ptr = p;
20731 return 0;
20732 fail:
20733 string_buffer_free(b);
20734 string_buffer_free(b2);
20735 return -1;
20736}
20737
20738static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
20739 char *static_buf)
20740{
20741 char *buf, *new_buf;
20742 size_t size, new_size;
20743
20744 buf = *pbuf;
20745 size = *psize;
20746 if (size >= (SIZE_MAX / 3) * 2)
20747 new_size = SIZE_MAX;
20748 else
20749 new_size = size + (size >> 1);
20750 if (buf == static_buf) {
20751 new_buf = js_malloc(ctx, new_size);
20752 if (!new_buf)
20753 return -1;
20754 memcpy(new_buf, buf, size);
20755 } else {
20756 new_buf = js_realloc(ctx, buf, new_size);
20757 if (!new_buf)
20758 return -1;
20759 }
20760 *pbuf = new_buf;
20761 *psize = new_size;
20762 return 0;
20763}
20764
20765/* convert a TOK_IDENT to a keyword when needed */
20766static void update_token_ident(JSParseState *s)
20767{
20768 if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
20769 (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
20770 (s->cur_func->js_mode & JS_MODE_STRICT)) ||
20771 (s->token.u.ident.atom == JS_ATOM_yield &&
20772 ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
20773 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20774 !s->cur_func->in_function_body && s->cur_func->parent &&
20775 (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
20776 (s->token.u.ident.atom == JS_ATOM_await &&
20777 (s->is_module ||
20778 (s->cur_func->func_kind & JS_FUNC_ASYNC) ||
20779 s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT ||
20780 (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
20781 !s->cur_func->in_function_body && s->cur_func->parent &&
20782 ((s->cur_func->parent->func_kind & JS_FUNC_ASYNC) ||
20783 s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) {
20784 if (s->token.u.ident.has_escape) {
20785 s->token.u.ident.is_reserved = TRUE;
20786 s->token.val = TOK_IDENT;
20787 } else {
20788 /* The keywords atoms are pre allocated */
20789 s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
20790 }
20791 }
20792}
20793
20794/* if the current token is an identifier or keyword, reparse it
20795 according to the current function type */
20796static void reparse_ident_token(JSParseState *s)
20797{
20798 if (s->token.val == TOK_IDENT ||
20799 (s->token.val >= TOK_FIRST_KEYWORD &&
20800 s->token.val <= TOK_LAST_KEYWORD)) {
20801 s->token.val = TOK_IDENT;
20802 s->token.u.ident.is_reserved = FALSE;
20803 update_token_ident(s);
20804 }
20805}
20806
20807/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
20808static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
20809 BOOL *pident_has_escape, int c, BOOL is_private)
20810{
20811 const uint8_t *p, *p1;
20812 char ident_buf[128], *buf;
20813 size_t ident_size, ident_pos;
20814 JSAtom atom;
20815
20816 p = *pp;
20817 buf = ident_buf;
20818 ident_size = sizeof(ident_buf);
20819 ident_pos = 0;
20820 if (is_private)
20821 buf[ident_pos++] = '#';
20822 for(;;) {
20823 p1 = p;
20824
20825 if (c < 128) {
20826 buf[ident_pos++] = c;
20827 } else {
20828 ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
20829 }
20830 c = *p1++;
20831 if (c == '\\' && *p1 == 'u') {
20832 c = lre_parse_escape(&p1, TRUE);
20833 *pident_has_escape = TRUE;
20834 } else if (c >= 128) {
20835 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
20836 }
20837 if (!lre_js_is_ident_next(c))
20838 break;
20839 p = p1;
20840 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
20841 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
20842 atom = JS_ATOM_NULL;
20843 goto done;
20844 }
20845 }
20846 }
20847 atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
20848 done:
20849 if (unlikely(buf != ident_buf))
20850 js_free(s->ctx, buf);
20851 *pp = p;
20852 return atom;
20853}
20854
20855
20856static __exception int next_token(JSParseState *s)
20857{
20858 const uint8_t *p;
20859 int c;
20860 BOOL ident_has_escape;
20861 JSAtom atom;
20862
20863 if (js_check_stack_overflow(s->ctx->rt, 0)) {
20864 return js_parse_error(s, "stack overflow");
20865 }
20866
20867 free_token(s, &s->token);
20868
20869 p = s->last_ptr = s->buf_ptr;
20870 s->got_lf = FALSE;
20871 redo:
20872 s->token.ptr = p;
20873 c = *p;
20874 switch(c) {
20875 case 0:
20876 if (p >= s->buf_end) {
20877 s->token.val = TOK_EOF;
20878 } else {
20879 goto def_token;
20880 }
20881 break;
20882 case '`':
20883 if (js_parse_template_part(s, p + 1))
20884 goto fail;
20885 p = s->buf_ptr;
20886 break;
20887 case '\'':
20888 case '\"':
20889 if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
20890 goto fail;
20891 break;
20892 case '\r': /* accept DOS and MAC newline sequences */
20893 if (p[1] == '\n') {
20894 p++;
20895 }
20896 /* fall thru */
20897 case '\n':
20898 p++;
20899 line_terminator:
20900 s->got_lf = TRUE;
20901 goto redo;
20902 case '\f':
20903 case '\v':
20904 case ' ':
20905 case '\t':
20906 p++;
20907 goto redo;
20908 case '/':
20909 if (p[1] == '*') {
20910 /* comment */
20911 p += 2;
20912 for(;;) {
20913 if (*p == '\0' && p >= s->buf_end) {
20914 js_parse_error(s, "unexpected end of comment");
20915 goto fail;
20916 }
20917 if (p[0] == '*' && p[1] == '/') {
20918 p += 2;
20919 break;
20920 }
20921 if (*p == '\n' || *p == '\r') {
20922 s->got_lf = TRUE; /* considered as LF for ASI */
20923 p++;
20924 } else if (*p >= 0x80) {
20925 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20926 if (c == CP_LS || c == CP_PS) {
20927 s->got_lf = TRUE; /* considered as LF for ASI */
20928 } else if (c == -1) {
20929 p++; /* skip invalid UTF-8 */
20930 }
20931 } else {
20932 p++;
20933 }
20934 }
20935 goto redo;
20936 } else if (p[1] == '/') {
20937 /* line comment */
20938 p += 2;
20939 skip_line_comment:
20940 for(;;) {
20941 if (*p == '\0' && p >= s->buf_end)
20942 break;
20943 if (*p == '\r' || *p == '\n')
20944 break;
20945 if (*p >= 0x80) {
20946 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
20947 /* LS or PS are considered as line terminator */
20948 if (c == CP_LS || c == CP_PS) {
20949 break;
20950 } else if (c == -1) {
20951 p++; /* skip invalid UTF-8 */
20952 }
20953 } else {
20954 p++;
20955 }
20956 }
20957 goto redo;
20958 } else if (p[1] == '=') {
20959 p += 2;
20960 s->token.val = TOK_DIV_ASSIGN;
20961 } else {
20962 p++;
20963 s->token.val = c;
20964 }
20965 break;
20966 case '\\':
20967 if (p[1] == 'u') {
20968 const uint8_t *p1 = p + 1;
20969 int c1 = lre_parse_escape(&p1, TRUE);
20970 if (c1 >= 0 && lre_js_is_ident_first(c1)) {
20971 c = c1;
20972 p = p1;
20973 ident_has_escape = TRUE;
20974 goto has_ident;
20975 } else {
20976 /* XXX: syntax error? */
20977 }
20978 }
20979 goto def_token;
20980 case 'a': case 'b': case 'c': case 'd':
20981 case 'e': case 'f': case 'g': case 'h':
20982 case 'i': case 'j': case 'k': case 'l':
20983 case 'm': case 'n': case 'o': case 'p':
20984 case 'q': case 'r': case 's': case 't':
20985 case 'u': case 'v': case 'w': case 'x':
20986 case 'y': case 'z':
20987 case 'A': case 'B': case 'C': case 'D':
20988 case 'E': case 'F': case 'G': case 'H':
20989 case 'I': case 'J': case 'K': case 'L':
20990 case 'M': case 'N': case 'O': case 'P':
20991 case 'Q': case 'R': case 'S': case 'T':
20992 case 'U': case 'V': case 'W': case 'X':
20993 case 'Y': case 'Z':
20994 case '_':
20995 case '$':
20996 /* identifier */
20997 p++;
20998 ident_has_escape = FALSE;
20999 has_ident:
21000 atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
21001 if (atom == JS_ATOM_NULL)
21002 goto fail;
21003 s->token.u.ident.atom = atom;
21004 s->token.u.ident.has_escape = ident_has_escape;
21005 s->token.u.ident.is_reserved = FALSE;
21006 s->token.val = TOK_IDENT;
21007 update_token_ident(s);
21008 break;
21009 case '#':
21010 /* private name */
21011 {
21012 const uint8_t *p1;
21013 p++;
21014 p1 = p;
21015 c = *p1++;
21016 if (c == '\\' && *p1 == 'u') {
21017 c = lre_parse_escape(&p1, TRUE);
21018 } else if (c >= 128) {
21019 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
21020 }
21021 if (!lre_js_is_ident_first(c)) {
21022 js_parse_error(s, "invalid first character of private name");
21023 goto fail;
21024 }
21025 p = p1;
21026 ident_has_escape = FALSE; /* not used */
21027 atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
21028 if (atom == JS_ATOM_NULL)
21029 goto fail;
21030 s->token.u.ident.atom = atom;
21031 s->token.val = TOK_PRIVATE_NAME;
21032 }
21033 break;
21034 case '.':
21035 if (p[1] == '.' && p[2] == '.') {
21036 p += 3;
21037 s->token.val = TOK_ELLIPSIS;
21038 break;
21039 }
21040 if (p[1] >= '0' && p[1] <= '9') {
21041 goto parse_number;
21042 } else {
21043 goto def_token;
21044 }
21045 break;
21046 case '0':
21047 /* in strict mode, octal literals are not accepted */
21048 if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
21049 js_parse_error(s, "octal literals are deprecated in strict mode");
21050 goto fail;
21051 }
21052 goto parse_number;
21053 case '1': case '2': case '3': case '4':
21054 case '5': case '6': case '7': case '8':
21055 case '9':
21056 /* number */
21057 parse_number:
21058 {
21059 JSValue ret;
21060 const uint8_t *p1;
21061 int flags;
21062 flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
21063 ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX;
21064 ret = js_atof(s->ctx, (const char *)p, (const char **)&p, 0,
21065 flags);
21066 if (JS_IsException(ret))
21067 goto fail;
21068 /* reject `10instanceof Number` */
21069 if (JS_VALUE_IS_NAN(ret) ||
21070 lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
21071 JS_FreeValue(s->ctx, ret);
21072 js_parse_error(s, "invalid number literal");
21073 goto fail;
21074 }
21075 s->token.val = TOK_NUMBER;
21076 s->token.u.num.val = ret;
21077 }
21078 break;
21079 case '*':
21080 if (p[1] == '=') {
21081 p += 2;
21082 s->token.val = TOK_MUL_ASSIGN;
21083 } else if (p[1] == '*') {
21084 if (p[2] == '=') {
21085 p += 3;
21086 s->token.val = TOK_POW_ASSIGN;
21087 } else {
21088 p += 2;
21089 s->token.val = TOK_POW;
21090 }
21091 } else {
21092 goto def_token;
21093 }
21094 break;
21095 case '%':
21096 if (p[1] == '=') {
21097 p += 2;
21098 s->token.val = TOK_MOD_ASSIGN;
21099 } else {
21100 goto def_token;
21101 }
21102 break;
21103 case '+':
21104 if (p[1] == '=') {
21105 p += 2;
21106 s->token.val = TOK_PLUS_ASSIGN;
21107 } else if (p[1] == '+') {
21108 p += 2;
21109 s->token.val = TOK_INC;
21110 } else {
21111 goto def_token;
21112 }
21113 break;
21114 case '-':
21115 if (p[1] == '=') {
21116 p += 2;
21117 s->token.val = TOK_MINUS_ASSIGN;
21118 } else if (p[1] == '-') {
21119 if (s->allow_html_comments && p[2] == '>' &&
21120 (s->got_lf || s->last_ptr == s->buf_start)) {
21121 /* Annex B: `-->` at beginning of line is an html comment end.
21122 It extends to the end of the line.
21123 */
21124 goto skip_line_comment;
21125 }
21126 p += 2;
21127 s->token.val = TOK_DEC;
21128 } else {
21129 goto def_token;
21130 }
21131 break;
21132 case '<':
21133 if (p[1] == '=') {
21134 p += 2;
21135 s->token.val = TOK_LTE;
21136 } else if (p[1] == '<') {
21137 if (p[2] == '=') {
21138 p += 3;
21139 s->token.val = TOK_SHL_ASSIGN;
21140 } else {
21141 p += 2;
21142 s->token.val = TOK_SHL;
21143 }
21144 } else if (s->allow_html_comments &&
21145 p[1] == '!' && p[2] == '-' && p[3] == '-') {
21146 /* Annex B: handle `<!--` single line html comments */
21147 goto skip_line_comment;
21148 } else {
21149 goto def_token;
21150 }
21151 break;
21152 case '>':
21153 if (p[1] == '=') {
21154 p += 2;
21155 s->token.val = TOK_GTE;
21156 } else if (p[1] == '>') {
21157 if (p[2] == '>') {
21158 if (p[3] == '=') {
21159 p += 4;
21160 s->token.val = TOK_SHR_ASSIGN;
21161 } else {
21162 p += 3;
21163 s->token.val = TOK_SHR;
21164 }
21165 } else if (p[2] == '=') {
21166 p += 3;
21167 s->token.val = TOK_SAR_ASSIGN;
21168 } else {
21169 p += 2;
21170 s->token.val = TOK_SAR;
21171 }
21172 } else {
21173 goto def_token;
21174 }
21175 break;
21176 case '=':
21177 if (p[1] == '=') {
21178 if (p[2] == '=') {
21179 p += 3;
21180 s->token.val = TOK_STRICT_EQ;
21181 } else {
21182 p += 2;
21183 s->token.val = TOK_EQ;
21184 }
21185 } else if (p[1] == '>') {
21186 p += 2;
21187 s->token.val = TOK_ARROW;
21188 } else {
21189 goto def_token;
21190 }
21191 break;
21192 case '!':
21193 if (p[1] == '=') {
21194 if (p[2] == '=') {
21195 p += 3;
21196 s->token.val = TOK_STRICT_NEQ;
21197 } else {
21198 p += 2;
21199 s->token.val = TOK_NEQ;
21200 }
21201 } else {
21202 goto def_token;
21203 }
21204 break;
21205 case '&':
21206 if (p[1] == '=') {
21207 p += 2;
21208 s->token.val = TOK_AND_ASSIGN;
21209 } else if (p[1] == '&') {
21210 if (p[2] == '=') {
21211 p += 3;
21212 s->token.val = TOK_LAND_ASSIGN;
21213 } else {
21214 p += 2;
21215 s->token.val = TOK_LAND;
21216 }
21217 } else {
21218 goto def_token;
21219 }
21220 break;
21221 case '^':
21222 if (p[1] == '=') {
21223 p += 2;
21224 s->token.val = TOK_XOR_ASSIGN;
21225 } else {
21226 goto def_token;
21227 }
21228 break;
21229 case '|':
21230 if (p[1] == '=') {
21231 p += 2;
21232 s->token.val = TOK_OR_ASSIGN;
21233 } else if (p[1] == '|') {
21234 if (p[2] == '=') {
21235 p += 3;
21236 s->token.val = TOK_LOR_ASSIGN;
21237 } else {
21238 p += 2;
21239 s->token.val = TOK_LOR;
21240 }
21241 } else {
21242 goto def_token;
21243 }
21244 break;
21245 case '?':
21246 if (p[1] == '?') {
21247 if (p[2] == '=') {
21248 p += 3;
21249 s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
21250 } else {
21251 p += 2;
21252 s->token.val = TOK_DOUBLE_QUESTION_MARK;
21253 }
21254 } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
21255 p += 2;
21256 s->token.val = TOK_QUESTION_MARK_DOT;
21257 } else {
21258 goto def_token;
21259 }
21260 break;
21261 default:
21262 if (c >= 128) {
21263 /* unicode value */
21264 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21265 switch(c) {
21266 case CP_PS:
21267 case CP_LS:
21268 /* XXX: should avoid incrementing line_number, but
21269 needed to handle HTML comments */
21270 goto line_terminator;
21271 default:
21272 if (lre_is_space(c)) {
21273 goto redo;
21274 } else if (lre_js_is_ident_first(c)) {
21275 ident_has_escape = FALSE;
21276 goto has_ident;
21277 } else {
21278 js_parse_error(s, "unexpected character");
21279 goto fail;
21280 }
21281 }
21282 }
21283 def_token:
21284 s->token.val = c;
21285 p++;
21286 break;
21287 }
21288 s->buf_ptr = p;
21289
21290 // dump_token(s, &s->token);
21291 return 0;
21292
21293 fail:
21294 s->token.val = TOK_ERROR;
21295 return -1;
21296}
21297
21298/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
21299static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
21300{
21301 const uint8_t *p;
21302 char ident_buf[128], *buf;
21303 size_t ident_size, ident_pos;
21304 JSAtom atom;
21305
21306 p = *pp;
21307 buf = ident_buf;
21308 ident_size = sizeof(ident_buf);
21309 ident_pos = 0;
21310 for(;;) {
21311 buf[ident_pos++] = c;
21312 c = *p;
21313 if (c >= 128 || !lre_is_id_continue_byte(c))
21314 break;
21315 p++;
21316 if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
21317 if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
21318 atom = JS_ATOM_NULL;
21319 goto done;
21320 }
21321 }
21322 }
21323 atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
21324 done:
21325 if (unlikely(buf != ident_buf))
21326 js_free(s->ctx, buf);
21327 *pp = p;
21328 return atom;
21329}
21330
21331static __exception int json_next_token(JSParseState *s)
21332{
21333 const uint8_t *p;
21334 int c;
21335 JSAtom atom;
21336
21337 if (js_check_stack_overflow(s->ctx->rt, 0)) {
21338 return js_parse_error(s, "stack overflow");
21339 }
21340
21341 free_token(s, &s->token);
21342
21343 p = s->last_ptr = s->buf_ptr;
21344 redo:
21345 s->token.ptr = p;
21346 c = *p;
21347 switch(c) {
21348 case 0:
21349 if (p >= s->buf_end) {
21350 s->token.val = TOK_EOF;
21351 } else {
21352 goto def_token;
21353 }
21354 break;
21355 case '\'':
21356 if (!s->ext_json) {
21357 /* JSON does not accept single quoted strings */
21358 goto def_token;
21359 }
21360 /* fall through */
21361 case '\"':
21362 if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
21363 goto fail;
21364 break;
21365 case '\r': /* accept DOS and MAC newline sequences */
21366 if (p[1] == '\n') {
21367 p++;
21368 }
21369 /* fall thru */
21370 case '\n':
21371 p++;
21372 goto redo;
21373 case '\f':
21374 case '\v':
21375 if (!s->ext_json) {
21376 /* JSONWhitespace does not match <VT>, nor <FF> */
21377 goto def_token;
21378 }
21379 /* fall through */
21380 case ' ':
21381 case '\t':
21382 p++;
21383 goto redo;
21384 case '/':
21385 if (!s->ext_json) {
21386 /* JSON does not accept comments */
21387 goto def_token;
21388 }
21389 if (p[1] == '*') {
21390 /* comment */
21391 p += 2;
21392 for(;;) {
21393 if (*p == '\0' && p >= s->buf_end) {
21394 js_parse_error(s, "unexpected end of comment");
21395 goto fail;
21396 }
21397 if (p[0] == '*' && p[1] == '/') {
21398 p += 2;
21399 break;
21400 }
21401 if (*p >= 0x80) {
21402 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21403 if (c == -1) {
21404 p++; /* skip invalid UTF-8 */
21405 }
21406 } else {
21407 p++;
21408 }
21409 }
21410 goto redo;
21411 } else if (p[1] == '/') {
21412 /* line comment */
21413 p += 2;
21414 for(;;) {
21415 if (*p == '\0' && p >= s->buf_end)
21416 break;
21417 if (*p == '\r' || *p == '\n')
21418 break;
21419 if (*p >= 0x80) {
21420 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21421 /* LS or PS are considered as line terminator */
21422 if (c == CP_LS || c == CP_PS) {
21423 break;
21424 } else if (c == -1) {
21425 p++; /* skip invalid UTF-8 */
21426 }
21427 } else {
21428 p++;
21429 }
21430 }
21431 goto redo;
21432 } else {
21433 goto def_token;
21434 }
21435 break;
21436 case 'a': case 'b': case 'c': case 'd':
21437 case 'e': case 'f': case 'g': case 'h':
21438 case 'i': case 'j': case 'k': case 'l':
21439 case 'm': case 'n': case 'o': case 'p':
21440 case 'q': case 'r': case 's': case 't':
21441 case 'u': case 'v': case 'w': case 'x':
21442 case 'y': case 'z':
21443 case 'A': case 'B': case 'C': case 'D':
21444 case 'E': case 'F': case 'G': case 'H':
21445 case 'I': case 'J': case 'K': case 'L':
21446 case 'M': case 'N': case 'O': case 'P':
21447 case 'Q': case 'R': case 'S': case 'T':
21448 case 'U': case 'V': case 'W': case 'X':
21449 case 'Y': case 'Z':
21450 case '_':
21451 case '$':
21452 /* identifier : only pure ascii characters are accepted */
21453 p++;
21454 atom = json_parse_ident(s, &p, c);
21455 if (atom == JS_ATOM_NULL)
21456 goto fail;
21457 s->token.u.ident.atom = atom;
21458 s->token.u.ident.has_escape = FALSE;
21459 s->token.u.ident.is_reserved = FALSE;
21460 s->token.val = TOK_IDENT;
21461 break;
21462 case '+':
21463 if (!s->ext_json || !is_digit(p[1]))
21464 goto def_token;
21465 goto parse_number;
21466 case '0':
21467 if (is_digit(p[1]))
21468 goto def_token;
21469 goto parse_number;
21470 case '-':
21471 if (!is_digit(p[1]))
21472 goto def_token;
21473 goto parse_number;
21474 case '1': case '2': case '3': case '4':
21475 case '5': case '6': case '7': case '8':
21476 case '9':
21477 /* number */
21478 parse_number:
21479 {
21480 JSValue ret;
21481 int flags, radix;
21482 if (!s->ext_json) {
21483 flags = 0;
21484 radix = 10;
21485 } else {
21486 flags = ATOD_ACCEPT_BIN_OCT;
21487 radix = 0;
21488 }
21489 ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
21490 flags);
21491 if (JS_IsException(ret))
21492 goto fail;
21493 s->token.val = TOK_NUMBER;
21494 s->token.u.num.val = ret;
21495 }
21496 break;
21497 default:
21498 if (c >= 128) {
21499 js_parse_error(s, "unexpected character");
21500 goto fail;
21501 }
21502 def_token:
21503 s->token.val = c;
21504 p++;
21505 break;
21506 }
21507 s->buf_ptr = p;
21508
21509 // dump_token(s, &s->token);
21510 return 0;
21511
21512 fail:
21513 s->token.val = TOK_ERROR;
21514 return -1;
21515}
21516
21517static int match_identifier(const uint8_t *p, const char *s) {
21518 uint32_t c;
21519 while (*s) {
21520 if ((uint8_t)*s++ != *p++)
21521 return 0;
21522 }
21523 c = *p;
21524 if (c >= 128)
21525 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21526 return !lre_js_is_ident_next(c);
21527}
21528
21529/* simple_next_token() is used to check for the next token in simple cases.
21530 It is only used for ':' and '=>', 'let' or 'function' look-ahead.
21531 (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule()
21532 Whitespace and comments are skipped correctly.
21533 Then the next token is analyzed, only for specific words.
21534 Return values:
21535 - '\n' if !no_line_terminator
21536 - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION
21537 - TOK_IDENT is returned for other identifiers and keywords
21538 - otherwise the next character or unicode codepoint is returned.
21539 */
21540static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
21541{
21542 const uint8_t *p;
21543 uint32_t c;
21544
21545 /* skip spaces and comments */
21546 p = *pp;
21547 for (;;) {
21548 switch(c = *p++) {
21549 case '\r':
21550 case '\n':
21551 if (no_line_terminator)
21552 return '\n';
21553 continue;
21554 case ' ':
21555 case '\t':
21556 case '\v':
21557 case '\f':
21558 continue;
21559 case '/':
21560 if (*p == '/') {
21561 if (no_line_terminator)
21562 return '\n';
21563 while (*p && *p != '\r' && *p != '\n')
21564 p++;
21565 continue;
21566 }
21567 if (*p == '*') {
21568 while (*++p) {
21569 if ((*p == '\r' || *p == '\n') && no_line_terminator)
21570 return '\n';
21571 if (*p == '*' && p[1] == '/') {
21572 p += 2;
21573 break;
21574 }
21575 }
21576 continue;
21577 }
21578 break;
21579 case '=':
21580 if (*p == '>')
21581 return TOK_ARROW;
21582 break;
21583 case 'i':
21584 if (match_identifier(p, "n"))
21585 return TOK_IN;
21586 if (match_identifier(p, "mport")) {
21587 *pp = p + 5;
21588 return TOK_IMPORT;
21589 }
21590 return TOK_IDENT;
21591 case 'o':
21592 if (match_identifier(p, "f"))
21593 return TOK_OF;
21594 return TOK_IDENT;
21595 case 'e':
21596 if (match_identifier(p, "xport"))
21597 return TOK_EXPORT;
21598 return TOK_IDENT;
21599 case 'f':
21600 if (match_identifier(p, "unction"))
21601 return TOK_FUNCTION;
21602 return TOK_IDENT;
21603 case '\\':
21604 if (*p == 'u') {
21605 if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE)))
21606 return TOK_IDENT;
21607 }
21608 break;
21609 default:
21610 if (c >= 128) {
21611 c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p);
21612 if (no_line_terminator && (c == CP_PS || c == CP_LS))
21613 return '\n';
21614 }
21615 if (lre_is_space(c))
21616 continue;
21617 if (lre_js_is_ident_first(c))
21618 return TOK_IDENT;
21619 break;
21620 }
21621 return c;
21622 }
21623}
21624
21625static int peek_token(JSParseState *s, BOOL no_line_terminator)
21626{
21627 const uint8_t *p = s->buf_ptr;
21628 return simple_next_token(&p, no_line_terminator);
21629}
21630
21631static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
21632{
21633 const uint8_t *p = *pp;
21634 int c;
21635
21636 if (p[0] == '#' && p[1] == '!') {
21637 p += 2;
21638 while (p < buf_end) {
21639 if (*p == '\n' || *p == '\r') {
21640 break;
21641 } else if (*p >= 0x80) {
21642 c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
21643 if (c == CP_LS || c == CP_PS) {
21644 break;
21645 } else if (c == -1) {
21646 p++; /* skip invalid UTF-8 */
21647 }
21648 } else {
21649 p++;
21650 }
21651 }
21652 *pp = p;
21653 }
21654}
21655
21656/* return true if 'input' contains the source of a module
21657 (heuristic). 'input' must be a zero terminated.
21658
21659 Heuristic: skip comments and expect 'import' keyword not followed
21660 by '(' or '.' or export keyword.
21661*/
21662BOOL JS_DetectModule(const char *input, size_t input_len)
21663{
21664 const uint8_t *p = (const uint8_t *)input;
21665 int tok;
21666
21667 skip_shebang(&p, p + input_len);
21668 switch(simple_next_token(&p, FALSE)) {
21669 case TOK_IMPORT:
21670 tok = simple_next_token(&p, FALSE);
21671 return (tok != '.' && tok != '(');
21672 case TOK_EXPORT:
21673 return TRUE;
21674 default:
21675 return FALSE;
21676 }
21677}
21678
21679static inline int get_prev_opcode(JSFunctionDef *fd) {
21680 if (fd->last_opcode_pos < 0)
21681 return OP_invalid;
21682 else
21683 return fd->byte_code.buf[fd->last_opcode_pos];
21684}
21685
21686static BOOL js_is_live_code(JSParseState *s) {
21687 switch (get_prev_opcode(s->cur_func)) {
21688 case OP_tail_call:
21689 case OP_tail_call_method:
21690 case OP_return:
21691 case OP_return_undef:
21692 case OP_return_async:
21693 case OP_throw:
21694 case OP_throw_error:
21695 case OP_goto:
21696#if SHORT_OPCODES
21697 case OP_goto8:
21698 case OP_goto16:
21699#endif
21700 case OP_ret:
21701 return FALSE;
21702 default:
21703 return TRUE;
21704 }
21705}
21706
21707static void emit_u8(JSParseState *s, uint8_t val)
21708{
21709 dbuf_putc(&s->cur_func->byte_code, val);
21710}
21711
21712static void emit_u16(JSParseState *s, uint16_t val)
21713{
21714 dbuf_put_u16(&s->cur_func->byte_code, val);
21715}
21716
21717static void emit_u32(JSParseState *s, uint32_t val)
21718{
21719 dbuf_put_u32(&s->cur_func->byte_code, val);
21720}
21721
21722static void emit_source_pos(JSParseState *s, const uint8_t *source_ptr)
21723{
21724 JSFunctionDef *fd = s->cur_func;
21725 DynBuf *bc = &fd->byte_code;
21726
21727 if (unlikely(fd->last_opcode_source_ptr != source_ptr)) {
21728 dbuf_putc(bc, OP_line_num);
21729 dbuf_put_u32(bc, source_ptr - s->buf_start);
21730 fd->last_opcode_source_ptr = source_ptr;
21731 }
21732}
21733
21734static void emit_op(JSParseState *s, uint8_t val)
21735{
21736 JSFunctionDef *fd = s->cur_func;
21737 DynBuf *bc = &fd->byte_code;
21738
21739 fd->last_opcode_pos = bc->size;
21740 dbuf_putc(bc, val);
21741}
21742
21743static void emit_atom(JSParseState *s, JSAtom name)
21744{
21745 emit_u32(s, JS_DupAtom(s->ctx, name));
21746}
21747
21748static int update_label(JSFunctionDef *s, int label, int delta)
21749{
21750 LabelSlot *ls;
21751
21752 assert(label >= 0 && label < s->label_count);
21753 ls = &s->label_slots[label];
21754 ls->ref_count += delta;
21755 assert(ls->ref_count >= 0);
21756 return ls->ref_count;
21757}
21758
21759static int new_label_fd(JSFunctionDef *fd, int label)
21760{
21761 LabelSlot *ls;
21762
21763 if (label < 0) {
21764 if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
21765 sizeof(fd->label_slots[0]),
21766 &fd->label_size, fd->label_count + 1))
21767 return -1;
21768 label = fd->label_count++;
21769 ls = &fd->label_slots[label];
21770 ls->ref_count = 0;
21771 ls->pos = -1;
21772 ls->pos2 = -1;
21773 ls->addr = -1;
21774 ls->first_reloc = NULL;
21775 }
21776 return label;
21777}
21778
21779static int new_label(JSParseState *s)
21780{
21781 return new_label_fd(s->cur_func, -1);
21782}
21783
21784/* don't update the last opcode and don't emit line number info */
21785static void emit_label_raw(JSParseState *s, int label)
21786{
21787 emit_u8(s, OP_label);
21788 emit_u32(s, label);
21789 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
21790}
21791
21792/* return the label ID offset */
21793static int emit_label(JSParseState *s, int label)
21794{
21795 if (label >= 0) {
21796 emit_op(s, OP_label);
21797 emit_u32(s, label);
21798 s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
21799 return s->cur_func->byte_code.size - 4;
21800 } else {
21801 return -1;
21802 }
21803}
21804
21805/* return label or -1 if dead code */
21806static int emit_goto(JSParseState *s, int opcode, int label)
21807{
21808 if (js_is_live_code(s)) {
21809 if (label < 0)
21810 label = new_label(s);
21811 emit_op(s, opcode);
21812 emit_u32(s, label);
21813 s->cur_func->label_slots[label].ref_count++;
21814 return label;
21815 }
21816 return -1;
21817}
21818
21819/* return the constant pool index. 'val' is not duplicated. */
21820static int cpool_add(JSParseState *s, JSValue val)
21821{
21822 JSFunctionDef *fd = s->cur_func;
21823
21824 if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
21825 &fd->cpool_size, fd->cpool_count + 1))
21826 return -1;
21827 fd->cpool[fd->cpool_count++] = val;
21828 return fd->cpool_count - 1;
21829}
21830
21831static __exception int emit_push_const(JSParseState *s, JSValueConst val,
21832 BOOL as_atom)
21833{
21834 int idx;
21835
21836 if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
21837 JSAtom atom;
21838 /* warning: JS_NewAtomStr frees the string value */
21839 JS_DupValue(s->ctx, val);
21840 atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
21841 if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
21842 emit_op(s, OP_push_atom_value);
21843 emit_u32(s, atom);
21844 return 0;
21845 }
21846 }
21847
21848 idx = cpool_add(s, JS_DupValue(s->ctx, val));
21849 if (idx < 0)
21850 return -1;
21851 emit_op(s, OP_push_const);
21852 emit_u32(s, idx);
21853 return 0;
21854}
21855
21856/* return the variable index or -1 if not found,
21857 add ARGUMENT_VAR_OFFSET for argument variables */
21858static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21859{
21860 int i;
21861 for(i = fd->arg_count; i-- > 0;) {
21862 if (fd->args[i].var_name == name)
21863 return i | ARGUMENT_VAR_OFFSET;
21864 }
21865 return -1;
21866}
21867
21868static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
21869{
21870 int i;
21871 for(i = fd->var_count; i-- > 0;) {
21872 if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
21873 return i;
21874 }
21875 return find_arg(ctx, fd, name);
21876}
21877
21878/* find a variable declaration in a given scope */
21879static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
21880 JSAtom name, int scope_level)
21881{
21882 int scope_idx;
21883 for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
21884 scope_idx = fd->vars[scope_idx].scope_next) {
21885 if (fd->vars[scope_idx].scope_level != scope_level)
21886 break;
21887 if (fd->vars[scope_idx].var_name == name)
21888 return scope_idx;
21889 }
21890 return -1;
21891}
21892
21893/* return true if scope == parent_scope or if scope is a child of
21894 parent_scope */
21895static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
21896 int scope, int parent_scope)
21897{
21898 while (scope >= 0) {
21899 if (scope == parent_scope)
21900 return TRUE;
21901 scope = fd->scopes[scope].parent;
21902 }
21903 return FALSE;
21904}
21905
21906/* find a 'var' declaration in the same scope or a child scope */
21907static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
21908 JSAtom name, int scope_level)
21909{
21910 int i;
21911 for(i = 0; i < fd->var_count; i++) {
21912 JSVarDef *vd = &fd->vars[i];
21913 if (vd->var_name == name && vd->scope_level == 0) {
21914 if (is_child_scope(ctx, fd, vd->scope_next,
21915 scope_level))
21916 return i;
21917 }
21918 }
21919 return -1;
21920}
21921
21922
21923static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
21924{
21925 int i;
21926 for(i = 0; i < fd->global_var_count; i++) {
21927 JSGlobalVar *hf = &fd->global_vars[i];
21928 if (hf->var_name == name)
21929 return hf;
21930 }
21931 return NULL;
21932
21933}
21934
21935static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
21936{
21937 JSGlobalVar *hf = find_global_var(fd, name);
21938 if (hf && hf->is_lexical)
21939 return hf;
21940 else
21941 return NULL;
21942}
21943
21944static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
21945 int scope_idx, BOOL check_catch_var)
21946{
21947 while (scope_idx >= 0) {
21948 JSVarDef *vd = &fd->vars[scope_idx];
21949 if (vd->var_name == name &&
21950 (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
21951 check_catch_var)))
21952 return scope_idx;
21953 scope_idx = vd->scope_next;
21954 }
21955
21956 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
21957 if (find_lexical_global_var(fd, name))
21958 return GLOBAL_VAR_OFFSET;
21959 }
21960 return -1;
21961}
21962
21963static int push_scope(JSParseState *s) {
21964 if (s->cur_func) {
21965 JSFunctionDef *fd = s->cur_func;
21966 int scope = fd->scope_count;
21967 /* XXX: should check for scope overflow */
21968 if ((fd->scope_count + 1) > fd->scope_size) {
21969 int new_size;
21970 size_t slack;
21971 JSVarScope *new_buf;
21972 /* XXX: potential arithmetic overflow */
21973 new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
21974 if (fd->scopes == fd->def_scope_array) {
21975 new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
21976 if (!new_buf)
21977 return -1;
21978 memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
21979 } else {
21980 new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
21981 if (!new_buf)
21982 return -1;
21983 }
21984 new_size += slack / sizeof(*new_buf);
21985 fd->scopes = new_buf;
21986 fd->scope_size = new_size;
21987 }
21988 fd->scope_count++;
21989 fd->scopes[scope].parent = fd->scope_level;
21990 fd->scopes[scope].first = fd->scope_first;
21991 emit_op(s, OP_enter_scope);
21992 emit_u16(s, scope);
21993 return fd->scope_level = scope;
21994 }
21995 return 0;
21996}
21997
21998static int get_first_lexical_var(JSFunctionDef *fd, int scope)
21999{
22000 while (scope >= 0) {
22001 int scope_idx = fd->scopes[scope].first;
22002 if (scope_idx >= 0)
22003 return scope_idx;
22004 scope = fd->scopes[scope].parent;
22005 }
22006 return -1;
22007}
22008
22009static void pop_scope(JSParseState *s) {
22010 if (s->cur_func) {
22011 /* disable scoped variables */
22012 JSFunctionDef *fd = s->cur_func;
22013 int scope = fd->scope_level;
22014 emit_op(s, OP_leave_scope);
22015 emit_u16(s, scope);
22016 fd->scope_level = fd->scopes[scope].parent;
22017 fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
22018 }
22019}
22020
22021static void close_scopes(JSParseState *s, int scope, int scope_stop)
22022{
22023 while (scope > scope_stop) {
22024 emit_op(s, OP_leave_scope);
22025 emit_u16(s, scope);
22026 scope = s->cur_func->scopes[scope].parent;
22027 }
22028}
22029
22030/* return the variable index or -1 if error */
22031static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
22032{
22033 JSVarDef *vd;
22034
22035 /* the local variable indexes are currently stored on 16 bits */
22036 if (fd->var_count >= JS_MAX_LOCAL_VARS) {
22037 JS_ThrowInternalError(ctx, "too many local variables");
22038 return -1;
22039 }
22040 if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
22041 &fd->var_size, fd->var_count + 1))
22042 return -1;
22043 vd = &fd->vars[fd->var_count++];
22044 memset(vd, 0, sizeof(*vd));
22045 vd->var_name = JS_DupAtom(ctx, name);
22046 vd->func_pool_idx = -1;
22047 return fd->var_count - 1;
22048}
22049
22050static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
22051 JSVarKindEnum var_kind)
22052{
22053 int idx = add_var(ctx, fd, name);
22054 if (idx >= 0) {
22055 JSVarDef *vd = &fd->vars[idx];
22056 vd->var_kind = var_kind;
22057 vd->scope_level = fd->scope_level;
22058 vd->scope_next = fd->scope_first;
22059 fd->scopes[fd->scope_level].first = idx;
22060 fd->scope_first = idx;
22061 }
22062 return idx;
22063}
22064
22065static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
22066{
22067 int idx = fd->func_var_idx;
22068 if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
22069 fd->func_var_idx = idx;
22070 fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
22071 if (fd->js_mode & JS_MODE_STRICT)
22072 fd->vars[idx].is_const = TRUE;
22073 }
22074 return idx;
22075}
22076
22077static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
22078{
22079 int idx = fd->arguments_var_idx;
22080 if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
22081 fd->arguments_var_idx = idx;
22082 }
22083 return idx;
22084}
22085
22086/* add an argument definition in the argument scope. Only needed when
22087 "eval()" may be called in the argument scope. Return 0 if OK. */
22088static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
22089{
22090 int idx;
22091 if (fd->arguments_arg_idx < 0) {
22092 idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
22093 if (idx < 0) {
22094 /* XXX: the scope links are not fully updated. May be an
22095 issue if there are child scopes of the argument
22096 scope */
22097 idx = add_var(ctx, fd, JS_ATOM_arguments);
22098 if (idx < 0)
22099 return -1;
22100 fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
22101 fd->scopes[ARG_SCOPE_INDEX].first = idx;
22102 fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
22103 fd->vars[idx].is_lexical = TRUE;
22104
22105 fd->arguments_arg_idx = idx;
22106 }
22107 }
22108 return 0;
22109}
22110
22111static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
22112{
22113 JSVarDef *vd;
22114
22115 /* the local variable indexes are currently stored on 16 bits */
22116 if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
22117 JS_ThrowInternalError(ctx, "too many arguments");
22118 return -1;
22119 }
22120 if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
22121 &fd->arg_size, fd->arg_count + 1))
22122 return -1;
22123 vd = &fd->args[fd->arg_count++];
22124 memset(vd, 0, sizeof(*vd));
22125 vd->var_name = JS_DupAtom(ctx, name);
22126 vd->func_pool_idx = -1;
22127 return fd->arg_count - 1;
22128}
22129
22130/* add a global variable definition */
22131static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
22132 JSAtom name)
22133{
22134 JSGlobalVar *hf;
22135
22136 if (js_resize_array(ctx, (void **)&s->global_vars,
22137 sizeof(s->global_vars[0]),
22138 &s->global_var_size, s->global_var_count + 1))
22139 return NULL;
22140 hf = &s->global_vars[s->global_var_count++];
22141 hf->cpool_idx = -1;
22142 hf->force_init = FALSE;
22143 hf->is_lexical = FALSE;
22144 hf->is_const = FALSE;
22145 hf->scope_level = s->scope_level;
22146 hf->var_name = JS_DupAtom(ctx, name);
22147 return hf;
22148}
22149
22150typedef enum {
22151 JS_VAR_DEF_WITH,
22152 JS_VAR_DEF_LET,
22153 JS_VAR_DEF_CONST,
22154 JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
22155 JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
22156 JS_VAR_DEF_CATCH,
22157 JS_VAR_DEF_VAR,
22158} JSVarDefEnum;
22159
22160static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
22161 JSVarDefEnum var_def_type)
22162{
22163 JSContext *ctx = s->ctx;
22164 JSVarDef *vd;
22165 int idx;
22166
22167 switch (var_def_type) {
22168 case JS_VAR_DEF_WITH:
22169 idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
22170 break;
22171
22172 case JS_VAR_DEF_LET:
22173 case JS_VAR_DEF_CONST:
22174 case JS_VAR_DEF_FUNCTION_DECL:
22175 case JS_VAR_DEF_NEW_FUNCTION_DECL:
22176 idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
22177 if (idx >= 0) {
22178 if (idx < GLOBAL_VAR_OFFSET) {
22179 if (fd->vars[idx].scope_level == fd->scope_level) {
22180 /* same scope: in non strict mode, functions
22181 can be redefined (annex B.3.3.4). */
22182 if (!(!(fd->js_mode & JS_MODE_STRICT) &&
22183 var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
22184 fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
22185 goto redef_lex_error;
22186 }
22187 } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
22188 goto redef_lex_error;
22189 }
22190 } else {
22191 if (fd->scope_level == fd->body_scope) {
22192 redef_lex_error:
22193 /* redefining a scoped var in the same scope: error */
22194 return js_parse_error(s, "invalid redefinition of lexical identifier");
22195 }
22196 }
22197 }
22198 if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
22199 var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
22200 fd->scope_level == fd->body_scope &&
22201 find_arg(ctx, fd, name) >= 0) {
22202 /* lexical variable redefines a parameter name */
22203 return js_parse_error(s, "invalid redefinition of parameter name");
22204 }
22205
22206 if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
22207 return js_parse_error(s, "invalid redefinition of a variable");
22208 }
22209
22210 if (fd->is_global_var) {
22211 JSGlobalVar *hf;
22212 hf = find_global_var(fd, name);
22213 if (hf && is_child_scope(ctx, fd, hf->scope_level,
22214 fd->scope_level)) {
22215 return js_parse_error(s, "invalid redefinition of global identifier");
22216 }
22217 }
22218
22219 if (fd->is_eval &&
22220 (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
22221 fd->eval_type == JS_EVAL_TYPE_MODULE) &&
22222 fd->scope_level == fd->body_scope) {
22223 JSGlobalVar *hf;
22224 hf = add_global_var(s->ctx, fd, name);
22225 if (!hf)
22226 return -1;
22227 hf->is_lexical = TRUE;
22228 hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
22229 idx = GLOBAL_VAR_OFFSET;
22230 } else {
22231 JSVarKindEnum var_kind;
22232 if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
22233 var_kind = JS_VAR_FUNCTION_DECL;
22234 else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
22235 var_kind = JS_VAR_NEW_FUNCTION_DECL;
22236 else
22237 var_kind = JS_VAR_NORMAL;
22238 idx = add_scope_var(ctx, fd, name, var_kind);
22239 if (idx >= 0) {
22240 vd = &fd->vars[idx];
22241 vd->is_lexical = 1;
22242 vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
22243 }
22244 }
22245 break;
22246
22247 case JS_VAR_DEF_CATCH:
22248 idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
22249 break;
22250
22251 case JS_VAR_DEF_VAR:
22252 if (find_lexical_decl(ctx, fd, name, fd->scope_first,
22253 FALSE) >= 0) {
22254 invalid_lexical_redefinition:
22255 /* error to redefine a var that inside a lexical scope */
22256 return js_parse_error(s, "invalid redefinition of lexical identifier");
22257 }
22258 if (fd->is_global_var) {
22259 JSGlobalVar *hf;
22260 hf = find_global_var(fd, name);
22261 if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
22262 fd->eval_type == JS_EVAL_TYPE_MODULE) {
22263 goto invalid_lexical_redefinition;
22264 }
22265 hf = add_global_var(s->ctx, fd, name);
22266 if (!hf)
22267 return -1;
22268 idx = GLOBAL_VAR_OFFSET;
22269 } else {
22270 /* if the variable already exists, don't add it again */
22271 idx = find_var(ctx, fd, name);
22272 if (idx >= 0)
22273 break;
22274 idx = add_var(ctx, fd, name);
22275 if (idx >= 0) {
22276 if (name == JS_ATOM_arguments && fd->has_arguments_binding)
22277 fd->arguments_var_idx = idx;
22278 fd->vars[idx].scope_next = fd->scope_level;
22279 }
22280 }
22281 break;
22282 default:
22283 abort();
22284 }
22285 return idx;
22286}
22287
22288/* add a private field variable in the current scope */
22289static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
22290 JSAtom name, JSVarKindEnum var_kind, BOOL is_static)
22291{
22292 JSContext *ctx = s->ctx;
22293 JSVarDef *vd;
22294 int idx;
22295
22296 idx = add_scope_var(ctx, fd, name, var_kind);
22297 if (idx < 0)
22298 return idx;
22299 vd = &fd->vars[idx];
22300 vd->is_lexical = 1;
22301 vd->is_const = 1;
22302 vd->is_static_private = is_static;
22303 return idx;
22304}
22305
22306static __exception int js_parse_expr(JSParseState *s);
22307static __exception int js_parse_function_decl(JSParseState *s,
22308 JSParseFunctionEnum func_type,
22309 JSFunctionKindEnum func_kind,
22310 JSAtom func_name, const uint8_t *ptr);
22311static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
22312static __exception int js_parse_function_decl2(JSParseState *s,
22313 JSParseFunctionEnum func_type,
22314 JSFunctionKindEnum func_kind,
22315 JSAtom func_name,
22316 const uint8_t *ptr,
22317 JSParseExportEnum export_flag,
22318 JSFunctionDef **pfd);
22319static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
22320static __exception int js_parse_assign_expr(JSParseState *s);
22321static __exception int js_parse_unary(JSParseState *s, int parse_flags);
22322static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
22323 JSAtom label_name,
22324 int label_break, int label_cont,
22325 int drop_count);
22326static void pop_break_entry(JSFunctionDef *fd);
22327static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
22328 JSAtom local_name, JSAtom export_name,
22329 JSExportTypeEnum export_type);
22330
22331/* Note: all the fields are already sealed except length */
22332static int seal_template_obj(JSContext *ctx, JSValueConst obj)
22333{
22334 JSObject *p;
22335 JSShapeProperty *prs;
22336
22337 p = JS_VALUE_GET_OBJ(obj);
22338 prs = find_own_property1(p, JS_ATOM_length);
22339 if (prs) {
22340 if (js_update_property_flags(ctx, p, &prs,
22341 prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
22342 return -1;
22343 }
22344 p->extensible = FALSE;
22345 return 0;
22346}
22347
22348static __exception int js_parse_template(JSParseState *s, int call, int *argc)
22349{
22350 JSContext *ctx = s->ctx;
22351 JSValue raw_array, template_object;
22352 JSToken cooked;
22353 int depth, ret;
22354
22355 raw_array = JS_UNDEFINED; /* avoid warning */
22356 template_object = JS_UNDEFINED; /* avoid warning */
22357 if (call) {
22358 /* Create a template object: an array of cooked strings */
22359 /* Create an array of raw strings and store it to the raw property */
22360 template_object = JS_NewArray(ctx);
22361 if (JS_IsException(template_object))
22362 return -1;
22363 // pool_idx = s->cur_func->cpool_count;
22364 ret = emit_push_const(s, template_object, 0);
22365 JS_FreeValue(ctx, template_object);
22366 if (ret)
22367 return -1;
22368 raw_array = JS_NewArray(ctx);
22369 if (JS_IsException(raw_array))
22370 return -1;
22371 if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
22372 raw_array, JS_PROP_THROW) < 0) {
22373 return -1;
22374 }
22375 }
22376
22377 depth = 0;
22378 while (s->token.val == TOK_TEMPLATE) {
22379 const uint8_t *p = s->token.ptr + 1;
22380 cooked = s->token;
22381 if (call) {
22382 if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
22383 JS_DupValue(ctx, s->token.u.str.str),
22384 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22385 return -1;
22386 }
22387 /* re-parse the string with escape sequences but do not throw a
22388 syntax error if it contains invalid sequences
22389 */
22390 if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
22391 cooked.u.str.str = JS_UNDEFINED;
22392 }
22393 if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
22394 cooked.u.str.str,
22395 JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
22396 return -1;
22397 }
22398 } else {
22399 JSString *str;
22400 /* re-parse the string with escape sequences and throw a
22401 syntax error if it contains invalid sequences
22402 */
22403 JS_FreeValue(ctx, s->token.u.str.str);
22404 s->token.u.str.str = JS_UNDEFINED;
22405 if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
22406 return -1;
22407 str = JS_VALUE_GET_STRING(cooked.u.str.str);
22408 if (str->len != 0 || depth == 0) {
22409 ret = emit_push_const(s, cooked.u.str.str, 1);
22410 JS_FreeValue(s->ctx, cooked.u.str.str);
22411 if (ret)
22412 return -1;
22413 if (depth == 0) {
22414 if (s->token.u.str.sep == '`')
22415 goto done1;
22416 emit_op(s, OP_get_field2);
22417 emit_atom(s, JS_ATOM_concat);
22418 }
22419 depth++;
22420 } else {
22421 JS_FreeValue(s->ctx, cooked.u.str.str);
22422 }
22423 }
22424 if (s->token.u.str.sep == '`')
22425 goto done;
22426 if (next_token(s))
22427 return -1;
22428 if (js_parse_expr(s))
22429 return -1;
22430 depth++;
22431 if (s->token.val != '}') {
22432 return js_parse_error(s, "expected '}' after template expression");
22433 }
22434 /* XXX: should convert to string at this stage? */
22435 free_token(s, &s->token);
22436 /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22437 * s->token.ptr are OK) */
22438 s->got_lf = FALSE;
22439 if (js_parse_template_part(s, s->buf_ptr))
22440 return -1;
22441 }
22442 return js_parse_expect(s, TOK_TEMPLATE);
22443
22444 done:
22445 if (call) {
22446 /* Seal the objects */
22447 seal_template_obj(ctx, raw_array);
22448 seal_template_obj(ctx, template_object);
22449 *argc = depth + 1;
22450 } else {
22451 emit_op(s, OP_call_method);
22452 emit_u16(s, depth - 1);
22453 }
22454 done1:
22455 return next_token(s);
22456}
22457
22458
22459#define PROP_TYPE_IDENT 0
22460#define PROP_TYPE_VAR 1
22461#define PROP_TYPE_GET 2
22462#define PROP_TYPE_SET 3
22463#define PROP_TYPE_STAR 4
22464#define PROP_TYPE_ASYNC 5
22465#define PROP_TYPE_ASYNC_STAR 6
22466
22467#define PROP_TYPE_PRIVATE (1 << 4)
22468
22469static BOOL token_is_ident(int tok)
22470{
22471 /* Accept keywords and reserved words as property names */
22472 return (tok == TOK_IDENT ||
22473 (tok >= TOK_FIRST_KEYWORD &&
22474 tok <= TOK_LAST_KEYWORD));
22475}
22476
22477/* if the property is an expression, name = JS_ATOM_NULL */
22478static int __exception js_parse_property_name(JSParseState *s,
22479 JSAtom *pname,
22480 BOOL allow_method, BOOL allow_var,
22481 BOOL allow_private)
22482{
22483 int is_private = 0;
22484 BOOL is_non_reserved_ident;
22485 JSAtom name;
22486 int prop_type;
22487
22488 prop_type = PROP_TYPE_IDENT;
22489 if (allow_method) {
22490 /* if allow_private is true (for class field parsing) and
22491 get/set is following by ';' (or LF with ASI), then it
22492 is a field name */
22493 if ((token_is_pseudo_keyword(s, JS_ATOM_get) ||
22494 token_is_pseudo_keyword(s, JS_ATOM_set)) &&
22495 (!allow_private || peek_token(s, TRUE) != '\n')) {
22496 /* get x(), set x() */
22497 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22498 if (next_token(s))
22499 goto fail1;
22500 if (s->token.val == ':' || s->token.val == ',' ||
22501 s->token.val == '}' || s->token.val == '(' ||
22502 s->token.val == '=' ||
22503 (s->token.val == ';' && allow_private)) {
22504 is_non_reserved_ident = TRUE;
22505 goto ident_found;
22506 }
22507 prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
22508 JS_FreeAtom(s->ctx, name);
22509 } else if (s->token.val == '*') {
22510 if (next_token(s))
22511 goto fail;
22512 prop_type = PROP_TYPE_STAR;
22513 } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
22514 peek_token(s, TRUE) != '\n') {
22515 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22516 if (next_token(s))
22517 goto fail1;
22518 if (s->token.val == ':' || s->token.val == ',' ||
22519 s->token.val == '}' || s->token.val == '(' ||
22520 s->token.val == '=') {
22521 is_non_reserved_ident = TRUE;
22522 goto ident_found;
22523 }
22524 JS_FreeAtom(s->ctx, name);
22525 if (s->token.val == '*') {
22526 if (next_token(s))
22527 goto fail;
22528 prop_type = PROP_TYPE_ASYNC_STAR;
22529 } else {
22530 prop_type = PROP_TYPE_ASYNC;
22531 }
22532 }
22533 }
22534
22535 if (token_is_ident(s->token.val)) {
22536 /* variable can only be a non-reserved identifier */
22537 is_non_reserved_ident =
22538 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
22539 /* keywords and reserved words have a valid atom */
22540 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22541 if (next_token(s))
22542 goto fail1;
22543 ident_found:
22544 if (is_non_reserved_ident &&
22545 prop_type == PROP_TYPE_IDENT && allow_var) {
22546 if (!(s->token.val == ':' ||
22547 (s->token.val == '(' && allow_method))) {
22548 prop_type = PROP_TYPE_VAR;
22549 }
22550 }
22551 } else if (s->token.val == TOK_STRING) {
22552 name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
22553 if (name == JS_ATOM_NULL)
22554 goto fail;
22555 if (next_token(s))
22556 goto fail1;
22557 } else if (s->token.val == TOK_NUMBER) {
22558 JSValue val;
22559 val = s->token.u.num.val;
22560 name = JS_ValueToAtom(s->ctx, val);
22561 if (name == JS_ATOM_NULL)
22562 goto fail;
22563 if (next_token(s))
22564 goto fail1;
22565 } else if (s->token.val == '[') {
22566 if (next_token(s))
22567 goto fail;
22568 if (js_parse_expr(s))
22569 goto fail;
22570 if (js_parse_expect(s, ']'))
22571 goto fail;
22572 name = JS_ATOM_NULL;
22573 } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
22574 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
22575 if (next_token(s))
22576 goto fail1;
22577 is_private = PROP_TYPE_PRIVATE;
22578 } else {
22579 goto invalid_prop;
22580 }
22581 if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
22582 s->token.val != '(') {
22583 JS_FreeAtom(s->ctx, name);
22584 invalid_prop:
22585 js_parse_error(s, "invalid property name");
22586 goto fail;
22587 }
22588 *pname = name;
22589 return prop_type | is_private;
22590 fail1:
22591 JS_FreeAtom(s->ctx, name);
22592 fail:
22593 *pname = JS_ATOM_NULL;
22594 return -1;
22595}
22596
22597typedef struct JSParsePos {
22598 BOOL got_lf;
22599 const uint8_t *ptr;
22600} JSParsePos;
22601
22602static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
22603{
22604 sp->ptr = s->token.ptr;
22605 sp->got_lf = s->got_lf;
22606 return 0;
22607}
22608
22609static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
22610{
22611 s->buf_ptr = sp->ptr;
22612 s->got_lf = sp->got_lf;
22613 return next_token(s);
22614}
22615
22616/* return TRUE if a regexp literal is allowed after this token */
22617static BOOL is_regexp_allowed(int tok)
22618{
22619 switch (tok) {
22620 case TOK_NUMBER:
22621 case TOK_STRING:
22622 case TOK_REGEXP:
22623 case TOK_DEC:
22624 case TOK_INC:
22625 case TOK_NULL:
22626 case TOK_FALSE:
22627 case TOK_TRUE:
22628 case TOK_THIS:
22629 case ')':
22630 case ']':
22631 case '}': /* XXX: regexp may occur after */
22632 case TOK_IDENT:
22633 return FALSE;
22634 default:
22635 return TRUE;
22636 }
22637}
22638
22639#define SKIP_HAS_SEMI (1 << 0)
22640#define SKIP_HAS_ELLIPSIS (1 << 1)
22641#define SKIP_HAS_ASSIGNMENT (1 << 2)
22642
22643static BOOL has_lf_in_range(const uint8_t *p1, const uint8_t *p2)
22644{
22645 const uint8_t *tmp;
22646 if (p1 > p2) {
22647 tmp = p1;
22648 p1 = p2;
22649 p2 = tmp;
22650 }
22651 return (memchr(p1, '\n', p2 - p1) != NULL);
22652}
22653
22654/* XXX: improve speed with early bailout */
22655/* XXX: no longer works if regexps are present. Could use previous
22656 regexp parsing heuristics to handle most cases */
22657static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
22658{
22659 char state[256];
22660 size_t level = 0;
22661 JSParsePos pos;
22662 int last_tok, tok = TOK_EOF;
22663 int c, tok_len, bits = 0;
22664 const uint8_t *last_token_ptr;
22665
22666 /* protect from underflow */
22667 state[level++] = 0;
22668
22669 js_parse_get_pos(s, &pos);
22670 last_tok = 0;
22671 for (;;) {
22672 switch(s->token.val) {
22673 case '(':
22674 case '[':
22675 case '{':
22676 if (level >= sizeof(state))
22677 goto done;
22678 state[level++] = s->token.val;
22679 break;
22680 case ')':
22681 if (state[--level] != '(')
22682 goto done;
22683 break;
22684 case ']':
22685 if (state[--level] != '[')
22686 goto done;
22687 break;
22688 case '}':
22689 c = state[--level];
22690 if (c == '`') {
22691 /* continue the parsing of the template */
22692 free_token(s, &s->token);
22693 /* Resume TOK_TEMPLATE parsing (s->token.line_num and
22694 * s->token.ptr are OK) */
22695 s->got_lf = FALSE;
22696 if (js_parse_template_part(s, s->buf_ptr))
22697 goto done;
22698 goto handle_template;
22699 } else if (c != '{') {
22700 goto done;
22701 }
22702 break;
22703 case TOK_TEMPLATE:
22704 handle_template:
22705 if (s->token.u.str.sep != '`') {
22706 /* '${' inside the template : closing '}' and continue
22707 parsing the template */
22708 if (level >= sizeof(state))
22709 goto done;
22710 state[level++] = '`';
22711 }
22712 break;
22713 case TOK_EOF:
22714 goto done;
22715 case ';':
22716 if (level == 2) {
22717 bits |= SKIP_HAS_SEMI;
22718 }
22719 break;
22720 case TOK_ELLIPSIS:
22721 if (level == 2) {
22722 bits |= SKIP_HAS_ELLIPSIS;
22723 }
22724 break;
22725 case '=':
22726 bits |= SKIP_HAS_ASSIGNMENT;
22727 break;
22728
22729 case TOK_DIV_ASSIGN:
22730 tok_len = 2;
22731 goto parse_regexp;
22732 case '/':
22733 tok_len = 1;
22734 parse_regexp:
22735 if (is_regexp_allowed(last_tok)) {
22736 s->buf_ptr -= tok_len;
22737 if (js_parse_regexp(s)) {
22738 /* XXX: should clear the exception */
22739 goto done;
22740 }
22741 }
22742 break;
22743 }
22744 /* last_tok is only used to recognize regexps */
22745 if (s->token.val == TOK_IDENT &&
22746 (token_is_pseudo_keyword(s, JS_ATOM_of) ||
22747 token_is_pseudo_keyword(s, JS_ATOM_yield))) {
22748 last_tok = TOK_OF;
22749 } else {
22750 last_tok = s->token.val;
22751 }
22752 last_token_ptr = s->token.ptr;
22753 if (next_token(s)) {
22754 /* XXX: should clear the exception generated by next_token() */
22755 break;
22756 }
22757 if (level <= 1) {
22758 tok = s->token.val;
22759 if (token_is_pseudo_keyword(s, JS_ATOM_of))
22760 tok = TOK_OF;
22761 if (no_line_terminator && has_lf_in_range(last_token_ptr, s->token.ptr))
22762 tok = '\n';
22763 break;
22764 }
22765 }
22766 done:
22767 if (pbits) {
22768 *pbits = bits;
22769 }
22770 if (js_parse_seek_token(s, &pos))
22771 return -1;
22772 return tok;
22773}
22774
22775static void set_object_name(JSParseState *s, JSAtom name)
22776{
22777 JSFunctionDef *fd = s->cur_func;
22778 int opcode;
22779
22780 opcode = get_prev_opcode(fd);
22781 if (opcode == OP_set_name) {
22782 /* XXX: should free atom after OP_set_name? */
22783 fd->byte_code.size = fd->last_opcode_pos;
22784 fd->last_opcode_pos = -1;
22785 emit_op(s, OP_set_name);
22786 emit_atom(s, name);
22787 } else if (opcode == OP_set_class_name) {
22788 int define_class_pos;
22789 JSAtom atom;
22790 define_class_pos = fd->last_opcode_pos + 1 -
22791 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22792 assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22793 /* for consistency we free the previous atom which is
22794 JS_ATOM_empty_string */
22795 atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
22796 JS_FreeAtom(s->ctx, atom);
22797 put_u32(fd->byte_code.buf + define_class_pos + 1,
22798 JS_DupAtom(s->ctx, name));
22799 fd->last_opcode_pos = -1;
22800 }
22801}
22802
22803static void set_object_name_computed(JSParseState *s)
22804{
22805 JSFunctionDef *fd = s->cur_func;
22806 int opcode;
22807
22808 opcode = get_prev_opcode(fd);
22809 if (opcode == OP_set_name) {
22810 /* XXX: should free atom after OP_set_name? */
22811 fd->byte_code.size = fd->last_opcode_pos;
22812 fd->last_opcode_pos = -1;
22813 emit_op(s, OP_set_name_computed);
22814 } else if (opcode == OP_set_class_name) {
22815 int define_class_pos;
22816 define_class_pos = fd->last_opcode_pos + 1 -
22817 get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
22818 assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
22819 fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
22820 fd->last_opcode_pos = -1;
22821 }
22822}
22823
22824static __exception int js_parse_object_literal(JSParseState *s)
22825{
22826 JSAtom name = JS_ATOM_NULL;
22827 const uint8_t *start_ptr;
22828 int prop_type;
22829 BOOL has_proto;
22830
22831 if (next_token(s))
22832 goto fail;
22833 /* XXX: add an initial length that will be patched back */
22834 emit_op(s, OP_object);
22835 has_proto = FALSE;
22836 while (s->token.val != '}') {
22837 /* specific case for getter/setter */
22838 start_ptr = s->token.ptr;
22839
22840 if (s->token.val == TOK_ELLIPSIS) {
22841 if (next_token(s))
22842 return -1;
22843 if (js_parse_assign_expr(s))
22844 return -1;
22845 emit_op(s, OP_null); /* dummy excludeList */
22846 emit_op(s, OP_copy_data_properties);
22847 emit_u8(s, 2 | (1 << 2) | (0 << 5));
22848 emit_op(s, OP_drop); /* pop excludeList */
22849 emit_op(s, OP_drop); /* pop src object */
22850 goto next;
22851 }
22852
22853 prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
22854 if (prop_type < 0)
22855 goto fail;
22856
22857 if (prop_type == PROP_TYPE_VAR) {
22858 /* shortcut for x: x */
22859 emit_op(s, OP_scope_get_var);
22860 emit_atom(s, name);
22861 emit_u16(s, s->cur_func->scope_level);
22862 emit_op(s, OP_define_field);
22863 emit_atom(s, name);
22864 } else if (s->token.val == '(') {
22865 BOOL is_getset = (prop_type == PROP_TYPE_GET ||
22866 prop_type == PROP_TYPE_SET);
22867 JSParseFunctionEnum func_type;
22868 JSFunctionKindEnum func_kind;
22869 int op_flags;
22870
22871 func_kind = JS_FUNC_NORMAL;
22872 if (is_getset) {
22873 func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
22874 } else {
22875 func_type = JS_PARSE_FUNC_METHOD;
22876 if (prop_type == PROP_TYPE_STAR)
22877 func_kind = JS_FUNC_GENERATOR;
22878 else if (prop_type == PROP_TYPE_ASYNC)
22879 func_kind = JS_FUNC_ASYNC;
22880 else if (prop_type == PROP_TYPE_ASYNC_STAR)
22881 func_kind = JS_FUNC_ASYNC_GENERATOR;
22882 }
22883 if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
22884 start_ptr))
22885 goto fail;
22886 if (name == JS_ATOM_NULL) {
22887 emit_op(s, OP_define_method_computed);
22888 } else {
22889 emit_op(s, OP_define_method);
22890 emit_atom(s, name);
22891 }
22892 if (is_getset) {
22893 op_flags = OP_DEFINE_METHOD_GETTER +
22894 prop_type - PROP_TYPE_GET;
22895 } else {
22896 op_flags = OP_DEFINE_METHOD_METHOD;
22897 }
22898 emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
22899 } else {
22900 if (name == JS_ATOM_NULL) {
22901 /* must be done before evaluating expr */
22902 emit_op(s, OP_to_propkey);
22903 }
22904 if (js_parse_expect(s, ':'))
22905 goto fail;
22906 if (js_parse_assign_expr(s))
22907 goto fail;
22908 if (name == JS_ATOM_NULL) {
22909 set_object_name_computed(s);
22910 emit_op(s, OP_define_array_el);
22911 emit_op(s, OP_drop);
22912 } else if (name == JS_ATOM___proto__) {
22913 if (has_proto) {
22914 js_parse_error(s, "duplicate __proto__ property name");
22915 goto fail;
22916 }
22917 emit_op(s, OP_set_proto);
22918 has_proto = TRUE;
22919 } else {
22920 set_object_name(s, name);
22921 emit_op(s, OP_define_field);
22922 emit_atom(s, name);
22923 }
22924 }
22925 JS_FreeAtom(s->ctx, name);
22926 next:
22927 name = JS_ATOM_NULL;
22928 if (s->token.val != ',')
22929 break;
22930 if (next_token(s))
22931 goto fail;
22932 }
22933 if (js_parse_expect(s, '}'))
22934 goto fail;
22935 return 0;
22936 fail:
22937 JS_FreeAtom(s->ctx, name);
22938 return -1;
22939}
22940
22941/* allow the 'in' binary operator */
22942#define PF_IN_ACCEPTED (1 << 0)
22943/* allow function calls parsing in js_parse_postfix_expr() */
22944#define PF_POSTFIX_CALL (1 << 1)
22945/* allow the exponentiation operator in js_parse_unary() */
22946#define PF_POW_ALLOWED (1 << 2)
22947/* forbid the exponentiation operator in js_parse_unary() */
22948#define PF_POW_FORBIDDEN (1 << 3)
22949
22950static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
22951static void emit_class_field_init(JSParseState *s);
22952static JSFunctionDef *js_new_function_def(JSContext *ctx,
22953 JSFunctionDef *parent,
22954 BOOL is_eval,
22955 BOOL is_func_expr,
22956 const char *filename,
22957 const uint8_t *source_ptr,
22958 GetLineColCache *get_line_col_cache);
22959static void emit_return(JSParseState *s, BOOL hasval);
22960
22961static __exception int js_parse_left_hand_side_expr(JSParseState *s)
22962{
22963 return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
22964}
22965
22966static __exception int js_parse_class_default_ctor(JSParseState *s,
22967 BOOL has_super,
22968 JSFunctionDef **pfd)
22969{
22970 JSParseFunctionEnum func_type;
22971 JSFunctionDef *fd = s->cur_func;
22972 int idx;
22973
22974 fd = js_new_function_def(s->ctx, fd, FALSE, FALSE, s->filename,
22975 s->token.ptr, &s->get_line_col_cache);
22976 if (!fd)
22977 return -1;
22978
22979 s->cur_func = fd;
22980 fd->has_home_object = TRUE;
22981 fd->super_allowed = TRUE;
22982 fd->has_prototype = FALSE;
22983 fd->has_this_binding = TRUE;
22984 fd->new_target_allowed = TRUE;
22985
22986 push_scope(s); /* enter body scope */
22987 fd->body_scope = fd->scope_level;
22988 if (has_super) {
22989 fd->is_derived_class_constructor = TRUE;
22990 fd->super_call_allowed = TRUE;
22991 fd->arguments_allowed = TRUE;
22992 fd->has_arguments_binding = TRUE;
22993 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
22994 emit_op(s, OP_init_ctor);
22995 // TODO(bnoordhuis) roll into OP_init_ctor
22996 emit_op(s, OP_scope_put_var_init);
22997 emit_atom(s, JS_ATOM_this);
22998 emit_u16(s, 0);
22999 emit_class_field_init(s);
23000 } else {
23001 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
23002 /* error if not invoked as a constructor */
23003 emit_op(s, OP_check_ctor);
23004 emit_class_field_init(s);
23005 }
23006
23007 fd->func_kind = JS_FUNC_NORMAL;
23008 fd->func_type = func_type;
23009 emit_return(s, FALSE);
23010
23011 s->cur_func = fd->parent;
23012 if (pfd)
23013 *pfd = fd;
23014
23015 /* the real object will be set at the end of the compilation */
23016 idx = cpool_add(s, JS_NULL);
23017 fd->parent_cpool_idx = idx;
23018
23019 return 0;
23020}
23021
23022/* find field in the current scope */
23023static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
23024 JSAtom name, int scope_level)
23025{
23026 int idx;
23027 idx = fd->scopes[scope_level].first;
23028 while (idx != -1) {
23029 if (fd->vars[idx].scope_level != scope_level)
23030 break;
23031 if (fd->vars[idx].var_name == name)
23032 return idx;
23033 idx = fd->vars[idx].scope_next;
23034 }
23035 return -1;
23036}
23037
23038/* initialize the class fields, called by the constructor. Note:
23039 super() can be called in an arrow function, so <this> and
23040 <class_fields_init> can be variable references */
23041static void emit_class_field_init(JSParseState *s)
23042{
23043 int label_next;
23044
23045 emit_op(s, OP_scope_get_var);
23046 emit_atom(s, JS_ATOM_class_fields_init);
23047 emit_u16(s, s->cur_func->scope_level);
23048
23049 /* no need to call the class field initializer if not defined */
23050 emit_op(s, OP_dup);
23051 label_next = emit_goto(s, OP_if_false, -1);
23052
23053 emit_op(s, OP_scope_get_var);
23054 emit_atom(s, JS_ATOM_this);
23055 emit_u16(s, 0);
23056
23057 emit_op(s, OP_swap);
23058
23059 emit_op(s, OP_call_method);
23060 emit_u16(s, 0);
23061
23062 emit_label(s, label_next);
23063 emit_op(s, OP_drop);
23064}
23065
23066/* build a private setter function name from the private getter name */
23067static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
23068{
23069 return js_atom_concat_str(ctx, name, "<set>");
23070}
23071
23072typedef struct {
23073 JSFunctionDef *fields_init_fd;
23074 int computed_fields_count;
23075 BOOL need_brand;
23076 int brand_push_pos;
23077 BOOL is_static;
23078} ClassFieldsDef;
23079
23080static __exception int emit_class_init_start(JSParseState *s,
23081 ClassFieldsDef *cf)
23082{
23083 int label_add_brand;
23084
23085 cf->fields_init_fd = js_parse_function_class_fields_init(s);
23086 if (!cf->fields_init_fd)
23087 return -1;
23088
23089 s->cur_func = cf->fields_init_fd;
23090
23091 if (!cf->is_static) {
23092 /* add the brand to the newly created instance */
23093 /* XXX: would be better to add the code only if needed, maybe in a
23094 later pass */
23095 emit_op(s, OP_push_false); /* will be patched later */
23096 cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
23097 label_add_brand = emit_goto(s, OP_if_false, -1);
23098
23099 emit_op(s, OP_scope_get_var);
23100 emit_atom(s, JS_ATOM_this);
23101 emit_u16(s, 0);
23102
23103 emit_op(s, OP_scope_get_var);
23104 emit_atom(s, JS_ATOM_home_object);
23105 emit_u16(s, 0);
23106
23107 emit_op(s, OP_add_brand);
23108
23109 emit_label(s, label_add_brand);
23110 }
23111 s->cur_func = s->cur_func->parent;
23112 return 0;
23113}
23114
23115static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
23116{
23117 int cpool_idx;
23118
23119 s->cur_func = cf->fields_init_fd;
23120 emit_op(s, OP_return_undef);
23121 s->cur_func = s->cur_func->parent;
23122
23123 cpool_idx = cpool_add(s, JS_NULL);
23124 cf->fields_init_fd->parent_cpool_idx = cpool_idx;
23125 emit_op(s, OP_fclosure);
23126 emit_u32(s, cpool_idx);
23127 emit_op(s, OP_set_home_object);
23128}
23129
23130
23131static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
23132 JSParseExportEnum export_flag)
23133{
23134 JSContext *ctx = s->ctx;
23135 JSFunctionDef *fd = s->cur_func;
23136 JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
23137 JSAtom class_var_name = JS_ATOM_NULL;
23138 JSFunctionDef *method_fd, *ctor_fd;
23139 int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
23140 int class_flags = 0, i, define_class_offset;
23141 BOOL is_static, is_private;
23142 const uint8_t *class_start_ptr = s->token.ptr;
23143 const uint8_t *start_ptr;
23144 ClassFieldsDef class_fields[2];
23145
23146 /* classes are parsed and executed in strict mode */
23147 saved_js_mode = fd->js_mode;
23148 fd->js_mode |= JS_MODE_STRICT;
23149 if (next_token(s))
23150 goto fail;
23151 if (s->token.val == TOK_IDENT) {
23152 if (s->token.u.ident.is_reserved) {
23153 js_parse_error_reserved_identifier(s);
23154 goto fail;
23155 }
23156 class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
23157 if (next_token(s))
23158 goto fail;
23159 } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
23160 js_parse_error(s, "class statement requires a name");
23161 goto fail;
23162 }
23163 if (!is_class_expr) {
23164 if (class_name == JS_ATOM_NULL)
23165 class_var_name = JS_ATOM__default_; /* export default */
23166 else
23167 class_var_name = class_name;
23168 class_var_name = JS_DupAtom(ctx, class_var_name);
23169 }
23170
23171 push_scope(s);
23172
23173 if (s->token.val == TOK_EXTENDS) {
23174 class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
23175 if (next_token(s))
23176 goto fail;
23177 if (js_parse_left_hand_side_expr(s))
23178 goto fail;
23179 } else {
23180 emit_op(s, OP_undefined);
23181 }
23182
23183 /* add a 'const' definition for the class name */
23184 if (class_name != JS_ATOM_NULL) {
23185 class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
23186 if (class_name_var_idx < 0)
23187 goto fail;
23188 }
23189
23190 if (js_parse_expect(s, '{'))
23191 goto fail;
23192
23193 /* this scope contains the private fields */
23194 push_scope(s);
23195
23196 emit_op(s, OP_push_const);
23197 ctor_cpool_offset = fd->byte_code.size;
23198 emit_u32(s, 0); /* will be patched at the end of the class parsing */
23199
23200 if (class_name == JS_ATOM_NULL) {
23201 if (class_var_name != JS_ATOM_NULL)
23202 class_name1 = JS_ATOM_default;
23203 else
23204 class_name1 = JS_ATOM_empty_string;
23205 } else {
23206 class_name1 = class_name;
23207 }
23208
23209 emit_op(s, OP_define_class);
23210 emit_atom(s, class_name1);
23211 emit_u8(s, class_flags);
23212 define_class_offset = fd->last_opcode_pos;
23213
23214 for(i = 0; i < 2; i++) {
23215 ClassFieldsDef *cf = &class_fields[i];
23216 cf->fields_init_fd = NULL;
23217 cf->computed_fields_count = 0;
23218 cf->need_brand = FALSE;
23219 cf->is_static = i;
23220 }
23221
23222 ctor_fd = NULL;
23223 while (s->token.val != '}') {
23224 if (s->token.val == ';') {
23225 if (next_token(s))
23226 goto fail;
23227 continue;
23228 }
23229 is_static = FALSE;
23230 if (s->token.val == TOK_STATIC) {
23231 int next = peek_token(s, TRUE);
23232 if (!(next == ';' || next == '}' || next == '(' || next == '='))
23233 is_static = TRUE;
23234 }
23235 prop_type = -1;
23236 if (is_static) {
23237 if (next_token(s))
23238 goto fail;
23239 if (s->token.val == '{') {
23240 ClassFieldsDef *cf = &class_fields[is_static];
23241 JSFunctionDef *init;
23242 if (!cf->fields_init_fd) {
23243 if (emit_class_init_start(s, cf))
23244 goto fail;
23245 }
23246 s->cur_func = cf->fields_init_fd;
23247 /* XXX: could try to avoid creating a new function and
23248 reuse 'fields_init_fd' with a specific 'var'
23249 scope */
23250 // stack is now: <empty>
23251 if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT,
23252 JS_FUNC_NORMAL, JS_ATOM_NULL,
23253 s->token.ptr,
23254 JS_PARSE_EXPORT_NONE, &init) < 0) {
23255 goto fail;
23256 }
23257 // stack is now: fclosure
23258 push_scope(s);
23259 emit_op(s, OP_scope_get_var);
23260 emit_atom(s, JS_ATOM_this);
23261 emit_u16(s, 0);
23262 // stack is now: fclosure this
23263 emit_op(s, OP_swap);
23264 // stack is now: this fclosure
23265 emit_op(s, OP_call_method);
23266 emit_u16(s, 0);
23267 // stack is now: returnvalue
23268 emit_op(s, OP_drop);
23269 // stack is now: <empty>
23270 pop_scope(s);
23271 s->cur_func = s->cur_func->parent;
23272 continue;
23273 }
23274 /* allow "static" field name */
23275 if (s->token.val == ';' || s->token.val == '=') {
23276 is_static = FALSE;
23277 name = JS_DupAtom(ctx, JS_ATOM_static);
23278 prop_type = PROP_TYPE_IDENT;
23279 }
23280 }
23281 if (is_static)
23282 emit_op(s, OP_swap);
23283 start_ptr = s->token.ptr;
23284 if (prop_type < 0) {
23285 prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
23286 if (prop_type < 0)
23287 goto fail;
23288 }
23289 is_private = prop_type & PROP_TYPE_PRIVATE;
23290 prop_type &= ~PROP_TYPE_PRIVATE;
23291
23292 if ((name == JS_ATOM_constructor && !is_static &&
23293 prop_type != PROP_TYPE_IDENT) ||
23294 (name == JS_ATOM_prototype && is_static) ||
23295 name == JS_ATOM_hash_constructor) {
23296 js_parse_error(s, "invalid method name");
23297 goto fail;
23298 }
23299 if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
23300 BOOL is_set = prop_type - PROP_TYPE_GET;
23301 JSFunctionDef *method_fd;
23302
23303 if (is_private) {
23304 int idx, var_kind, is_static1;
23305 idx = find_private_class_field(ctx, fd, name, fd->scope_level);
23306 if (idx >= 0) {
23307 var_kind = fd->vars[idx].var_kind;
23308 is_static1 = fd->vars[idx].is_static_private;
23309 if (var_kind == JS_VAR_PRIVATE_FIELD ||
23310 var_kind == JS_VAR_PRIVATE_METHOD ||
23311 var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
23312 var_kind == (JS_VAR_PRIVATE_GETTER + is_set) ||
23313 (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) &&
23314 is_static != is_static1)) {
23315 goto private_field_already_defined;
23316 }
23317 fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
23318 } else {
23319 if (add_private_class_field(s, fd, name,
23320 JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0)
23321 goto fail;
23322 }
23323 class_fields[is_static].need_brand = TRUE;
23324 }
23325
23326 if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
23327 JS_FUNC_NORMAL, JS_ATOM_NULL,
23328 start_ptr,
23329 JS_PARSE_EXPORT_NONE, &method_fd))
23330 goto fail;
23331 if (is_private) {
23332 method_fd->need_home_object = TRUE; /* needed for brand check */
23333 emit_op(s, OP_set_home_object);
23334 /* XXX: missing function name */
23335 emit_op(s, OP_scope_put_var_init);
23336 if (is_set) {
23337 JSAtom setter_name;
23338 int ret;
23339
23340 setter_name = get_private_setter_name(ctx, name);
23341 if (setter_name == JS_ATOM_NULL)
23342 goto fail;
23343 emit_atom(s, setter_name);
23344 ret = add_private_class_field(s, fd, setter_name,
23345 JS_VAR_PRIVATE_SETTER, is_static);
23346 JS_FreeAtom(ctx, setter_name);
23347 if (ret < 0)
23348 goto fail;
23349 } else {
23350 emit_atom(s, name);
23351 }
23352 emit_u16(s, s->cur_func->scope_level);
23353 } else {
23354 if (name == JS_ATOM_NULL) {
23355 emit_op(s, OP_define_method_computed);
23356 } else {
23357 emit_op(s, OP_define_method);
23358 emit_atom(s, name);
23359 }
23360 emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
23361 }
23362 } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
23363 ClassFieldsDef *cf = &class_fields[is_static];
23364 JSAtom field_var_name = JS_ATOM_NULL;
23365
23366 /* class field */
23367
23368 /* XXX: spec: not consistent with method name checks */
23369 if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
23370 js_parse_error(s, "invalid field name");
23371 goto fail;
23372 }
23373
23374 if (is_private) {
23375 if (find_private_class_field(ctx, fd, name,
23376 fd->scope_level) >= 0) {
23377 goto private_field_already_defined;
23378 }
23379 if (add_private_class_field(s, fd, name,
23380 JS_VAR_PRIVATE_FIELD, is_static) < 0)
23381 goto fail;
23382 emit_op(s, OP_private_symbol);
23383 emit_atom(s, name);
23384 emit_op(s, OP_scope_put_var_init);
23385 emit_atom(s, name);
23386 emit_u16(s, s->cur_func->scope_level);
23387 }
23388
23389 if (!cf->fields_init_fd) {
23390 if (emit_class_init_start(s, cf))
23391 goto fail;
23392 }
23393 if (name == JS_ATOM_NULL ) {
23394 /* save the computed field name into a variable */
23395 field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
23396 if (field_var_name == JS_ATOM_NULL)
23397 goto fail;
23398 if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
23399 JS_FreeAtom(ctx, field_var_name);
23400 goto fail;
23401 }
23402 emit_op(s, OP_to_propkey);
23403 emit_op(s, OP_scope_put_var_init);
23404 emit_atom(s, field_var_name);
23405 emit_u16(s, s->cur_func->scope_level);
23406 }
23407 s->cur_func = cf->fields_init_fd;
23408 emit_op(s, OP_scope_get_var);
23409 emit_atom(s, JS_ATOM_this);
23410 emit_u16(s, 0);
23411
23412 if (name == JS_ATOM_NULL) {
23413 emit_op(s, OP_scope_get_var);
23414 emit_atom(s, field_var_name);
23415 emit_u16(s, s->cur_func->scope_level);
23416 cf->computed_fields_count++;
23417 JS_FreeAtom(ctx, field_var_name);
23418 } else if (is_private) {
23419 emit_op(s, OP_scope_get_var);
23420 emit_atom(s, name);
23421 emit_u16(s, s->cur_func->scope_level);
23422 }
23423
23424 if (s->token.val == '=') {
23425 if (next_token(s))
23426 goto fail;
23427 if (js_parse_assign_expr(s))
23428 goto fail;
23429 } else {
23430 emit_op(s, OP_undefined);
23431 }
23432 if (is_private) {
23433 set_object_name_computed(s);
23434 emit_op(s, OP_define_private_field);
23435 } else if (name == JS_ATOM_NULL) {
23436 set_object_name_computed(s);
23437 emit_op(s, OP_define_array_el);
23438 emit_op(s, OP_drop);
23439 } else {
23440 set_object_name(s, name);
23441 emit_op(s, OP_define_field);
23442 emit_atom(s, name);
23443 }
23444 s->cur_func = s->cur_func->parent;
23445 if (js_parse_expect_semi(s))
23446 goto fail;
23447 } else {
23448 JSParseFunctionEnum func_type;
23449 JSFunctionKindEnum func_kind;
23450
23451 func_type = JS_PARSE_FUNC_METHOD;
23452 func_kind = JS_FUNC_NORMAL;
23453 if (prop_type == PROP_TYPE_STAR) {
23454 func_kind = JS_FUNC_GENERATOR;
23455 } else if (prop_type == PROP_TYPE_ASYNC) {
23456 func_kind = JS_FUNC_ASYNC;
23457 } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
23458 func_kind = JS_FUNC_ASYNC_GENERATOR;
23459 } else if (name == JS_ATOM_constructor && !is_static) {
23460 if (ctor_fd) {
23461 js_parse_error(s, "property constructor appears more than once");
23462 goto fail;
23463 }
23464 if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
23465 func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
23466 else
23467 func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
23468 }
23469 if (is_private) {
23470 class_fields[is_static].need_brand = TRUE;
23471 }
23472 if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, JS_PARSE_EXPORT_NONE, &method_fd))
23473 goto fail;
23474 if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
23475 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
23476 ctor_fd = method_fd;
23477 } else if (is_private) {
23478 method_fd->need_home_object = TRUE; /* needed for brand check */
23479 if (find_private_class_field(ctx, fd, name,
23480 fd->scope_level) >= 0) {
23481 private_field_already_defined:
23482 js_parse_error(s, "private class field is already defined");
23483 goto fail;
23484 }
23485 if (add_private_class_field(s, fd, name,
23486 JS_VAR_PRIVATE_METHOD, is_static) < 0)
23487 goto fail;
23488 emit_op(s, OP_set_home_object);
23489 emit_op(s, OP_set_name);
23490 emit_atom(s, name);
23491 emit_op(s, OP_scope_put_var_init);
23492 emit_atom(s, name);
23493 emit_u16(s, s->cur_func->scope_level);
23494 } else {
23495 if (name == JS_ATOM_NULL) {
23496 emit_op(s, OP_define_method_computed);
23497 } else {
23498 emit_op(s, OP_define_method);
23499 emit_atom(s, name);
23500 }
23501 emit_u8(s, OP_DEFINE_METHOD_METHOD);
23502 }
23503 }
23504 if (is_static)
23505 emit_op(s, OP_swap);
23506 JS_FreeAtom(ctx, name);
23507 name = JS_ATOM_NULL;
23508 }
23509
23510 if (s->token.val != '}') {
23511 js_parse_error(s, "expecting '%c'", '}');
23512 goto fail;
23513 }
23514
23515 if (!ctor_fd) {
23516 if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
23517 goto fail;
23518 }
23519 /* patch the constant pool index for the constructor */
23520 put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
23521
23522 /* store the class source code in the constructor. */
23523 if (!fd->strip_source) {
23524 js_free(ctx, ctor_fd->source);
23525 ctor_fd->source_len = s->buf_ptr - class_start_ptr;
23526 ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
23527 ctor_fd->source_len);
23528 if (!ctor_fd->source)
23529 goto fail;
23530 }
23531
23532 /* consume the '}' */
23533 if (next_token(s))
23534 goto fail;
23535
23536 {
23537 ClassFieldsDef *cf = &class_fields[0];
23538 int var_idx;
23539
23540 if (cf->need_brand) {
23541 /* add a private brand to the prototype */
23542 emit_op(s, OP_dup);
23543 emit_op(s, OP_null);
23544 emit_op(s, OP_swap);
23545 emit_op(s, OP_add_brand);
23546
23547 /* define the brand field in 'this' of the initializer */
23548 if (!cf->fields_init_fd) {
23549 if (emit_class_init_start(s, cf))
23550 goto fail;
23551 }
23552 /* patch the start of the function to enable the
23553 OP_add_brand_instance code */
23554 cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
23555 }
23556
23557 /* store the function to initialize the fields to that it can be
23558 referenced by the constructor */
23559 var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
23560 JS_VAR_DEF_CONST);
23561 if (var_idx < 0)
23562 goto fail;
23563 if (cf->fields_init_fd) {
23564 emit_class_init_end(s, cf);
23565 } else {
23566 emit_op(s, OP_undefined);
23567 }
23568 emit_op(s, OP_scope_put_var_init);
23569 emit_atom(s, JS_ATOM_class_fields_init);
23570 emit_u16(s, s->cur_func->scope_level);
23571 }
23572
23573 /* drop the prototype */
23574 emit_op(s, OP_drop);
23575
23576 if (class_fields[1].need_brand) {
23577 /* add a private brand to the class */
23578 emit_op(s, OP_dup);
23579 emit_op(s, OP_dup);
23580 emit_op(s, OP_add_brand);
23581 }
23582
23583 if (class_name != JS_ATOM_NULL) {
23584 /* store the class name in the scoped class name variable (it
23585 is independent from the class statement variable
23586 definition) */
23587 emit_op(s, OP_dup);
23588 emit_op(s, OP_scope_put_var_init);
23589 emit_atom(s, class_name);
23590 emit_u16(s, fd->scope_level);
23591 }
23592
23593 /* initialize the static fields */
23594 if (class_fields[1].fields_init_fd != NULL) {
23595 ClassFieldsDef *cf = &class_fields[1];
23596 emit_op(s, OP_dup);
23597 emit_class_init_end(s, cf);
23598 emit_op(s, OP_call_method);
23599 emit_u16(s, 0);
23600 emit_op(s, OP_drop);
23601 }
23602
23603 pop_scope(s);
23604 pop_scope(s);
23605
23606 /* the class statements have a block level scope */
23607 if (class_var_name != JS_ATOM_NULL) {
23608 if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
23609 goto fail;
23610 emit_op(s, OP_scope_put_var_init);
23611 emit_atom(s, class_var_name);
23612 emit_u16(s, fd->scope_level);
23613 } else {
23614 if (class_name == JS_ATOM_NULL) {
23615 /* cannot use OP_set_name because the name of the class
23616 must be defined before the static initializers are
23617 executed */
23618 emit_op(s, OP_set_class_name);
23619 emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
23620 }
23621 }
23622
23623 if (export_flag != JS_PARSE_EXPORT_NONE) {
23624 if (!add_export_entry(s, fd->module,
23625 class_var_name,
23626 export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
23627 JS_EXPORT_TYPE_LOCAL))
23628 goto fail;
23629 }
23630
23631 JS_FreeAtom(ctx, class_name);
23632 JS_FreeAtom(ctx, class_var_name);
23633 fd->js_mode = saved_js_mode;
23634 return 0;
23635 fail:
23636 JS_FreeAtom(ctx, name);
23637 JS_FreeAtom(ctx, class_name);
23638 JS_FreeAtom(ctx, class_var_name);
23639 fd->js_mode = saved_js_mode;
23640 return -1;
23641}
23642
23643static __exception int js_parse_array_literal(JSParseState *s)
23644{
23645 uint32_t idx;
23646 BOOL need_length;
23647
23648 if (next_token(s))
23649 return -1;
23650 /* small regular arrays are created on the stack */
23651 idx = 0;
23652 while (s->token.val != ']' && idx < 32) {
23653 if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
23654 break;
23655 if (js_parse_assign_expr(s))
23656 return -1;
23657 idx++;
23658 /* accept trailing comma */
23659 if (s->token.val == ',') {
23660 if (next_token(s))
23661 return -1;
23662 } else
23663 if (s->token.val != ']')
23664 goto done;
23665 }
23666 emit_op(s, OP_array_from);
23667 emit_u16(s, idx);
23668
23669 /* larger arrays and holes are handled with explicit indices */
23670 need_length = FALSE;
23671 while (s->token.val != ']' && idx < 0x7fffffff) {
23672 if (s->token.val == TOK_ELLIPSIS)
23673 break;
23674 need_length = TRUE;
23675 if (s->token.val != ',') {
23676 if (js_parse_assign_expr(s))
23677 return -1;
23678 emit_op(s, OP_define_field);
23679 emit_u32(s, __JS_AtomFromUInt32(idx));
23680 need_length = FALSE;
23681 }
23682 idx++;
23683 /* accept trailing comma */
23684 if (s->token.val == ',') {
23685 if (next_token(s))
23686 return -1;
23687 }
23688 }
23689 if (s->token.val == ']') {
23690 if (need_length) {
23691 /* Set the length: Cannot use OP_define_field because
23692 length is not configurable */
23693 emit_op(s, OP_dup);
23694 emit_op(s, OP_push_i32);
23695 emit_u32(s, idx);
23696 emit_op(s, OP_put_field);
23697 emit_atom(s, JS_ATOM_length);
23698 }
23699 goto done;
23700 }
23701
23702 /* huge arrays and spread elements require a dynamic index on the stack */
23703 emit_op(s, OP_push_i32);
23704 emit_u32(s, idx);
23705
23706 /* stack has array, index */
23707 while (s->token.val != ']') {
23708 if (s->token.val == TOK_ELLIPSIS) {
23709 if (next_token(s))
23710 return -1;
23711 if (js_parse_assign_expr(s))
23712 return -1;
23713#if 1
23714 emit_op(s, OP_append);
23715#else
23716 int label_next, label_done;
23717 label_next = new_label(s);
23718 label_done = new_label(s);
23719 /* enumerate object */
23720 emit_op(s, OP_for_of_start);
23721 emit_op(s, OP_rot5l);
23722 emit_op(s, OP_rot5l);
23723 emit_label(s, label_next);
23724 /* on stack: enum_rec array idx */
23725 emit_op(s, OP_for_of_next);
23726 emit_u8(s, 2);
23727 emit_goto(s, OP_if_true, label_done);
23728 /* append element */
23729 /* enum_rec array idx val -> enum_rec array new_idx */
23730 emit_op(s, OP_define_array_el);
23731 emit_op(s, OP_inc);
23732 emit_goto(s, OP_goto, label_next);
23733 emit_label(s, label_done);
23734 /* close enumeration */
23735 emit_op(s, OP_drop); /* drop undef val */
23736 emit_op(s, OP_nip1); /* drop enum_rec */
23737 emit_op(s, OP_nip1);
23738 emit_op(s, OP_nip1);
23739#endif
23740 } else {
23741 need_length = TRUE;
23742 if (s->token.val != ',') {
23743 if (js_parse_assign_expr(s))
23744 return -1;
23745 /* a idx val */
23746 emit_op(s, OP_define_array_el);
23747 need_length = FALSE;
23748 }
23749 emit_op(s, OP_inc);
23750 }
23751 if (s->token.val != ',')
23752 break;
23753 if (next_token(s))
23754 return -1;
23755 }
23756 if (need_length) {
23757 /* Set the length: cannot use OP_define_field because
23758 length is not configurable */
23759 emit_op(s, OP_dup1); /* array length - array array length */
23760 emit_op(s, OP_put_field);
23761 emit_atom(s, JS_ATOM_length);
23762 } else {
23763 emit_op(s, OP_drop); /* array length - array */
23764 }
23765done:
23766 return js_parse_expect(s, ']');
23767}
23768
23769/* XXX: remove */
23770static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
23771{
23772 /* check if scope chain contains a with statement */
23773 while (s) {
23774 int scope_idx = s->scopes[scope_level].first;
23775 while (scope_idx >= 0) {
23776 JSVarDef *vd = &s->vars[scope_idx];
23777
23778 if (vd->var_name == JS_ATOM__with_)
23779 return TRUE;
23780 scope_idx = vd->scope_next;
23781 }
23782 /* check parent scopes */
23783 scope_level = s->parent_scope_level;
23784 s = s->parent;
23785 }
23786 return FALSE;
23787}
23788
23789static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
23790 JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
23791 int tok)
23792{
23793 JSFunctionDef *fd;
23794 int opcode, scope, label, depth;
23795 JSAtom name;
23796
23797 /* we check the last opcode to get the lvalue type */
23798 fd = s->cur_func;
23799 scope = 0;
23800 name = JS_ATOM_NULL;
23801 label = -1;
23802 depth = 0;
23803 switch(opcode = get_prev_opcode(fd)) {
23804 case OP_scope_get_var:
23805 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23806 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23807 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
23808 (fd->js_mode & JS_MODE_STRICT)) {
23809 return js_parse_error(s, "invalid lvalue in strict mode");
23810 }
23811 if (name == JS_ATOM_this || name == JS_ATOM_new_target)
23812 goto invalid_lvalue;
23813 depth = 2; /* will generate OP_get_ref_value */
23814 break;
23815 case OP_get_field:
23816 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23817 depth = 1;
23818 break;
23819 case OP_scope_get_private_field:
23820 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
23821 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
23822 depth = 1;
23823 break;
23824 case OP_get_array_el:
23825 depth = 2;
23826 break;
23827 case OP_get_super_value:
23828 depth = 3;
23829 break;
23830 default:
23831 invalid_lvalue:
23832 if (tok == TOK_FOR) {
23833 return js_parse_error(s, "invalid for in/of left hand-side");
23834 } else if (tok == TOK_INC || tok == TOK_DEC) {
23835 return js_parse_error(s, "invalid increment/decrement operand");
23836 } else if (tok == '[' || tok == '{') {
23837 return js_parse_error(s, "invalid destructuring target");
23838 } else {
23839 return js_parse_error(s, "invalid assignment left-hand side");
23840 }
23841 }
23842 /* remove the last opcode */
23843 fd->byte_code.size = fd->last_opcode_pos;
23844 fd->last_opcode_pos = -1;
23845
23846 if (keep) {
23847 /* get the value but keep the object/fields on the stack */
23848 switch(opcode) {
23849 case OP_scope_get_var:
23850 label = new_label(s);
23851 emit_op(s, OP_scope_make_ref);
23852 emit_atom(s, name);
23853 emit_u32(s, label);
23854 emit_u16(s, scope);
23855 update_label(fd, label, 1);
23856 emit_op(s, OP_get_ref_value);
23857 opcode = OP_get_ref_value;
23858 break;
23859 case OP_get_field:
23860 emit_op(s, OP_get_field2);
23861 emit_atom(s, name);
23862 break;
23863 case OP_scope_get_private_field:
23864 emit_op(s, OP_scope_get_private_field2);
23865 emit_atom(s, name);
23866 emit_u16(s, scope);
23867 break;
23868 case OP_get_array_el:
23869 /* XXX: replace by a single opcode ? */
23870 emit_op(s, OP_to_propkey2);
23871 emit_op(s, OP_dup2);
23872 emit_op(s, OP_get_array_el);
23873 break;
23874 case OP_get_super_value:
23875 emit_op(s, OP_to_propkey);
23876 emit_op(s, OP_dup3);
23877 emit_op(s, OP_get_super_value);
23878 break;
23879 default:
23880 abort();
23881 }
23882 } else {
23883 switch(opcode) {
23884 case OP_scope_get_var:
23885 label = new_label(s);
23886 emit_op(s, OP_scope_make_ref);
23887 emit_atom(s, name);
23888 emit_u32(s, label);
23889 emit_u16(s, scope);
23890 update_label(fd, label, 1);
23891 opcode = OP_get_ref_value;
23892 break;
23893 default:
23894 break;
23895 }
23896 }
23897
23898 *popcode = opcode;
23899 *pscope = scope;
23900 /* name has refcount for OP_get_field and OP_get_ref_value,
23901 and JS_ATOM_NULL for other opcodes */
23902 *pname = name;
23903 *plabel = label;
23904 if (pdepth)
23905 *pdepth = depth;
23906 return 0;
23907}
23908
23909typedef enum {
23910 PUT_LVALUE_NOKEEP, /* [depth] v -> */
23911 PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
23912 just disable optimizations) */
23913 PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */
23914 PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
23915 PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
23916} PutLValueEnum;
23917
23918/* name has a live reference. 'is_let' is only used with opcode =
23919 OP_scope_get_var which is never generated by get_lvalue(). */
23920static void put_lvalue(JSParseState *s, int opcode, int scope,
23921 JSAtom name, int label, PutLValueEnum special,
23922 BOOL is_let)
23923{
23924 switch(opcode) {
23925 case OP_get_field:
23926 case OP_scope_get_private_field:
23927 /* depth = 1 */
23928 switch(special) {
23929 case PUT_LVALUE_NOKEEP:
23930 case PUT_LVALUE_NOKEEP_DEPTH:
23931 break;
23932 case PUT_LVALUE_KEEP_TOP:
23933 emit_op(s, OP_insert2); /* obj v -> v obj v */
23934 break;
23935 case PUT_LVALUE_KEEP_SECOND:
23936 emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
23937 break;
23938 case PUT_LVALUE_NOKEEP_BOTTOM:
23939 emit_op(s, OP_swap);
23940 break;
23941 default:
23942 abort();
23943 }
23944 break;
23945 case OP_get_array_el:
23946 case OP_get_ref_value:
23947 /* depth = 2 */
23948 if (opcode == OP_get_ref_value) {
23949 JS_FreeAtom(s->ctx, name);
23950 emit_label(s, label);
23951 }
23952 switch(special) {
23953 case PUT_LVALUE_NOKEEP:
23954 emit_op(s, OP_nop); /* will trigger optimization */
23955 break;
23956 case PUT_LVALUE_NOKEEP_DEPTH:
23957 break;
23958 case PUT_LVALUE_KEEP_TOP:
23959 emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
23960 break;
23961 case PUT_LVALUE_KEEP_SECOND:
23962 emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
23963 break;
23964 case PUT_LVALUE_NOKEEP_BOTTOM:
23965 emit_op(s, OP_rot3l);
23966 break;
23967 default:
23968 abort();
23969 }
23970 break;
23971 case OP_get_super_value:
23972 /* depth = 3 */
23973 switch(special) {
23974 case PUT_LVALUE_NOKEEP:
23975 case PUT_LVALUE_NOKEEP_DEPTH:
23976 break;
23977 case PUT_LVALUE_KEEP_TOP:
23978 emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
23979 break;
23980 case PUT_LVALUE_KEEP_SECOND:
23981 emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
23982 break;
23983 case PUT_LVALUE_NOKEEP_BOTTOM:
23984 emit_op(s, OP_rot4l);
23985 break;
23986 default:
23987 abort();
23988 }
23989 break;
23990 default:
23991 break;
23992 }
23993
23994 switch(opcode) {
23995 case OP_scope_get_var: /* val -- */
23996 assert(special == PUT_LVALUE_NOKEEP ||
23997 special == PUT_LVALUE_NOKEEP_DEPTH);
23998 emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
23999 emit_u32(s, name); /* has refcount */
24000 emit_u16(s, scope);
24001 break;
24002 case OP_get_field:
24003 emit_op(s, OP_put_field);
24004 emit_u32(s, name); /* name has refcount */
24005 break;
24006 case OP_scope_get_private_field:
24007 emit_op(s, OP_scope_put_private_field);
24008 emit_u32(s, name); /* name has refcount */
24009 emit_u16(s, scope);
24010 break;
24011 case OP_get_array_el:
24012 emit_op(s, OP_put_array_el);
24013 break;
24014 case OP_get_ref_value:
24015 emit_op(s, OP_put_ref_value);
24016 break;
24017 case OP_get_super_value:
24018 emit_op(s, OP_put_super_value);
24019 break;
24020 default:
24021 abort();
24022 }
24023}
24024
24025static __exception int js_parse_expr_paren(JSParseState *s)
24026{
24027 if (js_parse_expect(s, '('))
24028 return -1;
24029 if (js_parse_expr(s))
24030 return -1;
24031 if (js_parse_expect(s, ')'))
24032 return -1;
24033 return 0;
24034}
24035
24036static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
24037{
24038 char buf[ATOM_GET_STR_BUF_SIZE];
24039 return js_parse_error(s, "unsupported keyword: %s",
24040 JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
24041}
24042
24043static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
24044{
24045 JSFunctionDef *fd = s->cur_func;
24046 JSVarDefEnum var_def_type;
24047
24048 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
24049 return js_parse_error(s, "yield is a reserved identifier");
24050 }
24051 if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
24052 && (fd->js_mode & JS_MODE_STRICT)) {
24053 return js_parse_error(s, "invalid variable name in strict mode");
24054 }
24055 if (name == JS_ATOM_let
24056 && (tok == TOK_LET || tok == TOK_CONST)) {
24057 return js_parse_error(s, "invalid lexical variable name");
24058 }
24059 switch(tok) {
24060 case TOK_LET:
24061 var_def_type = JS_VAR_DEF_LET;
24062 break;
24063 case TOK_CONST:
24064 var_def_type = JS_VAR_DEF_CONST;
24065 break;
24066 case TOK_VAR:
24067 var_def_type = JS_VAR_DEF_VAR;
24068 break;
24069 case TOK_CATCH:
24070 var_def_type = JS_VAR_DEF_CATCH;
24071 break;
24072 default:
24073 abort();
24074 }
24075 if (define_var(s, fd, name, var_def_type) < 0)
24076 return -1;
24077 return 0;
24078}
24079
24080static void js_emit_spread_code(JSParseState *s, int depth)
24081{
24082 int label_rest_next, label_rest_done;
24083
24084 /* XXX: could check if enum object is an actual array and optimize
24085 slice extraction. enumeration record and target array are in a
24086 different order from OP_append case. */
24087 /* enum_rec xxx -- enum_rec xxx array 0 */
24088 emit_op(s, OP_array_from);
24089 emit_u16(s, 0);
24090 emit_op(s, OP_push_i32);
24091 emit_u32(s, 0);
24092 emit_label(s, label_rest_next = new_label(s));
24093 emit_op(s, OP_for_of_next);
24094 emit_u8(s, 2 + depth);
24095 label_rest_done = emit_goto(s, OP_if_true, -1);
24096 /* array idx val -- array idx */
24097 emit_op(s, OP_define_array_el);
24098 emit_op(s, OP_inc);
24099 emit_goto(s, OP_goto, label_rest_next);
24100 emit_label(s, label_rest_done);
24101 /* enum_rec xxx array idx undef -- enum_rec xxx array */
24102 emit_op(s, OP_drop);
24103 emit_op(s, OP_drop);
24104}
24105
24106static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
24107{
24108 /* Check for duplicate parameter names */
24109 JSFunctionDef *fd = s->cur_func;
24110 int i;
24111 for (i = 0; i < fd->arg_count; i++) {
24112 if (fd->args[i].var_name == name)
24113 goto duplicate;
24114 }
24115 for (i = 0; i < fd->var_count; i++) {
24116 if (fd->vars[i].var_name == name)
24117 goto duplicate;
24118 }
24119 return 0;
24120
24121duplicate:
24122 return js_parse_error(s, "duplicate parameter names not allowed in this context");
24123}
24124
24125/* tok = TOK_VAR, TOK_LET or TOK_CONST. Return whether a reference
24126 must be taken to the variable for proper 'with' or global variable
24127 evaluation */
24128/* Note: this function is needed only because variable references are
24129 not yet optimized in destructuring */
24130static BOOL need_var_reference(JSParseState *s, int tok)
24131{
24132 JSFunctionDef *fd = s->cur_func;
24133 if (tok != TOK_VAR)
24134 return FALSE; /* no reference for let/const */
24135 if (fd->js_mode & JS_MODE_STRICT) {
24136 if (!fd->is_global_var)
24137 return FALSE; /* local definitions in strict mode in function or direct eval */
24138 if (s->is_module)
24139 return FALSE; /* in a module global variables are like closure variables */
24140 }
24141 return TRUE;
24142}
24143
24144static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
24145{
24146 JSAtom name;
24147
24148 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
24149 || ((s->cur_func->js_mode & JS_MODE_STRICT) &&
24150 (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
24151 js_parse_error(s, "invalid destructuring target");
24152 return JS_ATOM_NULL;
24153 }
24154 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
24155 if (is_arg && js_parse_check_duplicate_parameter(s, name))
24156 goto fail;
24157 if (next_token(s))
24158 goto fail;
24159
24160 return name;
24161fail:
24162 JS_FreeAtom(s->ctx, name);
24163 return JS_ATOM_NULL;
24164}
24165
24166/* Return -1 if error, 0 if no initializer, 1 if an initializer is
24167 present at the top level. */
24168static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
24169 int hasval, int has_ellipsis,
24170 BOOL allow_initializer, BOOL export_flag)
24171{
24172 int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
24173 int start_addr, assign_addr;
24174 JSAtom prop_name, var_name;
24175 int opcode, scope, tok1, skip_bits;
24176 BOOL has_initializer;
24177
24178 if (has_ellipsis < 0) {
24179 /* pre-parse destructuration target for spread detection */
24180 js_parse_skip_parens_token(s, &skip_bits, FALSE);
24181 has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
24182 }
24183
24184 label_parse = new_label(s);
24185 label_assign = new_label(s);
24186
24187 start_addr = s->cur_func->byte_code.size;
24188 if (hasval) {
24189 /* consume value from the stack */
24190 emit_op(s, OP_dup);
24191 emit_op(s, OP_undefined);
24192 emit_op(s, OP_strict_eq);
24193 emit_goto(s, OP_if_true, label_parse);
24194 emit_label(s, label_assign);
24195 } else {
24196 emit_goto(s, OP_goto, label_parse);
24197 emit_label(s, label_assign);
24198 /* leave value on the stack */
24199 emit_op(s, OP_dup);
24200 }
24201 assign_addr = s->cur_func->byte_code.size;
24202 if (s->token.val == '{') {
24203 if (next_token(s))
24204 return -1;
24205 /* throw an exception if the value cannot be converted to an object */
24206 emit_op(s, OP_to_object);
24207 if (has_ellipsis) {
24208 /* add excludeList on stack just below src object */
24209 emit_op(s, OP_object);
24210 emit_op(s, OP_swap);
24211 }
24212 while (s->token.val != '}') {
24213 int prop_type;
24214 if (s->token.val == TOK_ELLIPSIS) {
24215 if (!has_ellipsis) {
24216 JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
24217 return -1;
24218 }
24219 if (next_token(s))
24220 return -1;
24221 if (tok) {
24222 var_name = js_parse_destructuring_var(s, tok, is_arg);
24223 if (var_name == JS_ATOM_NULL)
24224 return -1;
24225 if (need_var_reference(s, tok)) {
24226 /* Must make a reference for proper `with` semantics */
24227 emit_op(s, OP_scope_get_var);
24228 emit_atom(s, var_name);
24229 emit_u16(s, s->cur_func->scope_level);
24230 JS_FreeAtom(s->ctx, var_name);
24231 goto lvalue0;
24232 } else {
24233 opcode = OP_scope_get_var;
24234 scope = s->cur_func->scope_level;
24235 label_lvalue = -1;
24236 depth_lvalue = 0;
24237 }
24238 } else {
24239 if (js_parse_left_hand_side_expr(s))
24240 return -1;
24241 lvalue0:
24242 if (get_lvalue(s, &opcode, &scope, &var_name,
24243 &label_lvalue, &depth_lvalue, FALSE, '{'))
24244 return -1;
24245 }
24246 if (s->token.val != '}') {
24247 js_parse_error(s, "assignment rest property must be last");
24248 goto var_error;
24249 }
24250 emit_op(s, OP_object); /* target */
24251 emit_op(s, OP_copy_data_properties);
24252 emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
24253 goto set_val;
24254 }
24255 prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
24256 if (prop_type < 0)
24257 return -1;
24258 var_name = JS_ATOM_NULL;
24259 if (prop_type == PROP_TYPE_IDENT) {
24260 if (next_token(s))
24261 goto prop_error;
24262 if ((s->token.val == '[' || s->token.val == '{')
24263 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
24264 tok1 == '=' || tok1 == '}')) {
24265 if (prop_name == JS_ATOM_NULL) {
24266 /* computed property name on stack */
24267 if (has_ellipsis) {
24268 /* define the property in excludeList */
24269 emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
24270 emit_op(s, OP_perm3); /* TOS: src excludeList prop */
24271 emit_op(s, OP_null); /* TOS: src excludeList prop null */
24272 emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
24273 emit_op(s, OP_perm3); /* TOS: excludeList src prop */
24274 }
24275 /* get the computed property from the source object */
24276 emit_op(s, OP_get_array_el2);
24277 } else {
24278 /* named property */
24279 if (has_ellipsis) {
24280 /* define the property in excludeList */
24281 emit_op(s, OP_swap); /* TOS: src excludeList */
24282 emit_op(s, OP_null); /* TOS: src excludeList null */
24283 emit_op(s, OP_define_field); /* TOS: src excludeList */
24284 emit_atom(s, prop_name);
24285 emit_op(s, OP_swap); /* TOS: excludeList src */
24286 }
24287 /* get the named property from the source object */
24288 emit_op(s, OP_get_field2);
24289 emit_u32(s, prop_name);
24290 }
24291 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE, export_flag) < 0)
24292 return -1;
24293 if (s->token.val == '}')
24294 break;
24295 /* accept a trailing comma before the '}' */
24296 if (js_parse_expect(s, ','))
24297 return -1;
24298 continue;
24299 }
24300 if (prop_name == JS_ATOM_NULL) {
24301 emit_op(s, OP_to_propkey2);
24302 if (has_ellipsis) {
24303 /* define the property in excludeList */
24304 emit_op(s, OP_perm3);
24305 emit_op(s, OP_null);
24306 emit_op(s, OP_define_array_el);
24307 emit_op(s, OP_perm3);
24308 }
24309 /* source prop -- source source prop */
24310 emit_op(s, OP_dup1);
24311 } else {
24312 if (has_ellipsis) {
24313 /* define the property in excludeList */
24314 emit_op(s, OP_swap);
24315 emit_op(s, OP_null);
24316 emit_op(s, OP_define_field);
24317 emit_atom(s, prop_name);
24318 emit_op(s, OP_swap);
24319 }
24320 /* source -- source source */
24321 emit_op(s, OP_dup);
24322 }
24323 if (tok) {
24324 var_name = js_parse_destructuring_var(s, tok, is_arg);
24325 if (var_name == JS_ATOM_NULL)
24326 goto prop_error;
24327 if (need_var_reference(s, tok)) {
24328 /* Must make a reference for proper `with` semantics */
24329 emit_op(s, OP_scope_get_var);
24330 emit_atom(s, var_name);
24331 emit_u16(s, s->cur_func->scope_level);
24332 JS_FreeAtom(s->ctx, var_name);
24333 goto lvalue1;
24334 } else {
24335 /* no need to make a reference for let/const */
24336 opcode = OP_scope_get_var;
24337 scope = s->cur_func->scope_level;
24338 label_lvalue = -1;
24339 depth_lvalue = 0;
24340 }
24341 } else {
24342 if (js_parse_left_hand_side_expr(s))
24343 goto prop_error;
24344 lvalue1:
24345 if (get_lvalue(s, &opcode, &scope, &var_name,
24346 &label_lvalue, &depth_lvalue, FALSE, '{'))
24347 goto prop_error;
24348 /* swap ref and lvalue object if any */
24349 if (prop_name == JS_ATOM_NULL) {
24350 switch(depth_lvalue) {
24351 case 1:
24352 /* source prop x -> x source prop */
24353 emit_op(s, OP_rot3r);
24354 break;
24355 case 2:
24356 /* source prop x y -> x y source prop */
24357 emit_op(s, OP_swap2); /* t p2 s p1 */
24358 break;
24359 case 3:
24360 /* source prop x y z -> x y z source prop */
24361 emit_op(s, OP_rot5l);
24362 emit_op(s, OP_rot5l);
24363 break;
24364 }
24365 } else {
24366 switch(depth_lvalue) {
24367 case 1:
24368 /* source x -> x source */
24369 emit_op(s, OP_swap);
24370 break;
24371 case 2:
24372 /* source x y -> x y source */
24373 emit_op(s, OP_rot3l);
24374 break;
24375 case 3:
24376 /* source x y z -> x y z source */
24377 emit_op(s, OP_rot4l);
24378 break;
24379 }
24380 }
24381 }
24382 if (prop_name == JS_ATOM_NULL) {
24383 /* computed property name on stack */
24384 /* XXX: should have OP_get_array_el2x with depth */
24385 /* source prop -- val */
24386 emit_op(s, OP_get_array_el);
24387 } else {
24388 /* named property */
24389 /* XXX: should have OP_get_field2x with depth */
24390 /* source -- val */
24391 emit_op(s, OP_get_field);
24392 emit_u32(s, prop_name);
24393 }
24394 } else {
24395 /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
24396 if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
24397 goto prop_error;
24398 if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
24399 (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
24400 js_parse_error(s, "invalid destructuring target");
24401 goto prop_error;
24402 }
24403 if (has_ellipsis) {
24404 /* define the property in excludeList */
24405 emit_op(s, OP_swap);
24406 emit_op(s, OP_null);
24407 emit_op(s, OP_define_field);
24408 emit_atom(s, prop_name);
24409 emit_op(s, OP_swap);
24410 }
24411 if (!tok || need_var_reference(s, tok)) {
24412 /* generate reference */
24413 /* source -- source source */
24414 emit_op(s, OP_dup);
24415 emit_op(s, OP_scope_get_var);
24416 emit_atom(s, prop_name);
24417 emit_u16(s, s->cur_func->scope_level);
24418 goto lvalue1;
24419 } else {
24420 /* no need to make a reference for let/const */
24421 var_name = JS_DupAtom(s->ctx, prop_name);
24422 opcode = OP_scope_get_var;
24423 scope = s->cur_func->scope_level;
24424 label_lvalue = -1;
24425 depth_lvalue = 0;
24426
24427 /* source -- source val */
24428 emit_op(s, OP_get_field2);
24429 emit_u32(s, prop_name);
24430 }
24431 }
24432 set_val:
24433 if (tok) {
24434 if (js_define_var(s, var_name, tok))
24435 goto var_error;
24436 if (export_flag) {
24437 if (!add_export_entry(s, s->cur_func->module, var_name, var_name,
24438 JS_EXPORT_TYPE_LOCAL))
24439 goto var_error;
24440 }
24441 scope = s->cur_func->scope_level; /* XXX: check */
24442 }
24443 if (s->token.val == '=') { /* handle optional default value */
24444 int label_hasval;
24445 emit_op(s, OP_dup);
24446 emit_op(s, OP_undefined);
24447 emit_op(s, OP_strict_eq);
24448 label_hasval = emit_goto(s, OP_if_false, -1);
24449 if (next_token(s))
24450 goto var_error;
24451 emit_op(s, OP_drop);
24452 if (js_parse_assign_expr(s))
24453 goto var_error;
24454 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24455 set_object_name(s, var_name);
24456 emit_label(s, label_hasval);
24457 }
24458 /* store value into lvalue object */
24459 put_lvalue(s, opcode, scope, var_name, label_lvalue,
24460 PUT_LVALUE_NOKEEP_DEPTH,
24461 (tok == TOK_CONST || tok == TOK_LET));
24462 if (s->token.val == '}')
24463 break;
24464 /* accept a trailing comma before the '}' */
24465 if (js_parse_expect(s, ','))
24466 return -1;
24467 }
24468 /* drop the source object */
24469 emit_op(s, OP_drop);
24470 if (has_ellipsis) {
24471 emit_op(s, OP_drop); /* pop excludeList */
24472 }
24473 if (next_token(s))
24474 return -1;
24475 } else if (s->token.val == '[') {
24476 BOOL has_spread;
24477 int enum_depth;
24478 BlockEnv block_env;
24479
24480 if (next_token(s))
24481 return -1;
24482 /* the block environment is only needed in generators in case
24483 'yield' triggers a 'return' */
24484 push_break_entry(s->cur_func, &block_env,
24485 JS_ATOM_NULL, -1, -1, 2);
24486 block_env.has_iterator = TRUE;
24487 emit_op(s, OP_for_of_start);
24488 has_spread = FALSE;
24489 while (s->token.val != ']') {
24490 /* get the next value */
24491 if (s->token.val == TOK_ELLIPSIS) {
24492 if (next_token(s))
24493 return -1;
24494 if (s->token.val == ',' || s->token.val == ']')
24495 return js_parse_error(s, "missing binding pattern...");
24496 has_spread = TRUE;
24497 }
24498 if (s->token.val == ',') {
24499 /* do nothing, skip the value, has_spread is false */
24500 emit_op(s, OP_for_of_next);
24501 emit_u8(s, 0);
24502 emit_op(s, OP_drop);
24503 emit_op(s, OP_drop);
24504 } else if ((s->token.val == '[' || s->token.val == '{')
24505 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
24506 tok1 == '=' || tok1 == ']')) {
24507 if (has_spread) {
24508 if (tok1 == '=')
24509 return js_parse_error(s, "rest element cannot have a default value");
24510 js_emit_spread_code(s, 0);
24511 } else {
24512 emit_op(s, OP_for_of_next);
24513 emit_u8(s, 0);
24514 emit_op(s, OP_drop);
24515 }
24516 if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
24517 return -1;
24518 } else {
24519 var_name = JS_ATOM_NULL;
24520 if (tok) {
24521 var_name = js_parse_destructuring_var(s, tok, is_arg);
24522 if (var_name == JS_ATOM_NULL)
24523 goto var_error;
24524 if (js_define_var(s, var_name, tok))
24525 goto var_error;
24526 if (need_var_reference(s, tok)) {
24527 /* Must make a reference for proper `with` semantics */
24528 emit_op(s, OP_scope_get_var);
24529 emit_atom(s, var_name);
24530 emit_u16(s, s->cur_func->scope_level);
24531 JS_FreeAtom(s->ctx, var_name);
24532 goto lvalue2;
24533 } else {
24534 /* no need to make a reference for let/const */
24535 opcode = OP_scope_get_var;
24536 scope = s->cur_func->scope_level;
24537 label_lvalue = -1;
24538 enum_depth = 0;
24539 }
24540 } else {
24541 if (js_parse_left_hand_side_expr(s))
24542 return -1;
24543 lvalue2:
24544 if (get_lvalue(s, &opcode, &scope, &var_name,
24545 &label_lvalue, &enum_depth, FALSE, '[')) {
24546 return -1;
24547 }
24548 }
24549 if (has_spread) {
24550 js_emit_spread_code(s, enum_depth);
24551 } else {
24552 emit_op(s, OP_for_of_next);
24553 emit_u8(s, enum_depth);
24554 emit_op(s, OP_drop);
24555 }
24556 if (s->token.val == '=' && !has_spread) {
24557 /* handle optional default value */
24558 int label_hasval;
24559 emit_op(s, OP_dup);
24560 emit_op(s, OP_undefined);
24561 emit_op(s, OP_strict_eq);
24562 label_hasval = emit_goto(s, OP_if_false, -1);
24563 if (next_token(s))
24564 goto var_error;
24565 emit_op(s, OP_drop);
24566 if (js_parse_assign_expr(s))
24567 goto var_error;
24568 if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
24569 set_object_name(s, var_name);
24570 emit_label(s, label_hasval);
24571 }
24572 /* store value into lvalue object */
24573 put_lvalue(s, opcode, scope, var_name,
24574 label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
24575 (tok == TOK_CONST || tok == TOK_LET));
24576 }
24577 if (s->token.val == ']')
24578 break;
24579 if (has_spread)
24580 return js_parse_error(s, "rest element must be the last one");
24581 /* accept a trailing comma before the ']' */
24582 if (js_parse_expect(s, ','))
24583 return -1;
24584 }
24585 /* close iterator object:
24586 if completed, enum_obj has been replaced by undefined */
24587 emit_op(s, OP_iterator_close);
24588 pop_break_entry(s->cur_func);
24589 if (next_token(s))
24590 return -1;
24591 } else {
24592 return js_parse_error(s, "invalid assignment syntax");
24593 }
24594 if (s->token.val == '=' && allow_initializer) {
24595 label_done = emit_goto(s, OP_goto, -1);
24596 if (next_token(s))
24597 return -1;
24598 emit_label(s, label_parse);
24599 if (hasval)
24600 emit_op(s, OP_drop);
24601 if (js_parse_assign_expr(s))
24602 return -1;
24603 emit_goto(s, OP_goto, label_assign);
24604 emit_label(s, label_done);
24605 has_initializer = TRUE;
24606 } else {
24607 /* normally hasval is true except if
24608 js_parse_skip_parens_token() was wrong in the parsing */
24609 // assert(hasval);
24610 if (!hasval) {
24611 js_parse_error(s, "too complicated destructuring expression");
24612 return -1;
24613 }
24614 /* remove test and decrement label ref count */
24615 memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
24616 assign_addr - start_addr);
24617 s->cur_func->label_slots[label_parse].ref_count--;
24618 has_initializer = FALSE;
24619 }
24620 return has_initializer;
24621
24622 prop_error:
24623 JS_FreeAtom(s->ctx, prop_name);
24624 var_error:
24625 JS_FreeAtom(s->ctx, var_name);
24626 return -1;
24627}
24628
24629typedef enum FuncCallType {
24630 FUNC_CALL_NORMAL,
24631 FUNC_CALL_NEW,
24632 FUNC_CALL_SUPER_CTOR,
24633 FUNC_CALL_TEMPLATE,
24634} FuncCallType;
24635
24636static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
24637 int drop_count)
24638{
24639 int label_next, i;
24640 if (*poptional_chaining_label < 0)
24641 *poptional_chaining_label = new_label(s);
24642 /* XXX: could be more efficient with a specific opcode */
24643 emit_op(s, OP_dup);
24644 emit_op(s, OP_is_undefined_or_null);
24645 label_next = emit_goto(s, OP_if_false, -1);
24646 for(i = 0; i < drop_count; i++)
24647 emit_op(s, OP_drop);
24648 emit_op(s, OP_undefined);
24649 emit_goto(s, OP_goto, *poptional_chaining_label);
24650 emit_label(s, label_next);
24651}
24652
24653/* allowed parse_flags: PF_POSTFIX_CALL */
24654static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
24655{
24656 FuncCallType call_type;
24657 int optional_chaining_label;
24658 BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
24659 const uint8_t *op_token_ptr;
24660
24661 call_type = FUNC_CALL_NORMAL;
24662 switch(s->token.val) {
24663 case TOK_NUMBER:
24664 {
24665 JSValue val;
24666 val = s->token.u.num.val;
24667
24668 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
24669 emit_op(s, OP_push_i32);
24670 emit_u32(s, JS_VALUE_GET_INT(val));
24671 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SHORT_BIG_INT) {
24672 int64_t v;
24673 v = JS_VALUE_GET_SHORT_BIG_INT(val);
24674 if (v >= INT32_MIN && v <= INT32_MAX) {
24675 emit_op(s, OP_push_bigint_i32);
24676 emit_u32(s, v);
24677 } else {
24678 goto large_number;
24679 }
24680 } else {
24681 large_number:
24682 if (emit_push_const(s, val, 0) < 0)
24683 return -1;
24684 }
24685 }
24686 if (next_token(s))
24687 return -1;
24688 break;
24689 case TOK_TEMPLATE:
24690 if (js_parse_template(s, 0, NULL))
24691 return -1;
24692 break;
24693 case TOK_STRING:
24694 if (emit_push_const(s, s->token.u.str.str, 1))
24695 return -1;
24696 if (next_token(s))
24697 return -1;
24698 break;
24699
24700 case TOK_DIV_ASSIGN:
24701 s->buf_ptr -= 2;
24702 goto parse_regexp;
24703 case '/':
24704 s->buf_ptr--;
24705 parse_regexp:
24706 {
24707 JSValue str;
24708 int ret;
24709 if (!s->ctx->compile_regexp)
24710 return js_parse_error(s, "RegExp are not supported");
24711 /* the previous token is '/' or '/=', so no need to free */
24712 if (js_parse_regexp(s))
24713 return -1;
24714 ret = emit_push_const(s, s->token.u.regexp.body, 0);
24715 str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
24716 s->token.u.regexp.flags);
24717 if (JS_IsException(str)) {
24718 /* add the line number info */
24719 int line_num, col_num;
24720 line_num = get_line_col(&col_num, s->buf_start, s->token.ptr - s->buf_start);
24721 build_backtrace(s->ctx, s->ctx->rt->current_exception,
24722 s->filename, line_num + 1, col_num + 1, 0);
24723 return -1;
24724 }
24725 ret = emit_push_const(s, str, 0);
24726 JS_FreeValue(s->ctx, str);
24727 if (ret)
24728 return -1;
24729 /* we use a specific opcode to be sure the correct
24730 function is called (otherwise the bytecode would have
24731 to be verified by the RegExp constructor) */
24732 emit_op(s, OP_regexp);
24733 if (next_token(s))
24734 return -1;
24735 }
24736 break;
24737 case '(':
24738 if (js_parse_expr_paren(s))
24739 return -1;
24740 break;
24741 case TOK_FUNCTION:
24742 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24743 JS_FUNC_NORMAL, JS_ATOM_NULL,
24744 s->token.ptr))
24745 return -1;
24746 break;
24747 case TOK_CLASS:
24748 if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
24749 return -1;
24750 break;
24751 case TOK_NULL:
24752 if (next_token(s))
24753 return -1;
24754 emit_op(s, OP_null);
24755 break;
24756 case TOK_THIS:
24757 if (next_token(s))
24758 return -1;
24759 emit_op(s, OP_scope_get_var);
24760 emit_atom(s, JS_ATOM_this);
24761 emit_u16(s, 0);
24762 break;
24763 case TOK_FALSE:
24764 if (next_token(s))
24765 return -1;
24766 emit_op(s, OP_push_false);
24767 break;
24768 case TOK_TRUE:
24769 if (next_token(s))
24770 return -1;
24771 emit_op(s, OP_push_true);
24772 break;
24773 case TOK_IDENT:
24774 {
24775 JSAtom name;
24776 const uint8_t *source_ptr;
24777 if (s->token.u.ident.is_reserved) {
24778 return js_parse_error_reserved_identifier(s);
24779 }
24780 source_ptr = s->token.ptr;
24781 if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
24782 peek_token(s, TRUE) != '\n') {
24783 if (next_token(s))
24784 return -1;
24785 if (s->token.val == TOK_FUNCTION) {
24786 if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
24787 JS_FUNC_ASYNC, JS_ATOM_NULL,
24788 source_ptr))
24789 return -1;
24790 } else {
24791 name = JS_DupAtom(s->ctx, JS_ATOM_async);
24792 goto do_get_var;
24793 }
24794 } else {
24795 if (s->token.u.ident.atom == JS_ATOM_arguments &&
24796 !s->cur_func->arguments_allowed) {
24797 js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
24798 return -1;
24799 }
24800 name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
24801 if (next_token(s)) {
24802 JS_FreeAtom(s->ctx, name);
24803 return -1;
24804 }
24805 do_get_var:
24806 emit_source_pos(s, source_ptr);
24807 emit_op(s, OP_scope_get_var);
24808 emit_u32(s, name);
24809 emit_u16(s, s->cur_func->scope_level);
24810 }
24811 }
24812 break;
24813 case '{':
24814 case '[':
24815 if (s->token.val == '{') {
24816 if (js_parse_object_literal(s))
24817 return -1;
24818 } else {
24819 if (js_parse_array_literal(s))
24820 return -1;
24821 }
24822 break;
24823 case TOK_NEW:
24824 if (next_token(s))
24825 return -1;
24826 if (s->token.val == '.') {
24827 if (next_token(s))
24828 return -1;
24829 if (!token_is_pseudo_keyword(s, JS_ATOM_target))
24830 return js_parse_error(s, "expecting target");
24831 if (!s->cur_func->new_target_allowed)
24832 return js_parse_error(s, "new.target only allowed within functions");
24833 if (next_token(s))
24834 return -1;
24835 emit_op(s, OP_scope_get_var);
24836 emit_atom(s, JS_ATOM_new_target);
24837 emit_u16(s, 0);
24838 } else {
24839 if (js_parse_postfix_expr(s, 0))
24840 return -1;
24841 accept_lparen = TRUE;
24842 if (s->token.val != '(') {
24843 /* new operator on an object */
24844 emit_source_pos(s, s->token.ptr);
24845 emit_op(s, OP_dup);
24846 emit_op(s, OP_call_constructor);
24847 emit_u16(s, 0);
24848 } else {
24849 call_type = FUNC_CALL_NEW;
24850 }
24851 }
24852 break;
24853 case TOK_SUPER:
24854 if (next_token(s))
24855 return -1;
24856 if (s->token.val == '(') {
24857 if (!s->cur_func->super_call_allowed)
24858 return js_parse_error(s, "super() is only valid in a derived class constructor");
24859 call_type = FUNC_CALL_SUPER_CTOR;
24860 } else if (s->token.val == '.' || s->token.val == '[') {
24861 if (!s->cur_func->super_allowed)
24862 return js_parse_error(s, "'super' is only valid in a method");
24863 emit_op(s, OP_scope_get_var);
24864 emit_atom(s, JS_ATOM_this);
24865 emit_u16(s, 0);
24866 emit_op(s, OP_scope_get_var);
24867 emit_atom(s, JS_ATOM_home_object);
24868 emit_u16(s, 0);
24869 emit_op(s, OP_get_super);
24870 } else {
24871 return js_parse_error(s, "invalid use of 'super'");
24872 }
24873 break;
24874 case TOK_IMPORT:
24875 if (next_token(s))
24876 return -1;
24877 if (s->token.val == '.') {
24878 if (next_token(s))
24879 return -1;
24880 if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
24881 return js_parse_error(s, "meta expected");
24882 if (!s->is_module)
24883 return js_parse_error(s, "import.meta only valid in module code");
24884 if (next_token(s))
24885 return -1;
24886 emit_op(s, OP_special_object);
24887 emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
24888 } else {
24889 if (js_parse_expect(s, '('))
24890 return -1;
24891 if (!accept_lparen)
24892 return js_parse_error(s, "invalid use of 'import()'");
24893 if (js_parse_assign_expr(s))
24894 return -1;
24895 if (js_parse_expect(s, ')'))
24896 return -1;
24897 emit_op(s, OP_import);
24898 }
24899 break;
24900 default:
24901 return js_parse_error(s, "unexpected token in expression: '%.*s'",
24902 (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
24903 }
24904
24905 optional_chaining_label = -1;
24906 for(;;) {
24907 JSFunctionDef *fd = s->cur_func;
24908 BOOL has_optional_chain = FALSE;
24909
24910 if (s->token.val == TOK_QUESTION_MARK_DOT) {
24911 op_token_ptr = s->token.ptr;
24912 /* optional chaining */
24913 if (next_token(s))
24914 return -1;
24915 has_optional_chain = TRUE;
24916 if (s->token.val == '(' && accept_lparen) {
24917 goto parse_func_call;
24918 } else if (s->token.val == '[') {
24919 goto parse_array_access;
24920 } else {
24921 goto parse_property;
24922 }
24923 } else if (s->token.val == TOK_TEMPLATE &&
24924 call_type == FUNC_CALL_NORMAL) {
24925 if (optional_chaining_label >= 0) {
24926 return js_parse_error(s, "template literal cannot appear in an optional chain");
24927 }
24928 call_type = FUNC_CALL_TEMPLATE;
24929 op_token_ptr = s->token.ptr; /* XXX: check if right position */
24930 goto parse_func_call2;
24931 } else if (s->token.val == '(' && accept_lparen) {
24932 int opcode, arg_count, drop_count;
24933
24934 /* function call */
24935 parse_func_call:
24936 op_token_ptr = s->token.ptr;
24937 if (next_token(s))
24938 return -1;
24939
24940 if (call_type == FUNC_CALL_NORMAL) {
24941 parse_func_call2:
24942 switch(opcode = get_prev_opcode(fd)) {
24943 case OP_get_field:
24944 /* keep the object on the stack */
24945 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
24946 drop_count = 2;
24947 break;
24948 case OP_get_field_opt_chain:
24949 {
24950 int opt_chain_label, next_label;
24951 opt_chain_label = get_u32(fd->byte_code.buf +
24952 fd->last_opcode_pos + 1 + 4 + 1);
24953 /* keep the object on the stack */
24954 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
24955 fd->byte_code.size = fd->last_opcode_pos + 1 + 4;
24956 next_label = emit_goto(s, OP_goto, -1);
24957 emit_label(s, opt_chain_label);
24958 /* need an additional undefined value for the
24959 case where the optional field does not
24960 exists */
24961 emit_op(s, OP_undefined);
24962 emit_label(s, next_label);
24963 drop_count = 2;
24964 opcode = OP_get_field;
24965 }
24966 break;
24967 case OP_scope_get_private_field:
24968 /* keep the object on the stack */
24969 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
24970 drop_count = 2;
24971 break;
24972 case OP_get_array_el:
24973 /* keep the object on the stack */
24974 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
24975 drop_count = 2;
24976 break;
24977 case OP_get_array_el_opt_chain:
24978 {
24979 int opt_chain_label, next_label;
24980 opt_chain_label = get_u32(fd->byte_code.buf +
24981 fd->last_opcode_pos + 1 + 1);
24982 /* keep the object on the stack */
24983 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
24984 fd->byte_code.size = fd->last_opcode_pos + 1;
24985 next_label = emit_goto(s, OP_goto, -1);
24986 emit_label(s, opt_chain_label);
24987 /* need an additional undefined value for the
24988 case where the optional field does not
24989 exists */
24990 emit_op(s, OP_undefined);
24991 emit_label(s, next_label);
24992 drop_count = 2;
24993 opcode = OP_get_array_el;
24994 }
24995 break;
24996 case OP_scope_get_var:
24997 {
24998 JSAtom name;
24999 int scope;
25000 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25001 scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
25002 if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
25003 /* direct 'eval' */
25004 opcode = OP_eval;
25005 } else {
25006 /* verify if function name resolves to a simple
25007 get_loc/get_arg: a function call inside a `with`
25008 statement can resolve to a method call of the
25009 `with` context object
25010 */
25011 /* XXX: always generate the OP_scope_get_ref
25012 and remove it in variable resolution
25013 pass ? */
25014 if (has_with_scope(fd, scope)) {
25015 opcode = OP_scope_get_ref;
25016 fd->byte_code.buf[fd->last_opcode_pos] = opcode;
25017 }
25018 }
25019 drop_count = 1;
25020 }
25021 break;
25022 case OP_get_super_value:
25023 fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
25024 /* on stack: this func_obj */
25025 opcode = OP_get_array_el;
25026 drop_count = 2;
25027 break;
25028 default:
25029 opcode = OP_invalid;
25030 drop_count = 1;
25031 break;
25032 }
25033 if (has_optional_chain) {
25034 optional_chain_test(s, &optional_chaining_label,
25035 drop_count);
25036 }
25037 } else {
25038 opcode = OP_invalid;
25039 }
25040
25041 if (call_type == FUNC_CALL_TEMPLATE) {
25042 if (js_parse_template(s, 1, &arg_count))
25043 return -1;
25044 goto emit_func_call;
25045 } else if (call_type == FUNC_CALL_SUPER_CTOR) {
25046 emit_op(s, OP_scope_get_var);
25047 emit_atom(s, JS_ATOM_this_active_func);
25048 emit_u16(s, 0);
25049
25050 emit_op(s, OP_get_super);
25051
25052 emit_op(s, OP_scope_get_var);
25053 emit_atom(s, JS_ATOM_new_target);
25054 emit_u16(s, 0);
25055 } else if (call_type == FUNC_CALL_NEW) {
25056 emit_op(s, OP_dup); /* new.target = function */
25057 }
25058
25059 /* parse arguments */
25060 arg_count = 0;
25061 while (s->token.val != ')') {
25062 if (arg_count >= 65535) {
25063 return js_parse_error(s, "Too many call arguments");
25064 }
25065 if (s->token.val == TOK_ELLIPSIS)
25066 break;
25067 if (js_parse_assign_expr(s))
25068 return -1;
25069 arg_count++;
25070 if (s->token.val == ')')
25071 break;
25072 /* accept a trailing comma before the ')' */
25073 if (js_parse_expect(s, ','))
25074 return -1;
25075 }
25076 if (s->token.val == TOK_ELLIPSIS) {
25077 emit_op(s, OP_array_from);
25078 emit_u16(s, arg_count);
25079 emit_op(s, OP_push_i32);
25080 emit_u32(s, arg_count);
25081
25082 /* on stack: array idx */
25083 while (s->token.val != ')') {
25084 if (s->token.val == TOK_ELLIPSIS) {
25085 if (next_token(s))
25086 return -1;
25087 if (js_parse_assign_expr(s))
25088 return -1;
25089#if 1
25090 /* XXX: could pass is_last indicator? */
25091 emit_op(s, OP_append);
25092#else
25093 int label_next, label_done;
25094 label_next = new_label(s);
25095 label_done = new_label(s);
25096 /* push enumerate object below array/idx pair */
25097 emit_op(s, OP_for_of_start);
25098 emit_op(s, OP_rot5l);
25099 emit_op(s, OP_rot5l);
25100 emit_label(s, label_next);
25101 /* on stack: enum_rec array idx */
25102 emit_op(s, OP_for_of_next);
25103 emit_u8(s, 2);
25104 emit_goto(s, OP_if_true, label_done);
25105 /* append element */
25106 /* enum_rec array idx val -> enum_rec array new_idx */
25107 emit_op(s, OP_define_array_el);
25108 emit_op(s, OP_inc);
25109 emit_goto(s, OP_goto, label_next);
25110 emit_label(s, label_done);
25111 /* close enumeration, drop enum_rec and idx */
25112 emit_op(s, OP_drop); /* drop undef */
25113 emit_op(s, OP_nip1); /* drop enum_rec */
25114 emit_op(s, OP_nip1);
25115 emit_op(s, OP_nip1);
25116#endif
25117 } else {
25118 if (js_parse_assign_expr(s))
25119 return -1;
25120 /* array idx val */
25121 emit_op(s, OP_define_array_el);
25122 emit_op(s, OP_inc);
25123 }
25124 if (s->token.val == ')')
25125 break;
25126 /* accept a trailing comma before the ')' */
25127 if (js_parse_expect(s, ','))
25128 return -1;
25129 }
25130 if (next_token(s))
25131 return -1;
25132 /* drop the index */
25133 emit_op(s, OP_drop);
25134
25135 emit_source_pos(s, op_token_ptr);
25136 /* apply function call */
25137 switch(opcode) {
25138 case OP_get_field:
25139 case OP_scope_get_private_field:
25140 case OP_get_array_el:
25141 case OP_scope_get_ref:
25142 /* obj func array -> func obj array */
25143 emit_op(s, OP_perm3);
25144 emit_op(s, OP_apply);
25145 emit_u16(s, call_type == FUNC_CALL_NEW);
25146 break;
25147 case OP_eval:
25148 emit_op(s, OP_apply_eval);
25149 emit_u16(s, fd->scope_level);
25150 fd->has_eval_call = TRUE;
25151 break;
25152 default:
25153 if (call_type == FUNC_CALL_SUPER_CTOR) {
25154 emit_op(s, OP_apply);
25155 emit_u16(s, 1);
25156 /* set the 'this' value */
25157 emit_op(s, OP_dup);
25158 emit_op(s, OP_scope_put_var_init);
25159 emit_atom(s, JS_ATOM_this);
25160 emit_u16(s, 0);
25161
25162 emit_class_field_init(s);
25163 } else if (call_type == FUNC_CALL_NEW) {
25164 /* obj func array -> func obj array */
25165 emit_op(s, OP_perm3);
25166 emit_op(s, OP_apply);
25167 emit_u16(s, 1);
25168 } else {
25169 /* func array -> func undef array */
25170 emit_op(s, OP_undefined);
25171 emit_op(s, OP_swap);
25172 emit_op(s, OP_apply);
25173 emit_u16(s, 0);
25174 }
25175 break;
25176 }
25177 } else {
25178 if (next_token(s))
25179 return -1;
25180 emit_func_call:
25181 emit_source_pos(s, op_token_ptr);
25182 switch(opcode) {
25183 case OP_get_field:
25184 case OP_scope_get_private_field:
25185 case OP_get_array_el:
25186 case OP_scope_get_ref:
25187 emit_op(s, OP_call_method);
25188 emit_u16(s, arg_count);
25189 break;
25190 case OP_eval:
25191 emit_op(s, OP_eval);
25192 emit_u16(s, arg_count);
25193 emit_u16(s, fd->scope_level);
25194 fd->has_eval_call = TRUE;
25195 break;
25196 default:
25197 if (call_type == FUNC_CALL_SUPER_CTOR) {
25198 emit_op(s, OP_call_constructor);
25199 emit_u16(s, arg_count);
25200
25201 /* set the 'this' value */
25202 emit_op(s, OP_dup);
25203 emit_op(s, OP_scope_put_var_init);
25204 emit_atom(s, JS_ATOM_this);
25205 emit_u16(s, 0);
25206
25207 emit_class_field_init(s);
25208 } else if (call_type == FUNC_CALL_NEW) {
25209 emit_op(s, OP_call_constructor);
25210 emit_u16(s, arg_count);
25211 } else {
25212 emit_op(s, OP_call);
25213 emit_u16(s, arg_count);
25214 }
25215 break;
25216 }
25217 }
25218 call_type = FUNC_CALL_NORMAL;
25219 } else if (s->token.val == '.') {
25220 op_token_ptr = s->token.ptr;
25221 if (next_token(s))
25222 return -1;
25223 parse_property:
25224 emit_source_pos(s, op_token_ptr);
25225 if (s->token.val == TOK_PRIVATE_NAME) {
25226 /* private class field */
25227 if (get_prev_opcode(fd) == OP_get_super) {
25228 return js_parse_error(s, "private class field forbidden after super");
25229 }
25230 if (has_optional_chain) {
25231 optional_chain_test(s, &optional_chaining_label, 1);
25232 }
25233 emit_op(s, OP_scope_get_private_field);
25234 emit_atom(s, s->token.u.ident.atom);
25235 emit_u16(s, s->cur_func->scope_level);
25236 } else {
25237 if (!token_is_ident(s->token.val)) {
25238 return js_parse_error(s, "expecting field name");
25239 }
25240 if (get_prev_opcode(fd) == OP_get_super) {
25241 JSValue val;
25242 int ret;
25243 val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
25244 ret = emit_push_const(s, val, 1);
25245 JS_FreeValue(s->ctx, val);
25246 if (ret)
25247 return -1;
25248 emit_op(s, OP_get_super_value);
25249 } else {
25250 if (has_optional_chain) {
25251 optional_chain_test(s, &optional_chaining_label, 1);
25252 }
25253 emit_op(s, OP_get_field);
25254 emit_atom(s, s->token.u.ident.atom);
25255 }
25256 }
25257 if (next_token(s))
25258 return -1;
25259 } else if (s->token.val == '[') {
25260 int prev_op;
25261 op_token_ptr = s->token.ptr;
25262 parse_array_access:
25263 prev_op = get_prev_opcode(fd);
25264 if (has_optional_chain) {
25265 optional_chain_test(s, &optional_chaining_label, 1);
25266 }
25267 if (next_token(s))
25268 return -1;
25269 if (js_parse_expr(s))
25270 return -1;
25271 if (js_parse_expect(s, ']'))
25272 return -1;
25273 emit_source_pos(s, op_token_ptr);
25274 if (prev_op == OP_get_super) {
25275 emit_op(s, OP_get_super_value);
25276 } else {
25277 emit_op(s, OP_get_array_el);
25278 }
25279 } else {
25280 break;
25281 }
25282 }
25283 if (optional_chaining_label >= 0) {
25284 JSFunctionDef *fd = s->cur_func;
25285 int opcode;
25286 emit_label_raw(s, optional_chaining_label);
25287 /* modify the last opcode so that it is an indicator of an
25288 optional chain */
25289 opcode = get_prev_opcode(fd);
25290 if (opcode == OP_get_field || opcode == OP_get_array_el) {
25291 if (opcode == OP_get_field)
25292 opcode = OP_get_field_opt_chain;
25293 else
25294 opcode = OP_get_array_el_opt_chain;
25295 fd->byte_code.buf[fd->last_opcode_pos] = opcode;
25296 } else {
25297 fd->last_opcode_pos = -1;
25298 }
25299 }
25300 return 0;
25301}
25302
25303static __exception int js_parse_delete(JSParseState *s)
25304{
25305 JSFunctionDef *fd = s->cur_func;
25306 JSAtom name;
25307 int opcode;
25308
25309 if (next_token(s))
25310 return -1;
25311 if (js_parse_unary(s, PF_POW_FORBIDDEN))
25312 return -1;
25313 switch(opcode = get_prev_opcode(fd)) {
25314 case OP_get_field:
25315 case OP_get_field_opt_chain:
25316 {
25317 JSValue val;
25318 int ret, opt_chain_label, next_label;
25319 if (opcode == OP_get_field_opt_chain) {
25320 opt_chain_label = get_u32(fd->byte_code.buf +
25321 fd->last_opcode_pos + 1 + 4 + 1);
25322 } else {
25323 opt_chain_label = -1;
25324 }
25325 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25326 fd->byte_code.size = fd->last_opcode_pos;
25327 val = JS_AtomToValue(s->ctx, name);
25328 ret = emit_push_const(s, val, 1);
25329 JS_FreeValue(s->ctx, val);
25330 JS_FreeAtom(s->ctx, name);
25331 if (ret)
25332 return ret;
25333 emit_op(s, OP_delete);
25334 if (opt_chain_label >= 0) {
25335 next_label = emit_goto(s, OP_goto, -1);
25336 emit_label(s, opt_chain_label);
25337 /* if the optional chain is not taken, return 'true' */
25338 emit_op(s, OP_drop);
25339 emit_op(s, OP_push_true);
25340 emit_label(s, next_label);
25341 }
25342 fd->last_opcode_pos = -1;
25343 }
25344 break;
25345 case OP_get_array_el:
25346 fd->byte_code.size = fd->last_opcode_pos;
25347 fd->last_opcode_pos = -1;
25348 emit_op(s, OP_delete);
25349 break;
25350 case OP_get_array_el_opt_chain:
25351 {
25352 int opt_chain_label, next_label;
25353 opt_chain_label = get_u32(fd->byte_code.buf +
25354 fd->last_opcode_pos + 1 + 1);
25355 fd->byte_code.size = fd->last_opcode_pos;
25356 emit_op(s, OP_delete);
25357 next_label = emit_goto(s, OP_goto, -1);
25358 emit_label(s, opt_chain_label);
25359 /* if the optional chain is not taken, return 'true' */
25360 emit_op(s, OP_drop);
25361 emit_op(s, OP_push_true);
25362 emit_label(s, next_label);
25363 fd->last_opcode_pos = -1;
25364 }
25365 break;
25366 case OP_scope_get_var:
25367 /* 'delete this': this is not a reference */
25368 name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
25369 if (name == JS_ATOM_this || name == JS_ATOM_new_target)
25370 goto ret_true;
25371 if (fd->js_mode & JS_MODE_STRICT) {
25372 return js_parse_error(s, "cannot delete a direct reference in strict mode");
25373 } else {
25374 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
25375 }
25376 break;
25377 case OP_scope_get_private_field:
25378 return js_parse_error(s, "cannot delete a private class field");
25379 case OP_get_super_value:
25380 fd->byte_code.size = fd->last_opcode_pos;
25381 fd->last_opcode_pos = -1;
25382 emit_op(s, OP_throw_error);
25383 emit_atom(s, JS_ATOM_NULL);
25384 emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
25385 break;
25386 default:
25387 ret_true:
25388 emit_op(s, OP_drop);
25389 emit_op(s, OP_push_true);
25390 break;
25391 }
25392 return 0;
25393}
25394
25395/* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */
25396static __exception int js_parse_unary(JSParseState *s, int parse_flags)
25397{
25398 int op;
25399 const uint8_t *op_token_ptr;
25400
25401 switch(s->token.val) {
25402 case '+':
25403 case '-':
25404 case '!':
25405 case '~':
25406 case TOK_VOID:
25407 op_token_ptr = s->token.ptr;
25408 op = s->token.val;
25409 if (next_token(s))
25410 return -1;
25411 if (js_parse_unary(s, PF_POW_FORBIDDEN))
25412 return -1;
25413 switch(op) {
25414 case '-':
25415 emit_source_pos(s, op_token_ptr);
25416 emit_op(s, OP_neg);
25417 break;
25418 case '+':
25419 emit_source_pos(s, op_token_ptr);
25420 emit_op(s, OP_plus);
25421 break;
25422 case '!':
25423 emit_op(s, OP_lnot);
25424 break;
25425 case '~':
25426 emit_source_pos(s, op_token_ptr);
25427 emit_op(s, OP_not);
25428 break;
25429 case TOK_VOID:
25430 emit_op(s, OP_drop);
25431 emit_op(s, OP_undefined);
25432 break;
25433 default:
25434 abort();
25435 }
25436 parse_flags = 0;
25437 break;
25438 case TOK_DEC:
25439 case TOK_INC:
25440 {
25441 int opcode, op, scope, label;
25442 JSAtom name;
25443 op = s->token.val;
25444 op_token_ptr = s->token.ptr;
25445 if (next_token(s))
25446 return -1;
25447 if (js_parse_unary(s, 0))
25448 return -1;
25449 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25450 return -1;
25451 emit_source_pos(s, op_token_ptr);
25452 emit_op(s, OP_dec + op - TOK_DEC);
25453 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
25454 FALSE);
25455 }
25456 break;
25457 case TOK_TYPEOF:
25458 {
25459 JSFunctionDef *fd;
25460 if (next_token(s))
25461 return -1;
25462 if (js_parse_unary(s, PF_POW_FORBIDDEN))
25463 return -1;
25464 /* reference access should not return an exception, so we
25465 patch the get_var */
25466 fd = s->cur_func;
25467 if (get_prev_opcode(fd) == OP_scope_get_var) {
25468 fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
25469 }
25470 emit_op(s, OP_typeof);
25471 parse_flags = 0;
25472 }
25473 break;
25474 case TOK_DELETE:
25475 if (js_parse_delete(s))
25476 return -1;
25477 parse_flags = 0;
25478 break;
25479 case TOK_AWAIT:
25480 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
25481 return js_parse_error(s, "unexpected 'await' keyword");
25482 if (!s->cur_func->in_function_body)
25483 return js_parse_error(s, "await in default expression");
25484 if (next_token(s))
25485 return -1;
25486 if (js_parse_unary(s, PF_POW_FORBIDDEN))
25487 return -1;
25488 s->cur_func->has_await = TRUE;
25489 emit_op(s, OP_await);
25490 parse_flags = 0;
25491 break;
25492 default:
25493 if (js_parse_postfix_expr(s, PF_POSTFIX_CALL))
25494 return -1;
25495 if (!s->got_lf &&
25496 (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
25497 int opcode, op, scope, label;
25498 JSAtom name;
25499 op = s->token.val;
25500 op_token_ptr = s->token.ptr;
25501 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
25502 return -1;
25503 emit_source_pos(s, op_token_ptr);
25504 emit_op(s, OP_post_dec + op - TOK_DEC);
25505 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
25506 FALSE);
25507 if (next_token(s))
25508 return -1;
25509 }
25510 break;
25511 }
25512 if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
25513 if (s->token.val == TOK_POW) {
25514 /* Strict ES7 exponentiation syntax rules: To solve
25515 conficting semantics between different implementations
25516 regarding the precedence of prefix operators and the
25517 postifx exponential, ES7 specifies that -2**2 is a
25518 syntax error. */
25519 if (parse_flags & PF_POW_FORBIDDEN) {
25520 JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
25521 return -1;
25522 }
25523 op_token_ptr = s->token.ptr;
25524 if (next_token(s))
25525 return -1;
25526 if (js_parse_unary(s, PF_POW_ALLOWED))
25527 return -1;
25528 emit_source_pos(s, op_token_ptr);
25529 emit_op(s, OP_pow);
25530 }
25531 }
25532 return 0;
25533}
25534
25535/* allowed parse_flags: PF_IN_ACCEPTED */
25536static __exception int js_parse_expr_binary(JSParseState *s, int level,
25537 int parse_flags)
25538{
25539 int op, opcode;
25540 const uint8_t *op_token_ptr;
25541
25542 if (level == 0) {
25543 return js_parse_unary(s, PF_POW_ALLOWED);
25544 } else if (s->token.val == TOK_PRIVATE_NAME &&
25545 (parse_flags & PF_IN_ACCEPTED) && level == 4 &&
25546 peek_token(s, FALSE) == TOK_IN) {
25547 JSAtom atom;
25548
25549 atom = JS_DupAtom(s->ctx, s->token.u.ident.atom);
25550 if (next_token(s))
25551 goto fail_private_in;
25552 if (s->token.val != TOK_IN)
25553 goto fail_private_in;
25554 if (next_token(s))
25555 goto fail_private_in;
25556 if (js_parse_expr_binary(s, level - 1, parse_flags)) {
25557 fail_private_in:
25558 JS_FreeAtom(s->ctx, atom);
25559 return -1;
25560 }
25561 emit_op(s, OP_scope_in_private_field);
25562 emit_atom(s, atom);
25563 emit_u16(s, s->cur_func->scope_level);
25564 JS_FreeAtom(s->ctx, atom);
25565 return 0;
25566 } else {
25567 if (js_parse_expr_binary(s, level - 1, parse_flags))
25568 return -1;
25569 }
25570 for(;;) {
25571 op = s->token.val;
25572 op_token_ptr = s->token.ptr;
25573 switch(level) {
25574 case 1:
25575 switch(op) {
25576 case '*':
25577 opcode = OP_mul;
25578 break;
25579 case '/':
25580 opcode = OP_div;
25581 break;
25582 case '%':
25583 opcode = OP_mod;
25584 break;
25585 default:
25586 return 0;
25587 }
25588 break;
25589 case 2:
25590 switch(op) {
25591 case '+':
25592 opcode = OP_add;
25593 break;
25594 case '-':
25595 opcode = OP_sub;
25596 break;
25597 default:
25598 return 0;
25599 }
25600 break;
25601 case 3:
25602 switch(op) {
25603 case TOK_SHL:
25604 opcode = OP_shl;
25605 break;
25606 case TOK_SAR:
25607 opcode = OP_sar;
25608 break;
25609 case TOK_SHR:
25610 opcode = OP_shr;
25611 break;
25612 default:
25613 return 0;
25614 }
25615 break;
25616 case 4:
25617 switch(op) {
25618 case '<':
25619 opcode = OP_lt;
25620 break;
25621 case '>':
25622 opcode = OP_gt;
25623 break;
25624 case TOK_LTE:
25625 opcode = OP_lte;
25626 break;
25627 case TOK_GTE:
25628 opcode = OP_gte;
25629 break;
25630 case TOK_INSTANCEOF:
25631 opcode = OP_instanceof;
25632 break;
25633 case TOK_IN:
25634 if (parse_flags & PF_IN_ACCEPTED) {
25635 opcode = OP_in;
25636 } else {
25637 return 0;
25638 }
25639 break;
25640 default:
25641 return 0;
25642 }
25643 break;
25644 case 5:
25645 switch(op) {
25646 case TOK_EQ:
25647 opcode = OP_eq;
25648 break;
25649 case TOK_NEQ:
25650 opcode = OP_neq;
25651 break;
25652 case TOK_STRICT_EQ:
25653 opcode = OP_strict_eq;
25654 break;
25655 case TOK_STRICT_NEQ:
25656 opcode = OP_strict_neq;
25657 break;
25658 default:
25659 return 0;
25660 }
25661 break;
25662 case 6:
25663 switch(op) {
25664 case '&':
25665 opcode = OP_and;
25666 break;
25667 default:
25668 return 0;
25669 }
25670 break;
25671 case 7:
25672 switch(op) {
25673 case '^':
25674 opcode = OP_xor;
25675 break;
25676 default:
25677 return 0;
25678 }
25679 break;
25680 case 8:
25681 switch(op) {
25682 case '|':
25683 opcode = OP_or;
25684 break;
25685 default:
25686 return 0;
25687 }
25688 break;
25689 default:
25690 abort();
25691 }
25692 if (next_token(s))
25693 return -1;
25694 if (js_parse_expr_binary(s, level - 1, parse_flags))
25695 return -1;
25696 emit_source_pos(s, op_token_ptr);
25697 emit_op(s, opcode);
25698 }
25699 return 0;
25700}
25701
25702/* allowed parse_flags: PF_IN_ACCEPTED */
25703static __exception int js_parse_logical_and_or(JSParseState *s, int op,
25704 int parse_flags)
25705{
25706 int label1;
25707
25708 if (op == TOK_LAND) {
25709 if (js_parse_expr_binary(s, 8, parse_flags))
25710 return -1;
25711 } else {
25712 if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
25713 return -1;
25714 }
25715 if (s->token.val == op) {
25716 label1 = new_label(s);
25717
25718 for(;;) {
25719 if (next_token(s))
25720 return -1;
25721 emit_op(s, OP_dup);
25722 emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
25723 emit_op(s, OP_drop);
25724
25725 if (op == TOK_LAND) {
25726 if (js_parse_expr_binary(s, 8, parse_flags))
25727 return -1;
25728 } else {
25729 if (js_parse_logical_and_or(s, TOK_LAND,
25730 parse_flags))
25731 return -1;
25732 }
25733 if (s->token.val != op) {
25734 if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
25735 return js_parse_error(s, "cannot mix ?? with && or ||");
25736 break;
25737 }
25738 }
25739
25740 emit_label(s, label1);
25741 }
25742 return 0;
25743}
25744
25745static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
25746{
25747 int label1;
25748
25749 if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
25750 return -1;
25751 if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
25752 label1 = new_label(s);
25753 for(;;) {
25754 if (next_token(s))
25755 return -1;
25756
25757 emit_op(s, OP_dup);
25758 emit_op(s, OP_is_undefined_or_null);
25759 emit_goto(s, OP_if_false, label1);
25760 emit_op(s, OP_drop);
25761
25762 if (js_parse_expr_binary(s, 8, parse_flags))
25763 return -1;
25764 if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
25765 break;
25766 }
25767 emit_label(s, label1);
25768 }
25769 return 0;
25770}
25771
25772/* allowed parse_flags: PF_IN_ACCEPTED */
25773static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
25774{
25775 int label1, label2;
25776
25777 if (js_parse_coalesce_expr(s, parse_flags))
25778 return -1;
25779 if (s->token.val == '?') {
25780 if (next_token(s))
25781 return -1;
25782 label1 = emit_goto(s, OP_if_false, -1);
25783
25784 if (js_parse_assign_expr(s))
25785 return -1;
25786 if (js_parse_expect(s, ':'))
25787 return -1;
25788
25789 label2 = emit_goto(s, OP_goto, -1);
25790
25791 emit_label(s, label1);
25792
25793 if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
25794 return -1;
25795
25796 emit_label(s, label2);
25797 }
25798 return 0;
25799}
25800
25801/* allowed parse_flags: PF_IN_ACCEPTED */
25802static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
25803{
25804 int opcode, op, scope, skip_bits;
25805 JSAtom name0 = JS_ATOM_NULL;
25806 JSAtom name;
25807
25808 if (s->token.val == TOK_YIELD) {
25809 BOOL is_star = FALSE, is_async;
25810
25811 if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
25812 return js_parse_error(s, "unexpected 'yield' keyword");
25813 if (!s->cur_func->in_function_body)
25814 return js_parse_error(s, "yield in default expression");
25815 if (next_token(s))
25816 return -1;
25817 /* XXX: is there a better method to detect 'yield' without
25818 parameters ? */
25819 if (s->token.val != ';' && s->token.val != ')' &&
25820 s->token.val != ']' && s->token.val != '}' &&
25821 s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
25822 if (s->token.val == '*') {
25823 is_star = TRUE;
25824 if (next_token(s))
25825 return -1;
25826 }
25827 if (js_parse_assign_expr2(s, parse_flags))
25828 return -1;
25829 } else {
25830 emit_op(s, OP_undefined);
25831 }
25832 is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
25833
25834 if (is_star) {
25835 int label_loop, label_return, label_next;
25836 int label_return1, label_yield, label_throw, label_throw1;
25837 int label_throw2;
25838
25839 label_loop = new_label(s);
25840 label_yield = new_label(s);
25841
25842 emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
25843
25844 /* remove the catch offset (XXX: could avoid pushing back
25845 undefined) */
25846 emit_op(s, OP_drop);
25847 emit_op(s, OP_undefined);
25848
25849 emit_op(s, OP_undefined); /* initial value */
25850
25851 emit_label(s, label_loop);
25852 emit_op(s, OP_iterator_next);
25853 if (is_async)
25854 emit_op(s, OP_await);
25855 emit_op(s, OP_iterator_check_object);
25856 emit_op(s, OP_get_field2);
25857 emit_atom(s, JS_ATOM_done);
25858 label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
25859 emit_label(s, label_yield);
25860 if (is_async) {
25861 /* OP_async_yield_star takes the value as parameter */
25862 emit_op(s, OP_get_field);
25863 emit_atom(s, JS_ATOM_value);
25864 emit_op(s, OP_async_yield_star);
25865 } else {
25866 /* OP_yield_star takes (value, done) as parameter */
25867 emit_op(s, OP_yield_star);
25868 }
25869 emit_op(s, OP_dup);
25870 label_return = emit_goto(s, OP_if_true, -1);
25871 emit_op(s, OP_drop);
25872 emit_goto(s, OP_goto, label_loop);
25873
25874 emit_label(s, label_return);
25875 emit_op(s, OP_push_i32);
25876 emit_u32(s, 2);
25877 emit_op(s, OP_strict_eq);
25878 label_throw = emit_goto(s, OP_if_true, -1);
25879
25880 /* return handling */
25881 if (is_async)
25882 emit_op(s, OP_await);
25883 emit_op(s, OP_iterator_call);
25884 emit_u8(s, 0);
25885 label_return1 = emit_goto(s, OP_if_true, -1);
25886 if (is_async)
25887 emit_op(s, OP_await);
25888 emit_op(s, OP_iterator_check_object);
25889 emit_op(s, OP_get_field2);
25890 emit_atom(s, JS_ATOM_done);
25891 emit_goto(s, OP_if_false, label_yield);
25892
25893 emit_op(s, OP_get_field);
25894 emit_atom(s, JS_ATOM_value);
25895
25896 emit_label(s, label_return1);
25897 emit_op(s, OP_nip);
25898 emit_op(s, OP_nip);
25899 emit_op(s, OP_nip);
25900 emit_return(s, TRUE);
25901
25902 /* throw handling */
25903 emit_label(s, label_throw);
25904 emit_op(s, OP_iterator_call);
25905 emit_u8(s, 1);
25906 label_throw1 = emit_goto(s, OP_if_true, -1);
25907 if (is_async)
25908 emit_op(s, OP_await);
25909 emit_op(s, OP_iterator_check_object);
25910 emit_op(s, OP_get_field2);
25911 emit_atom(s, JS_ATOM_done);
25912 emit_goto(s, OP_if_false, label_yield);
25913 emit_goto(s, OP_goto, label_next);
25914 /* close the iterator and throw a type error exception */
25915 emit_label(s, label_throw1);
25916 emit_op(s, OP_iterator_call);
25917 emit_u8(s, 2);
25918 label_throw2 = emit_goto(s, OP_if_true, -1);
25919 if (is_async)
25920 emit_op(s, OP_await);
25921 emit_label(s, label_throw2);
25922
25923 emit_op(s, OP_throw_error);
25924 emit_atom(s, JS_ATOM_NULL);
25925 emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
25926
25927 emit_label(s, label_next);
25928 emit_op(s, OP_get_field);
25929 emit_atom(s, JS_ATOM_value);
25930 emit_op(s, OP_nip); /* keep the value associated with
25931 done = true */
25932 emit_op(s, OP_nip);
25933 emit_op(s, OP_nip);
25934 } else {
25935 int label_next;
25936
25937 if (is_async)
25938 emit_op(s, OP_await);
25939 emit_op(s, OP_yield);
25940 label_next = emit_goto(s, OP_if_false, -1);
25941 emit_return(s, TRUE);
25942 emit_label(s, label_next);
25943 }
25944 return 0;
25945 } else if (s->token.val == '(' &&
25946 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
25947 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
25948 JS_FUNC_NORMAL, JS_ATOM_NULL,
25949 s->token.ptr);
25950 } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) {
25951 const uint8_t *source_ptr;
25952 int tok;
25953 JSParsePos pos;
25954
25955 /* fast test */
25956 tok = peek_token(s, TRUE);
25957 if (tok == TOK_FUNCTION || tok == '\n')
25958 goto next;
25959
25960 source_ptr = s->token.ptr;
25961 js_parse_get_pos(s, &pos);
25962 if (next_token(s))
25963 return -1;
25964 if ((s->token.val == '(' &&
25965 js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
25966 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
25967 peek_token(s, TRUE) == TOK_ARROW)) {
25968 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
25969 JS_FUNC_ASYNC, JS_ATOM_NULL,
25970 source_ptr);
25971 } else {
25972 /* undo the token parsing */
25973 if (js_parse_seek_token(s, &pos))
25974 return -1;
25975 }
25976 } else if (s->token.val == TOK_IDENT &&
25977 peek_token(s, TRUE) == TOK_ARROW) {
25978 return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
25979 JS_FUNC_NORMAL, JS_ATOM_NULL,
25980 s->token.ptr);
25981 } else if ((s->token.val == '{' || s->token.val == '[') &&
25982 js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
25983 if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
25984 return -1;
25985 return 0;
25986 }
25987 next:
25988 if (s->token.val == TOK_IDENT) {
25989 /* name0 is used to check for OP_set_name pattern, not duplicated */
25990 name0 = s->token.u.ident.atom;
25991 }
25992 if (js_parse_cond_expr(s, parse_flags))
25993 return -1;
25994
25995 op = s->token.val;
25996 if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
25997 int label;
25998 const uint8_t *op_token_ptr;
25999 op_token_ptr = s->token.ptr;
26000 if (next_token(s))
26001 return -1;
26002 if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
26003 return -1;
26004
26005 if (js_parse_assign_expr2(s, parse_flags)) {
26006 JS_FreeAtom(s->ctx, name);
26007 return -1;
26008 }
26009
26010 if (op == '=') {
26011 if (opcode == OP_get_ref_value && name == name0) {
26012 set_object_name(s, name);
26013 }
26014 } else {
26015 static const uint8_t assign_opcodes[] = {
26016 OP_mul, OP_div, OP_mod, OP_add, OP_sub,
26017 OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
26018 OP_pow,
26019 };
26020 op = assign_opcodes[op - TOK_MUL_ASSIGN];
26021 emit_source_pos(s, op_token_ptr);
26022 emit_op(s, op);
26023 }
26024 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
26025 } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
26026 int label, label1, depth_lvalue, label2;
26027
26028 if (next_token(s))
26029 return -1;
26030 if (get_lvalue(s, &opcode, &scope, &name, &label,
26031 &depth_lvalue, TRUE, op) < 0)
26032 return -1;
26033
26034 emit_op(s, OP_dup);
26035 if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
26036 emit_op(s, OP_is_undefined_or_null);
26037 label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
26038 -1);
26039 emit_op(s, OP_drop);
26040
26041 if (js_parse_assign_expr2(s, parse_flags)) {
26042 JS_FreeAtom(s->ctx, name);
26043 return -1;
26044 }
26045
26046 if (opcode == OP_get_ref_value && name == name0) {
26047 set_object_name(s, name);
26048 }
26049
26050 switch(depth_lvalue) {
26051 case 1:
26052 emit_op(s, OP_insert2);
26053 break;
26054 case 2:
26055 emit_op(s, OP_insert3);
26056 break;
26057 case 3:
26058 emit_op(s, OP_insert4);
26059 break;
26060 default:
26061 abort();
26062 }
26063
26064 /* XXX: we disable the OP_put_ref_value optimization by not
26065 using put_lvalue() otherwise depth_lvalue is not correct */
26066 put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
26067 FALSE);
26068 label2 = emit_goto(s, OP_goto, -1);
26069
26070 emit_label(s, label1);
26071
26072 /* remove the lvalue stack entries */
26073 while (depth_lvalue != 0) {
26074 emit_op(s, OP_nip);
26075 depth_lvalue--;
26076 }
26077
26078 emit_label(s, label2);
26079 }
26080 return 0;
26081}
26082
26083static __exception int js_parse_assign_expr(JSParseState *s)
26084{
26085 return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
26086}
26087
26088/* allowed parse_flags: PF_IN_ACCEPTED */
26089static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
26090{
26091 BOOL comma = FALSE;
26092 for(;;) {
26093 if (js_parse_assign_expr2(s, parse_flags))
26094 return -1;
26095 if (comma) {
26096 /* prevent get_lvalue from using the last expression
26097 as an lvalue. This also prevents the conversion of
26098 of get_var to get_ref for method lookup in function
26099 call inside `with` statement.
26100 */
26101 s->cur_func->last_opcode_pos = -1;
26102 }
26103 if (s->token.val != ',')
26104 break;
26105 comma = TRUE;
26106 if (next_token(s))
26107 return -1;
26108 emit_op(s, OP_drop);
26109 }
26110 return 0;
26111}
26112
26113static __exception int js_parse_expr(JSParseState *s)
26114{
26115 return js_parse_expr2(s, PF_IN_ACCEPTED);
26116}
26117
26118static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
26119 JSAtom label_name,
26120 int label_break, int label_cont,
26121 int drop_count)
26122{
26123 be->prev = fd->top_break;
26124 fd->top_break = be;
26125 be->label_name = label_name;
26126 be->label_break = label_break;
26127 be->label_cont = label_cont;
26128 be->drop_count = drop_count;
26129 be->label_finally = -1;
26130 be->scope_level = fd->scope_level;
26131 be->has_iterator = FALSE;
26132 be->is_regular_stmt = FALSE;
26133}
26134
26135static void pop_break_entry(JSFunctionDef *fd)
26136{
26137 BlockEnv *be;
26138 be = fd->top_break;
26139 fd->top_break = be->prev;
26140}
26141
26142static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
26143{
26144 BlockEnv *top;
26145 int i, scope_level;
26146
26147 scope_level = s->cur_func->scope_level;
26148 top = s->cur_func->top_break;
26149 while (top != NULL) {
26150 close_scopes(s, scope_level, top->scope_level);
26151 scope_level = top->scope_level;
26152 if (is_cont &&
26153 top->label_cont != -1 &&
26154 (name == JS_ATOM_NULL || top->label_name == name)) {
26155 /* continue stays inside the same block */
26156 emit_goto(s, OP_goto, top->label_cont);
26157 return 0;
26158 }
26159 if (!is_cont &&
26160 top->label_break != -1 &&
26161 ((name == JS_ATOM_NULL && !top->is_regular_stmt) ||
26162 top->label_name == name)) {
26163 emit_goto(s, OP_goto, top->label_break);
26164 return 0;
26165 }
26166 i = 0;
26167 if (top->has_iterator) {
26168 emit_op(s, OP_iterator_close);
26169 i += 3;
26170 }
26171 for(; i < top->drop_count; i++)
26172 emit_op(s, OP_drop);
26173 if (top->label_finally != -1) {
26174 /* must push dummy value to keep same stack depth */
26175 emit_op(s, OP_undefined);
26176 emit_goto(s, OP_gosub, top->label_finally);
26177 emit_op(s, OP_drop);
26178 }
26179 top = top->prev;
26180 }
26181 if (name == JS_ATOM_NULL) {
26182 if (is_cont)
26183 return js_parse_error(s, "continue must be inside loop");
26184 else
26185 return js_parse_error(s, "break must be inside loop or switch");
26186 } else {
26187 return js_parse_error(s, "break/continue label not found");
26188 }
26189}
26190
26191/* execute the finally blocks before return */
26192static void emit_return(JSParseState *s, BOOL hasval)
26193{
26194 BlockEnv *top;
26195
26196 if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
26197 if (!hasval) {
26198 /* no value: direct return in case of async generator */
26199 emit_op(s, OP_undefined);
26200 hasval = TRUE;
26201 } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
26202 /* the await must be done before handling the "finally" in
26203 case it raises an exception */
26204 emit_op(s, OP_await);
26205 }
26206 }
26207
26208 top = s->cur_func->top_break;
26209 while (top != NULL) {
26210 if (top->has_iterator || top->label_finally != -1) {
26211 if (!hasval) {
26212 emit_op(s, OP_undefined);
26213 hasval = TRUE;
26214 }
26215 /* Remove the stack elements up to and including the catch
26216 offset. When 'yield' is used in an expression we have
26217 no easy way to count them, so we use this specific
26218 instruction instead. */
26219 emit_op(s, OP_nip_catch);
26220 /* stack: iter_obj next ret_val */
26221 if (top->has_iterator) {
26222 if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
26223 int label_next, label_next2;
26224 emit_op(s, OP_nip); /* next */
26225 emit_op(s, OP_swap);
26226 emit_op(s, OP_get_field2);
26227 emit_atom(s, JS_ATOM_return);
26228 /* stack: iter_obj return_func */
26229 emit_op(s, OP_dup);
26230 emit_op(s, OP_is_undefined_or_null);
26231 label_next = emit_goto(s, OP_if_true, -1);
26232 emit_op(s, OP_call_method);
26233 emit_u16(s, 0);
26234 emit_op(s, OP_iterator_check_object);
26235 emit_op(s, OP_await);
26236 label_next2 = emit_goto(s, OP_goto, -1);
26237 emit_label(s, label_next);
26238 emit_op(s, OP_drop);
26239 emit_label(s, label_next2);
26240 emit_op(s, OP_drop);
26241 } else {
26242 emit_op(s, OP_rot3r);
26243 emit_op(s, OP_undefined); /* dummy catch offset */
26244 emit_op(s, OP_iterator_close);
26245 }
26246 } else {
26247 /* execute the "finally" block */
26248 emit_goto(s, OP_gosub, top->label_finally);
26249 }
26250 }
26251 top = top->prev;
26252 }
26253 if (s->cur_func->is_derived_class_constructor) {
26254 int label_return;
26255
26256 /* 'this' can be uninitialized, so it may be accessed only if
26257 the derived class constructor does not return an object */
26258 if (hasval) {
26259 emit_op(s, OP_check_ctor_return);
26260 label_return = emit_goto(s, OP_if_false, -1);
26261 emit_op(s, OP_drop);
26262 } else {
26263 label_return = -1;
26264 }
26265
26266 /* The error should be raised in the caller context, so we use
26267 a specific opcode */
26268 emit_op(s, OP_scope_get_var_checkthis);
26269 emit_atom(s, JS_ATOM_this);
26270 emit_u16(s, 0);
26271
26272 emit_label(s, label_return);
26273 emit_op(s, OP_return);
26274 } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
26275 emit_op(s, OP_return_async);
26276 } else {
26277 emit_op(s, hasval ? OP_return : OP_return_undef);
26278 }
26279}
26280
26281#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */
26282/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
26283#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
26284#define DECL_MASK_OTHER (1 << 2) /* all other declarations */
26285#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
26286
26287static __exception int js_parse_statement_or_decl(JSParseState *s,
26288 int decl_mask);
26289
26290static __exception int js_parse_statement(JSParseState *s)
26291{
26292 return js_parse_statement_or_decl(s, 0);
26293}
26294
26295static __exception int js_parse_block(JSParseState *s)
26296{
26297 if (js_parse_expect(s, '{'))
26298 return -1;
26299 if (s->token.val != '}') {
26300 push_scope(s);
26301 for(;;) {
26302 if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
26303 return -1;
26304 if (s->token.val == '}')
26305 break;
26306 }
26307 pop_scope(s);
26308 }
26309 if (next_token(s))
26310 return -1;
26311 return 0;
26312}
26313
26314/* allowed parse_flags: PF_IN_ACCEPTED */
26315static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
26316 BOOL export_flag)
26317{
26318 JSContext *ctx = s->ctx;
26319 JSFunctionDef *fd = s->cur_func;
26320 JSAtom name = JS_ATOM_NULL;
26321
26322 for (;;) {
26323 if (s->token.val == TOK_IDENT) {
26324 if (s->token.u.ident.is_reserved) {
26325 return js_parse_error_reserved_identifier(s);
26326 }
26327 name = JS_DupAtom(ctx, s->token.u.ident.atom);
26328 if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
26329 js_parse_error(s, "'let' is not a valid lexical identifier");
26330 goto var_error;
26331 }
26332 if (next_token(s))
26333 goto var_error;
26334 if (js_define_var(s, name, tok))
26335 goto var_error;
26336 if (export_flag) {
26337 if (!add_export_entry(s, s->cur_func->module, name, name,
26338 JS_EXPORT_TYPE_LOCAL))
26339 goto var_error;
26340 }
26341
26342 if (s->token.val == '=') {
26343 if (next_token(s))
26344 goto var_error;
26345 if (need_var_reference(s, tok)) {
26346 /* Must make a reference for proper `with` semantics */
26347 int opcode, scope, label;
26348 JSAtom name1;
26349
26350 emit_op(s, OP_scope_get_var);
26351 emit_atom(s, name);
26352 emit_u16(s, fd->scope_level);
26353 if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
26354 goto var_error;
26355 if (js_parse_assign_expr2(s, parse_flags)) {
26356 JS_FreeAtom(ctx, name1);
26357 goto var_error;
26358 }
26359 set_object_name(s, name);
26360 put_lvalue(s, opcode, scope, name1, label,
26361 PUT_LVALUE_NOKEEP, FALSE);
26362 } else {
26363 if (js_parse_assign_expr2(s, parse_flags))
26364 goto var_error;
26365 set_object_name(s, name);
26366 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
26367 OP_scope_put_var_init : OP_scope_put_var);
26368 emit_atom(s, name);
26369 emit_u16(s, fd->scope_level);
26370 }
26371 } else {
26372 if (tok == TOK_CONST) {
26373 js_parse_error(s, "missing initializer for const variable");
26374 goto var_error;
26375 }
26376 if (tok == TOK_LET) {
26377 /* initialize lexical variable upon entering its scope */
26378 emit_op(s, OP_undefined);
26379 emit_op(s, OP_scope_put_var_init);
26380 emit_atom(s, name);
26381 emit_u16(s, fd->scope_level);
26382 }
26383 }
26384 JS_FreeAtom(ctx, name);
26385 } else {
26386 int skip_bits;
26387 if ((s->token.val == '[' || s->token.val == '{')
26388 && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
26389 emit_op(s, OP_undefined);
26390 if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, export_flag) < 0)
26391 return -1;
26392 } else {
26393 return js_parse_error(s, "variable name expected");
26394 }
26395 }
26396 if (s->token.val != ',')
26397 break;
26398 if (next_token(s))
26399 return -1;
26400 }
26401 return 0;
26402
26403 var_error:
26404 JS_FreeAtom(ctx, name);
26405 return -1;
26406}
26407
26408/* test if the current token is a label. Use simplistic look-ahead scanner */
26409static BOOL is_label(JSParseState *s)
26410{
26411 return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
26412 peek_token(s, FALSE) == ':');
26413}
26414
26415/* test if the current token is a let keyword. Use simplistic look-ahead scanner */
26416static int is_let(JSParseState *s, int decl_mask)
26417{
26418 int res = FALSE;
26419 const uint8_t *last_token_ptr;
26420
26421 if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
26422 JSParsePos pos;
26423 js_parse_get_pos(s, &pos);
26424 for (;;) {
26425 last_token_ptr = s->token.ptr;
26426 if (next_token(s)) {
26427 res = -1;
26428 break;
26429 }
26430 if (s->token.val == '[') {
26431 /* let [ is a syntax restriction:
26432 it never introduces an ExpressionStatement */
26433 res = TRUE;
26434 break;
26435 }
26436 if (s->token.val == '{' ||
26437 (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
26438 s->token.val == TOK_LET ||
26439 s->token.val == TOK_YIELD ||
26440 s->token.val == TOK_AWAIT) {
26441 /* Check for possible ASI if not scanning for Declaration */
26442 /* XXX: should also check that `{` introduces a BindingPattern,
26443 but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
26444 if (!has_lf_in_range(last_token_ptr, s->token.ptr) ||
26445 (decl_mask & DECL_MASK_OTHER)) {
26446 res = TRUE;
26447 break;
26448 }
26449 break;
26450 }
26451 break;
26452 }
26453 if (js_parse_seek_token(s, &pos)) {
26454 res = -1;
26455 }
26456 }
26457 return res;
26458}
26459
26460/* XXX: handle IteratorClose when exiting the loop before the
26461 enumeration is done */
26462static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
26463 BOOL is_async)
26464{
26465 JSContext *ctx = s->ctx;
26466 JSFunctionDef *fd = s->cur_func;
26467 JSAtom var_name;
26468 BOOL has_initializer, is_for_of, has_destructuring;
26469 int tok, tok1, opcode, scope, block_scope_level;
26470 int label_next, label_expr, label_cont, label_body, label_break;
26471 int pos_next, pos_expr;
26472 BlockEnv break_entry;
26473
26474 has_initializer = FALSE;
26475 has_destructuring = FALSE;
26476 is_for_of = FALSE;
26477 block_scope_level = fd->scope_level;
26478 label_cont = new_label(s);
26479 label_body = new_label(s);
26480 label_break = new_label(s);
26481 label_next = new_label(s);
26482
26483 /* create scope for the lexical variables declared in the enumeration
26484 expressions. XXX: Not completely correct because of weird capturing
26485 semantics in `for (i of o) a.push(function(){return i})` */
26486 push_scope(s);
26487
26488 /* local for_in scope starts here so individual elements
26489 can be closed in statement. */
26490 push_break_entry(s->cur_func, &break_entry,
26491 label_name, label_break, label_cont, 1);
26492 break_entry.scope_level = block_scope_level;
26493
26494 label_expr = emit_goto(s, OP_goto, -1);
26495
26496 pos_next = s->cur_func->byte_code.size;
26497 emit_label(s, label_next);
26498
26499 tok = s->token.val;
26500 switch (is_let(s, DECL_MASK_OTHER)) {
26501 case TRUE:
26502 tok = TOK_LET;
26503 break;
26504 case FALSE:
26505 break;
26506 default:
26507 return -1;
26508 }
26509 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26510 if (next_token(s))
26511 return -1;
26512
26513 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
26514 if (s->token.val == '[' || s->token.val == '{') {
26515 if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0)
26516 return -1;
26517 has_destructuring = TRUE;
26518 } else {
26519 return js_parse_error(s, "variable name expected");
26520 }
26521 var_name = JS_ATOM_NULL;
26522 } else {
26523 var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26524 if (next_token(s)) {
26525 JS_FreeAtom(s->ctx, var_name);
26526 return -1;
26527 }
26528 if (js_define_var(s, var_name, tok)) {
26529 JS_FreeAtom(s->ctx, var_name);
26530 return -1;
26531 }
26532 emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
26533 OP_scope_put_var_init : OP_scope_put_var);
26534 emit_atom(s, var_name);
26535 emit_u16(s, fd->scope_level);
26536 }
26537 } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) &&
26538 peek_token(s, FALSE) == TOK_OF) {
26539 return js_parse_error(s, "'for of' expression cannot start with 'async'");
26540 } else {
26541 int skip_bits;
26542 if ((s->token.val == '[' || s->token.val == '{')
26543 && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
26544 if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
26545 return -1;
26546 } else {
26547 int lvalue_label;
26548 if (js_parse_left_hand_side_expr(s))
26549 return -1;
26550 if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
26551 NULL, FALSE, TOK_FOR))
26552 return -1;
26553 put_lvalue(s, opcode, scope, var_name, lvalue_label,
26554 PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
26555 }
26556 var_name = JS_ATOM_NULL;
26557 }
26558 emit_goto(s, OP_goto, label_body);
26559
26560 pos_expr = s->cur_func->byte_code.size;
26561 emit_label(s, label_expr);
26562 if (s->token.val == '=') {
26563 /* XXX: potential scoping issue if inside `with` statement */
26564 has_initializer = TRUE;
26565 /* parse and evaluate initializer prior to evaluating the
26566 object (only used with "for in" with a non lexical variable
26567 in non strict mode */
26568 if (next_token(s) || js_parse_assign_expr2(s, 0)) {
26569 JS_FreeAtom(ctx, var_name);
26570 return -1;
26571 }
26572 if (var_name != JS_ATOM_NULL) {
26573 emit_op(s, OP_scope_put_var);
26574 emit_atom(s, var_name);
26575 emit_u16(s, fd->scope_level);
26576 }
26577 }
26578 JS_FreeAtom(ctx, var_name);
26579
26580 if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
26581 break_entry.has_iterator = is_for_of = TRUE;
26582 break_entry.drop_count += 2;
26583 if (has_initializer)
26584 goto initializer_error;
26585 } else if (s->token.val == TOK_IN) {
26586 if (is_async)
26587 return js_parse_error(s, "'for await' loop should be used with 'of'");
26588 if (has_initializer &&
26589 (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
26590 has_destructuring)) {
26591 initializer_error:
26592 return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
26593 is_for_of ? "of" : "in");
26594 }
26595 } else {
26596 return js_parse_error(s, "expected 'of' or 'in' in for control expression");
26597 }
26598 if (next_token(s))
26599 return -1;
26600 if (is_for_of) {
26601 if (js_parse_assign_expr(s))
26602 return -1;
26603 } else {
26604 if (js_parse_expr(s))
26605 return -1;
26606 }
26607 /* close the scope after having evaluated the expression so that
26608 the TDZ values are in the closures */
26609 close_scopes(s, s->cur_func->scope_level, block_scope_level);
26610 if (is_for_of) {
26611 if (is_async)
26612 emit_op(s, OP_for_await_of_start);
26613 else
26614 emit_op(s, OP_for_of_start);
26615 /* on stack: enum_rec */
26616 } else {
26617 emit_op(s, OP_for_in_start);
26618 /* on stack: enum_obj */
26619 }
26620 emit_goto(s, OP_goto, label_cont);
26621
26622 if (js_parse_expect(s, ')'))
26623 return -1;
26624
26625 if (OPTIMIZE) {
26626 /* move the `next` code here */
26627 DynBuf *bc = &s->cur_func->byte_code;
26628 int chunk_size = pos_expr - pos_next;
26629 int offset = bc->size - pos_next;
26630 int i;
26631 dbuf_realloc(bc, bc->size + chunk_size);
26632 dbuf_put(bc, bc->buf + pos_next, chunk_size);
26633 memset(bc->buf + pos_next, OP_nop, chunk_size);
26634 /* `next` part ends with a goto */
26635 s->cur_func->last_opcode_pos = bc->size - 5;
26636 /* relocate labels */
26637 for (i = label_cont; i < s->cur_func->label_count; i++) {
26638 LabelSlot *ls = &s->cur_func->label_slots[i];
26639 if (ls->pos >= pos_next && ls->pos < pos_expr)
26640 ls->pos += offset;
26641 }
26642 }
26643
26644 emit_label(s, label_body);
26645 if (js_parse_statement(s))
26646 return -1;
26647
26648 close_scopes(s, s->cur_func->scope_level, block_scope_level);
26649
26650 emit_label(s, label_cont);
26651 if (is_for_of) {
26652 if (is_async) {
26653 /* stack: iter_obj next catch_offset */
26654 /* call the next method */
26655 emit_op(s, OP_for_await_of_next);
26656 /* get the result of the promise */
26657 emit_op(s, OP_await);
26658 /* unwrap the value and done values */
26659 emit_op(s, OP_iterator_get_value_done);
26660 } else {
26661 emit_op(s, OP_for_of_next);
26662 emit_u8(s, 0);
26663 }
26664 } else {
26665 emit_op(s, OP_for_in_next);
26666 }
26667 /* on stack: enum_rec / enum_obj value bool */
26668 emit_goto(s, OP_if_false, label_next);
26669 /* drop the undefined value from for_xx_next */
26670 emit_op(s, OP_drop);
26671
26672 emit_label(s, label_break);
26673 if (is_for_of) {
26674 /* close and drop enum_rec */
26675 emit_op(s, OP_iterator_close);
26676 } else {
26677 emit_op(s, OP_drop);
26678 }
26679 pop_break_entry(s->cur_func);
26680 pop_scope(s);
26681 return 0;
26682}
26683
26684static void set_eval_ret_undefined(JSParseState *s)
26685{
26686 if (s->cur_func->eval_ret_idx >= 0) {
26687 emit_op(s, OP_undefined);
26688 emit_op(s, OP_put_loc);
26689 emit_u16(s, s->cur_func->eval_ret_idx);
26690 }
26691}
26692
26693static __exception int js_parse_statement_or_decl(JSParseState *s,
26694 int decl_mask)
26695{
26696 JSContext *ctx = s->ctx;
26697 JSAtom label_name;
26698 int tok;
26699
26700 /* specific label handling */
26701 /* XXX: support multiple labels on loop statements */
26702 label_name = JS_ATOM_NULL;
26703 if (is_label(s)) {
26704 BlockEnv *be;
26705
26706 label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
26707
26708 for (be = s->cur_func->top_break; be; be = be->prev) {
26709 if (be->label_name == label_name) {
26710 js_parse_error(s, "duplicate label name");
26711 goto fail;
26712 }
26713 }
26714
26715 if (next_token(s))
26716 goto fail;
26717 if (js_parse_expect(s, ':'))
26718 goto fail;
26719 if (s->token.val != TOK_FOR
26720 && s->token.val != TOK_DO
26721 && s->token.val != TOK_WHILE) {
26722 /* labelled regular statement */
26723 int label_break, mask;
26724 BlockEnv break_entry;
26725
26726 label_break = new_label(s);
26727 push_break_entry(s->cur_func, &break_entry,
26728 label_name, label_break, -1, 0);
26729 break_entry.is_regular_stmt = TRUE;
26730 if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
26731 (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
26732 mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
26733 } else {
26734 mask = 0;
26735 }
26736 if (js_parse_statement_or_decl(s, mask))
26737 goto fail;
26738 emit_label(s, label_break);
26739 pop_break_entry(s->cur_func);
26740 goto done;
26741 }
26742 }
26743
26744 switch(tok = s->token.val) {
26745 case '{':
26746 if (js_parse_block(s))
26747 goto fail;
26748 break;
26749 case TOK_RETURN:
26750 {
26751 const uint8_t *op_token_ptr;
26752 if (s->cur_func->is_eval) {
26753 js_parse_error(s, "return not in a function");
26754 goto fail;
26755 }
26756 if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
26757 js_parse_error(s, "return in a static initializer block");
26758 goto fail;
26759 }
26760 op_token_ptr = s->token.ptr;
26761 if (next_token(s))
26762 goto fail;
26763 if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
26764 if (js_parse_expr(s))
26765 goto fail;
26766 emit_source_pos(s, op_token_ptr);
26767 emit_return(s, TRUE);
26768 } else {
26769 emit_source_pos(s, op_token_ptr);
26770 emit_return(s, FALSE);
26771 }
26772 if (js_parse_expect_semi(s))
26773 goto fail;
26774 }
26775 break;
26776 case TOK_THROW:
26777 {
26778 const uint8_t *op_token_ptr;
26779 op_token_ptr = s->token.ptr;
26780 if (next_token(s))
26781 goto fail;
26782 if (s->got_lf) {
26783 js_parse_error(s, "line terminator not allowed after throw");
26784 goto fail;
26785 }
26786 if (js_parse_expr(s))
26787 goto fail;
26788 emit_source_pos(s, op_token_ptr);
26789 emit_op(s, OP_throw);
26790 if (js_parse_expect_semi(s))
26791 goto fail;
26792 }
26793 break;
26794 case TOK_LET:
26795 case TOK_CONST:
26796 haslet:
26797 if (!(decl_mask & DECL_MASK_OTHER)) {
26798 js_parse_error(s, "lexical declarations can't appear in single-statement context");
26799 goto fail;
26800 }
26801 /* fall thru */
26802 case TOK_VAR:
26803 if (next_token(s))
26804 goto fail;
26805 if (js_parse_var(s, TRUE, tok, FALSE))
26806 goto fail;
26807 if (js_parse_expect_semi(s))
26808 goto fail;
26809 break;
26810 case TOK_IF:
26811 {
26812 int label1, label2, mask;
26813 if (next_token(s))
26814 goto fail;
26815 /* create a new scope for `let f;if(1) function f(){}` */
26816 push_scope(s);
26817 set_eval_ret_undefined(s);
26818 if (js_parse_expr_paren(s))
26819 goto fail;
26820 label1 = emit_goto(s, OP_if_false, -1);
26821 if (s->cur_func->js_mode & JS_MODE_STRICT)
26822 mask = 0;
26823 else
26824 mask = DECL_MASK_FUNC; /* Annex B.3.4 */
26825
26826 if (js_parse_statement_or_decl(s, mask))
26827 goto fail;
26828
26829 if (s->token.val == TOK_ELSE) {
26830 label2 = emit_goto(s, OP_goto, -1);
26831 if (next_token(s))
26832 goto fail;
26833
26834 emit_label(s, label1);
26835 if (js_parse_statement_or_decl(s, mask))
26836 goto fail;
26837
26838 label1 = label2;
26839 }
26840 emit_label(s, label1);
26841 pop_scope(s);
26842 }
26843 break;
26844 case TOK_WHILE:
26845 {
26846 int label_cont, label_break;
26847 BlockEnv break_entry;
26848
26849 label_cont = new_label(s);
26850 label_break = new_label(s);
26851
26852 push_break_entry(s->cur_func, &break_entry,
26853 label_name, label_break, label_cont, 0);
26854
26855 if (next_token(s))
26856 goto fail;
26857
26858 set_eval_ret_undefined(s);
26859
26860 emit_label(s, label_cont);
26861 if (js_parse_expr_paren(s))
26862 goto fail;
26863 emit_goto(s, OP_if_false, label_break);
26864
26865 if (js_parse_statement(s))
26866 goto fail;
26867 emit_goto(s, OP_goto, label_cont);
26868
26869 emit_label(s, label_break);
26870
26871 pop_break_entry(s->cur_func);
26872 }
26873 break;
26874 case TOK_DO:
26875 {
26876 int label_cont, label_break, label1;
26877 BlockEnv break_entry;
26878
26879 label_cont = new_label(s);
26880 label_break = new_label(s);
26881 label1 = new_label(s);
26882
26883 push_break_entry(s->cur_func, &break_entry,
26884 label_name, label_break, label_cont, 0);
26885
26886 if (next_token(s))
26887 goto fail;
26888
26889 emit_label(s, label1);
26890
26891 set_eval_ret_undefined(s);
26892
26893 if (js_parse_statement(s))
26894 goto fail;
26895
26896 emit_label(s, label_cont);
26897 if (js_parse_expect(s, TOK_WHILE))
26898 goto fail;
26899 if (js_parse_expr_paren(s))
26900 goto fail;
26901 /* Insert semicolon if missing */
26902 if (s->token.val == ';') {
26903 if (next_token(s))
26904 goto fail;
26905 }
26906 emit_goto(s, OP_if_true, label1);
26907
26908 emit_label(s, label_break);
26909
26910 pop_break_entry(s->cur_func);
26911 }
26912 break;
26913 case TOK_FOR:
26914 {
26915 int label_cont, label_break, label_body, label_test;
26916 int pos_cont, pos_body, block_scope_level;
26917 BlockEnv break_entry;
26918 int tok, bits;
26919 BOOL is_async;
26920
26921 if (next_token(s))
26922 goto fail;
26923
26924 set_eval_ret_undefined(s);
26925 bits = 0;
26926 is_async = FALSE;
26927 if (s->token.val == '(') {
26928 js_parse_skip_parens_token(s, &bits, FALSE);
26929 } else if (s->token.val == TOK_AWAIT) {
26930 if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
26931 js_parse_error(s, "for await is only valid in asynchronous functions");
26932 goto fail;
26933 }
26934 is_async = TRUE;
26935 if (next_token(s))
26936 goto fail;
26937 s->cur_func->has_await = TRUE;
26938 }
26939 if (js_parse_expect(s, '('))
26940 goto fail;
26941
26942 if (!(bits & SKIP_HAS_SEMI)) {
26943 /* parse for/in or for/of */
26944 if (js_parse_for_in_of(s, label_name, is_async))
26945 goto fail;
26946 break;
26947 }
26948 block_scope_level = s->cur_func->scope_level;
26949
26950 /* create scope for the lexical variables declared in the initial,
26951 test and increment expressions */
26952 push_scope(s);
26953 /* initial expression */
26954 tok = s->token.val;
26955 if (tok != ';') {
26956 switch (is_let(s, DECL_MASK_OTHER)) {
26957 case TRUE:
26958 tok = TOK_LET;
26959 break;
26960 case FALSE:
26961 break;
26962 default:
26963 goto fail;
26964 }
26965 if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
26966 if (next_token(s))
26967 goto fail;
26968 if (js_parse_var(s, FALSE, tok, FALSE))
26969 goto fail;
26970 } else {
26971 if (js_parse_expr2(s, FALSE))
26972 goto fail;
26973 emit_op(s, OP_drop);
26974 }
26975
26976 /* close the closures before the first iteration */
26977 close_scopes(s, s->cur_func->scope_level, block_scope_level);
26978 }
26979 if (js_parse_expect(s, ';'))
26980 goto fail;
26981
26982 label_test = new_label(s);
26983 label_cont = new_label(s);
26984 label_body = new_label(s);
26985 label_break = new_label(s);
26986
26987 push_break_entry(s->cur_func, &break_entry,
26988 label_name, label_break, label_cont, 0);
26989
26990 /* test expression */
26991 if (s->token.val == ';') {
26992 /* no test expression */
26993 label_test = label_body;
26994 } else {
26995 emit_label(s, label_test);
26996 if (js_parse_expr(s))
26997 goto fail;
26998 emit_goto(s, OP_if_false, label_break);
26999 }
27000 if (js_parse_expect(s, ';'))
27001 goto fail;
27002
27003 if (s->token.val == ')') {
27004 /* no end expression */
27005 break_entry.label_cont = label_cont = label_test;
27006 pos_cont = 0; /* avoid warning */
27007 } else {
27008 /* skip the end expression */
27009 emit_goto(s, OP_goto, label_body);
27010
27011 pos_cont = s->cur_func->byte_code.size;
27012 emit_label(s, label_cont);
27013 if (js_parse_expr(s))
27014 goto fail;
27015 emit_op(s, OP_drop);
27016 if (label_test != label_body)
27017 emit_goto(s, OP_goto, label_test);
27018 }
27019 if (js_parse_expect(s, ')'))
27020 goto fail;
27021
27022 pos_body = s->cur_func->byte_code.size;
27023 emit_label(s, label_body);
27024 if (js_parse_statement(s))
27025 goto fail;
27026
27027 /* close the closures before the next iteration */
27028 /* XXX: check continue case */
27029 close_scopes(s, s->cur_func->scope_level, block_scope_level);
27030
27031 if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
27032 /* move the increment code here */
27033 DynBuf *bc = &s->cur_func->byte_code;
27034 int chunk_size = pos_body - pos_cont;
27035 int offset = bc->size - pos_cont;
27036 int i;
27037 dbuf_realloc(bc, bc->size + chunk_size);
27038 dbuf_put(bc, bc->buf + pos_cont, chunk_size);
27039 memset(bc->buf + pos_cont, OP_nop, chunk_size);
27040 /* increment part ends with a goto */
27041 s->cur_func->last_opcode_pos = bc->size - 5;
27042 /* relocate labels */
27043 for (i = label_cont; i < s->cur_func->label_count; i++) {
27044 LabelSlot *ls = &s->cur_func->label_slots[i];
27045 if (ls->pos >= pos_cont && ls->pos < pos_body)
27046 ls->pos += offset;
27047 }
27048 } else {
27049 emit_goto(s, OP_goto, label_cont);
27050 }
27051
27052 emit_label(s, label_break);
27053
27054 pop_break_entry(s->cur_func);
27055 pop_scope(s);
27056 }
27057 break;
27058 case TOK_BREAK:
27059 case TOK_CONTINUE:
27060 {
27061 int is_cont = s->token.val - TOK_BREAK;
27062 int label;
27063
27064 if (next_token(s))
27065 goto fail;
27066 if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
27067 label = s->token.u.ident.atom;
27068 else
27069 label = JS_ATOM_NULL;
27070 if (emit_break(s, label, is_cont))
27071 goto fail;
27072 if (label != JS_ATOM_NULL) {
27073 if (next_token(s))
27074 goto fail;
27075 }
27076 if (js_parse_expect_semi(s))
27077 goto fail;
27078 }
27079 break;
27080 case TOK_SWITCH:
27081 {
27082 int label_case, label_break, label1;
27083 int default_label_pos;
27084 BlockEnv break_entry;
27085
27086 if (next_token(s))
27087 goto fail;
27088
27089 set_eval_ret_undefined(s);
27090 if (js_parse_expr_paren(s))
27091 goto fail;
27092
27093 push_scope(s);
27094 label_break = new_label(s);
27095 push_break_entry(s->cur_func, &break_entry,
27096 label_name, label_break, -1, 1);
27097
27098 if (js_parse_expect(s, '{'))
27099 goto fail;
27100
27101 default_label_pos = -1;
27102 label_case = -1;
27103 while (s->token.val != '}') {
27104 if (s->token.val == TOK_CASE) {
27105 label1 = -1;
27106 if (label_case >= 0) {
27107 /* skip the case if needed */
27108 label1 = emit_goto(s, OP_goto, -1);
27109 }
27110 emit_label(s, label_case);
27111 label_case = -1;
27112 for (;;) {
27113 /* parse a sequence of case clauses */
27114 if (next_token(s))
27115 goto fail;
27116 emit_op(s, OP_dup);
27117 if (js_parse_expr(s))
27118 goto fail;
27119 if (js_parse_expect(s, ':'))
27120 goto fail;
27121 emit_op(s, OP_strict_eq);
27122 if (s->token.val == TOK_CASE) {
27123 label1 = emit_goto(s, OP_if_true, label1);
27124 } else {
27125 label_case = emit_goto(s, OP_if_false, -1);
27126 emit_label(s, label1);
27127 break;
27128 }
27129 }
27130 } else if (s->token.val == TOK_DEFAULT) {
27131 if (next_token(s))
27132 goto fail;
27133 if (js_parse_expect(s, ':'))
27134 goto fail;
27135 if (default_label_pos >= 0) {
27136 js_parse_error(s, "duplicate default");
27137 goto fail;
27138 }
27139 if (label_case < 0) {
27140 /* falling thru direct from switch expression */
27141 label_case = emit_goto(s, OP_goto, -1);
27142 }
27143 /* Emit a dummy label opcode. Label will be patched after
27144 the end of the switch body. Do not use emit_label(s, 0)
27145 because it would clobber label 0 address, preventing
27146 proper optimizer operation.
27147 */
27148 emit_op(s, OP_label);
27149 emit_u32(s, 0);
27150 default_label_pos = s->cur_func->byte_code.size - 4;
27151 } else {
27152 if (label_case < 0) {
27153 /* falling thru direct from switch expression */
27154 js_parse_error(s, "invalid switch statement");
27155 goto fail;
27156 }
27157 if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
27158 goto fail;
27159 }
27160 }
27161 if (js_parse_expect(s, '}'))
27162 goto fail;
27163 if (default_label_pos >= 0) {
27164 /* Ugly patch for the the `default` label, shameful and risky */
27165 put_u32(s->cur_func->byte_code.buf + default_label_pos,
27166 label_case);
27167 s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
27168 } else {
27169 emit_label(s, label_case);
27170 }
27171 emit_label(s, label_break);
27172 emit_op(s, OP_drop); /* drop the switch expression */
27173
27174 pop_break_entry(s->cur_func);
27175 pop_scope(s);
27176 }
27177 break;
27178 case TOK_TRY:
27179 {
27180 int label_catch, label_catch2, label_finally, label_end;
27181 JSAtom name;
27182 BlockEnv block_env;
27183
27184 set_eval_ret_undefined(s);
27185 if (next_token(s))
27186 goto fail;
27187 label_catch = new_label(s);
27188 label_catch2 = new_label(s);
27189 label_finally = new_label(s);
27190 label_end = new_label(s);
27191
27192 emit_goto(s, OP_catch, label_catch);
27193
27194 push_break_entry(s->cur_func, &block_env,
27195 JS_ATOM_NULL, -1, -1, 1);
27196 block_env.label_finally = label_finally;
27197
27198 if (js_parse_block(s))
27199 goto fail;
27200
27201 pop_break_entry(s->cur_func);
27202
27203 if (js_is_live_code(s)) {
27204 /* drop the catch offset */
27205 emit_op(s, OP_drop);
27206 /* must push dummy value to keep same stack size */
27207 emit_op(s, OP_undefined);
27208 emit_goto(s, OP_gosub, label_finally);
27209 emit_op(s, OP_drop);
27210
27211 emit_goto(s, OP_goto, label_end);
27212 }
27213
27214 if (s->token.val == TOK_CATCH) {
27215 if (next_token(s))
27216 goto fail;
27217
27218 push_scope(s); /* catch variable */
27219 emit_label(s, label_catch);
27220
27221 if (s->token.val == '{') {
27222 /* support optional-catch-binding feature */
27223 emit_op(s, OP_drop); /* pop the exception object */
27224 } else {
27225 if (js_parse_expect(s, '('))
27226 goto fail;
27227 if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
27228 if (s->token.val == '[' || s->token.val == '{') {
27229 /* XXX: TOK_LET is not completely correct */
27230 if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE, FALSE) < 0)
27231 goto fail;
27232 } else {
27233 js_parse_error(s, "identifier expected");
27234 goto fail;
27235 }
27236 } else {
27237 name = JS_DupAtom(ctx, s->token.u.ident.atom);
27238 if (next_token(s)
27239 || js_define_var(s, name, TOK_CATCH) < 0) {
27240 JS_FreeAtom(ctx, name);
27241 goto fail;
27242 }
27243 /* store the exception value in the catch variable */
27244 emit_op(s, OP_scope_put_var);
27245 emit_u32(s, name);
27246 emit_u16(s, s->cur_func->scope_level);
27247 }
27248 if (js_parse_expect(s, ')'))
27249 goto fail;
27250 }
27251 /* XXX: should keep the address to nop it out if there is no finally block */
27252 emit_goto(s, OP_catch, label_catch2);
27253
27254 push_scope(s); /* catch block */
27255 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
27256 -1, -1, 1);
27257 block_env.label_finally = label_finally;
27258
27259 if (js_parse_block(s))
27260 goto fail;
27261
27262 pop_break_entry(s->cur_func);
27263 pop_scope(s); /* catch block */
27264 pop_scope(s); /* catch variable */
27265
27266 if (js_is_live_code(s)) {
27267 /* drop the catch2 offset */
27268 emit_op(s, OP_drop);
27269 /* XXX: should keep the address to nop it out if there is no finally block */
27270 /* must push dummy value to keep same stack size */
27271 emit_op(s, OP_undefined);
27272 emit_goto(s, OP_gosub, label_finally);
27273 emit_op(s, OP_drop);
27274 emit_goto(s, OP_goto, label_end);
27275 }
27276 /* catch exceptions thrown in the catch block to execute the
27277 * finally clause and rethrow the exception */
27278 emit_label(s, label_catch2);
27279 /* catch value is at TOS, no need to push undefined */
27280 emit_goto(s, OP_gosub, label_finally);
27281 emit_op(s, OP_throw);
27282
27283 } else if (s->token.val == TOK_FINALLY) {
27284 /* finally without catch : execute the finally clause
27285 * and rethrow the exception */
27286 emit_label(s, label_catch);
27287 /* catch value is at TOS, no need to push undefined */
27288 emit_goto(s, OP_gosub, label_finally);
27289 emit_op(s, OP_throw);
27290 } else {
27291 js_parse_error(s, "expecting catch or finally");
27292 goto fail;
27293 }
27294 emit_label(s, label_finally);
27295 if (s->token.val == TOK_FINALLY) {
27296 int saved_eval_ret_idx = 0; /* avoid warning */
27297
27298 if (next_token(s))
27299 goto fail;
27300 /* on the stack: ret_value gosub_ret_value */
27301 push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
27302 -1, -1, 2);
27303
27304 if (s->cur_func->eval_ret_idx >= 0) {
27305 /* 'finally' updates eval_ret only if not a normal
27306 termination */
27307 saved_eval_ret_idx =
27308 add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
27309 if (saved_eval_ret_idx < 0)
27310 goto fail;
27311 emit_op(s, OP_get_loc);
27312 emit_u16(s, s->cur_func->eval_ret_idx);
27313 emit_op(s, OP_put_loc);
27314 emit_u16(s, saved_eval_ret_idx);
27315 set_eval_ret_undefined(s);
27316 }
27317
27318 if (js_parse_block(s))
27319 goto fail;
27320
27321 if (s->cur_func->eval_ret_idx >= 0) {
27322 emit_op(s, OP_get_loc);
27323 emit_u16(s, saved_eval_ret_idx);
27324 emit_op(s, OP_put_loc);
27325 emit_u16(s, s->cur_func->eval_ret_idx);
27326 }
27327 pop_break_entry(s->cur_func);
27328 }
27329 emit_op(s, OP_ret);
27330 emit_label(s, label_end);
27331 }
27332 break;
27333 case ';':
27334 /* empty statement */
27335 if (next_token(s))
27336 goto fail;
27337 break;
27338 case TOK_WITH:
27339 if (s->cur_func->js_mode & JS_MODE_STRICT) {
27340 js_parse_error(s, "invalid keyword: with");
27341 goto fail;
27342 } else {
27343 int with_idx;
27344
27345 if (next_token(s))
27346 goto fail;
27347
27348 if (js_parse_expr_paren(s))
27349 goto fail;
27350
27351 push_scope(s);
27352 with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
27353 JS_VAR_DEF_WITH);
27354 if (with_idx < 0)
27355 goto fail;
27356 emit_op(s, OP_to_object);
27357 emit_op(s, OP_put_loc);
27358 emit_u16(s, with_idx);
27359
27360 set_eval_ret_undefined(s);
27361 if (js_parse_statement(s))
27362 goto fail;
27363
27364 /* Popping scope drops lexical context for the with object variable */
27365 pop_scope(s);
27366 }
27367 break;
27368 case TOK_FUNCTION:
27369 /* ES6 Annex B.3.2 and B.3.3 semantics */
27370 if (!(decl_mask & DECL_MASK_FUNC))
27371 goto func_decl_error;
27372 if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
27373 goto func_decl_error;
27374 goto parse_func_var;
27375 case TOK_IDENT:
27376 if (s->token.u.ident.is_reserved) {
27377 js_parse_error_reserved_identifier(s);
27378 goto fail;
27379 }
27380 /* Determine if `let` introduces a Declaration or an ExpressionStatement */
27381 switch (is_let(s, decl_mask)) {
27382 case TRUE:
27383 tok = TOK_LET;
27384 goto haslet;
27385 case FALSE:
27386 break;
27387 default:
27388 goto fail;
27389 }
27390 if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
27391 peek_token(s, TRUE) == TOK_FUNCTION) {
27392 if (!(decl_mask & DECL_MASK_OTHER)) {
27393 func_decl_error:
27394 js_parse_error(s, "function declarations can't appear in single-statement context");
27395 goto fail;
27396 }
27397 parse_func_var:
27398 if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
27399 JS_FUNC_NORMAL, JS_ATOM_NULL,
27400 s->token.ptr))
27401 goto fail;
27402 break;
27403 }
27404 goto hasexpr;
27405
27406 case TOK_CLASS:
27407 if (!(decl_mask & DECL_MASK_OTHER)) {
27408 js_parse_error(s, "class declarations can't appear in single-statement context");
27409 goto fail;
27410 }
27411 if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
27412 return -1;
27413 break;
27414
27415 case TOK_DEBUGGER:
27416 /* currently no debugger, so just skip the keyword */
27417 if (next_token(s))
27418 goto fail;
27419 if (js_parse_expect_semi(s))
27420 goto fail;
27421 break;
27422
27423 case TOK_ENUM:
27424 case TOK_EXPORT:
27425 case TOK_EXTENDS:
27426 js_unsupported_keyword(s, s->token.u.ident.atom);
27427 goto fail;
27428
27429 default:
27430 hasexpr:
27431 emit_source_pos(s, s->token.ptr);
27432 if (js_parse_expr(s))
27433 goto fail;
27434 if (s->cur_func->eval_ret_idx >= 0) {
27435 /* store the expression value so that it can be returned
27436 by eval() */
27437 emit_op(s, OP_put_loc);
27438 emit_u16(s, s->cur_func->eval_ret_idx);
27439 } else {
27440 emit_op(s, OP_drop); /* drop the result */
27441 }
27442 if (js_parse_expect_semi(s))
27443 goto fail;
27444 break;
27445 }
27446done:
27447 JS_FreeAtom(ctx, label_name);
27448 return 0;
27449fail:
27450 JS_FreeAtom(ctx, label_name);
27451 return -1;
27452}
27453
27454/* 'name' is freed */
27455static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
27456{
27457 JSModuleDef *m;
27458 m = js_mallocz(ctx, sizeof(*m));
27459 if (!m) {
27460 JS_FreeAtom(ctx, name);
27461 return NULL;
27462 }
27463 m->header.ref_count = 1;
27464 m->module_name = name;
27465 m->module_ns = JS_UNDEFINED;
27466 m->func_obj = JS_UNDEFINED;
27467 m->eval_exception = JS_UNDEFINED;
27468 m->meta_obj = JS_UNDEFINED;
27469 m->promise = JS_UNDEFINED;
27470 m->resolving_funcs[0] = JS_UNDEFINED;
27471 m->resolving_funcs[1] = JS_UNDEFINED;
27472 list_add_tail(&m->link, &ctx->loaded_modules);
27473 return m;
27474}
27475
27476static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
27477 JS_MarkFunc *mark_func)
27478{
27479 int i;
27480
27481 for(i = 0; i < m->export_entries_count; i++) {
27482 JSExportEntry *me = &m->export_entries[i];
27483 if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
27484 me->u.local.var_ref) {
27485 mark_func(rt, &me->u.local.var_ref->header);
27486 }
27487 }
27488
27489 JS_MarkValue(rt, m->module_ns, mark_func);
27490 JS_MarkValue(rt, m->func_obj, mark_func);
27491 JS_MarkValue(rt, m->eval_exception, mark_func);
27492 JS_MarkValue(rt, m->meta_obj, mark_func);
27493 JS_MarkValue(rt, m->promise, mark_func);
27494 JS_MarkValue(rt, m->resolving_funcs[0], mark_func);
27495 JS_MarkValue(rt, m->resolving_funcs[1], mark_func);
27496}
27497
27498static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
27499{
27500 int i;
27501
27502 JS_FreeAtom(ctx, m->module_name);
27503
27504 for(i = 0; i < m->req_module_entries_count; i++) {
27505 JSReqModuleEntry *rme = &m->req_module_entries[i];
27506 JS_FreeAtom(ctx, rme->module_name);
27507 }
27508 js_free(ctx, m->req_module_entries);
27509
27510 for(i = 0; i < m->export_entries_count; i++) {
27511 JSExportEntry *me = &m->export_entries[i];
27512 if (me->export_type == JS_EXPORT_TYPE_LOCAL)
27513 free_var_ref(ctx->rt, me->u.local.var_ref);
27514 JS_FreeAtom(ctx, me->export_name);
27515 JS_FreeAtom(ctx, me->local_name);
27516 }
27517 js_free(ctx, m->export_entries);
27518
27519 js_free(ctx, m->star_export_entries);
27520
27521 for(i = 0; i < m->import_entries_count; i++) {
27522 JSImportEntry *mi = &m->import_entries[i];
27523 JS_FreeAtom(ctx, mi->import_name);
27524 }
27525 js_free(ctx, m->import_entries);
27526 js_free(ctx, m->async_parent_modules);
27527
27528 JS_FreeValue(ctx, m->module_ns);
27529 JS_FreeValue(ctx, m->func_obj);
27530 JS_FreeValue(ctx, m->eval_exception);
27531 JS_FreeValue(ctx, m->meta_obj);
27532 JS_FreeValue(ctx, m->promise);
27533 JS_FreeValue(ctx, m->resolving_funcs[0]);
27534 JS_FreeValue(ctx, m->resolving_funcs[1]);
27535 list_del(&m->link);
27536 js_free(ctx, m);
27537}
27538
27539static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
27540 JSAtom module_name)
27541{
27542 JSReqModuleEntry *rme;
27543 int i;
27544
27545 /* no need to add the module request if it is already present */
27546 for(i = 0; i < m->req_module_entries_count; i++) {
27547 rme = &m->req_module_entries[i];
27548 if (rme->module_name == module_name)
27549 return i;
27550 }
27551
27552 if (js_resize_array(ctx, (void **)&m->req_module_entries,
27553 sizeof(JSReqModuleEntry),
27554 &m->req_module_entries_size,
27555 m->req_module_entries_count + 1))
27556 return -1;
27557 rme = &m->req_module_entries[m->req_module_entries_count++];
27558 rme->module_name = JS_DupAtom(ctx, module_name);
27559 rme->module = NULL;
27560 return i;
27561}
27562
27563static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
27564 JSAtom export_name)
27565{
27566 JSExportEntry *me;
27567 int i;
27568 for(i = 0; i < m->export_entries_count; i++) {
27569 me = &m->export_entries[i];
27570 if (me->export_name == export_name)
27571 return me;
27572 }
27573 return NULL;
27574}
27575
27576static JSExportEntry *add_export_entry2(JSContext *ctx,
27577 JSParseState *s, JSModuleDef *m,
27578 JSAtom local_name, JSAtom export_name,
27579 JSExportTypeEnum export_type)
27580{
27581 JSExportEntry *me;
27582
27583 if (find_export_entry(ctx, m, export_name)) {
27584 char buf1[ATOM_GET_STR_BUF_SIZE];
27585 if (s) {
27586 js_parse_error(s, "duplicate exported name '%s'",
27587 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
27588 } else {
27589 JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
27590 }
27591 return NULL;
27592 }
27593
27594 if (js_resize_array(ctx, (void **)&m->export_entries,
27595 sizeof(JSExportEntry),
27596 &m->export_entries_size,
27597 m->export_entries_count + 1))
27598 return NULL;
27599 me = &m->export_entries[m->export_entries_count++];
27600 memset(me, 0, sizeof(*me));
27601 me->local_name = JS_DupAtom(ctx, local_name);
27602 me->export_name = JS_DupAtom(ctx, export_name);
27603 me->export_type = export_type;
27604 return me;
27605}
27606
27607static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
27608 JSAtom local_name, JSAtom export_name,
27609 JSExportTypeEnum export_type)
27610{
27611 return add_export_entry2(s->ctx, s, m, local_name, export_name,
27612 export_type);
27613}
27614
27615static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
27616 int req_module_idx)
27617{
27618 JSStarExportEntry *se;
27619
27620 if (js_resize_array(ctx, (void **)&m->star_export_entries,
27621 sizeof(JSStarExportEntry),
27622 &m->star_export_entries_size,
27623 m->star_export_entries_count + 1))
27624 return -1;
27625 se = &m->star_export_entries[m->star_export_entries_count++];
27626 se->req_module_idx = req_module_idx;
27627 return 0;
27628}
27629
27630/* create a C module */
27631JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
27632 JSModuleInitFunc *func)
27633{
27634 JSModuleDef *m;
27635 JSAtom name;
27636 name = JS_NewAtom(ctx, name_str);
27637 if (name == JS_ATOM_NULL)
27638 return NULL;
27639 m = js_new_module_def(ctx, name);
27640 m->init_func = func;
27641 return m;
27642}
27643
27644int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
27645{
27646 JSExportEntry *me;
27647 JSAtom name;
27648 name = JS_NewAtom(ctx, export_name);
27649 if (name == JS_ATOM_NULL)
27650 return -1;
27651 me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
27652 JS_EXPORT_TYPE_LOCAL);
27653 JS_FreeAtom(ctx, name);
27654 if (!me)
27655 return -1;
27656 else
27657 return 0;
27658}
27659
27660int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
27661 JSValue val)
27662{
27663 JSExportEntry *me;
27664 JSAtom name;
27665 name = JS_NewAtom(ctx, export_name);
27666 if (name == JS_ATOM_NULL)
27667 goto fail;
27668 me = find_export_entry(ctx, m, name);
27669 JS_FreeAtom(ctx, name);
27670 if (!me)
27671 goto fail;
27672 set_value(ctx, me->u.local.var_ref->pvalue, val);
27673 return 0;
27674 fail:
27675 JS_FreeValue(ctx, val);
27676 return -1;
27677}
27678
27679void JS_SetModuleLoaderFunc(JSRuntime *rt,
27680 JSModuleNormalizeFunc *module_normalize,
27681 JSModuleLoaderFunc *module_loader, void *opaque)
27682{
27683 rt->module_normalize_func = module_normalize;
27684 rt->module_loader_func = module_loader;
27685 rt->module_loader_opaque = opaque;
27686}
27687
27688/* default module filename normalizer */
27689static char *js_default_module_normalize_name(JSContext *ctx,
27690 const char *base_name,
27691 const char *name)
27692{
27693 char *filename, *p;
27694 const char *r;
27695 int cap;
27696 int len;
27697
27698 if (name[0] != '.') {
27699 /* if no initial dot, the module name is not modified */
27700 return js_strdup(ctx, name);
27701 }
27702
27703 p = strrchr(base_name, '/');
27704 if (p)
27705 len = p - base_name;
27706 else
27707 len = 0;
27708
27709 cap = len + strlen(name) + 1 + 1;
27710 filename = js_malloc(ctx, cap);
27711 if (!filename)
27712 return NULL;
27713 memcpy(filename, base_name, len);
27714 filename[len] = '\0';
27715
27716 /* we only normalize the leading '..' or '.' */
27717 r = name;
27718 for(;;) {
27719 if (r[0] == '.' && r[1] == '/') {
27720 r += 2;
27721 } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
27722 /* remove the last path element of filename, except if "."
27723 or ".." */
27724 if (filename[0] == '\0')
27725 break;
27726 p = strrchr(filename, '/');
27727 if (!p)
27728 p = filename;
27729 else
27730 p++;
27731 if (!strcmp(p, ".") || !strcmp(p, ".."))
27732 break;
27733 if (p > filename)
27734 p--;
27735 *p = '\0';
27736 r += 3;
27737 } else {
27738 break;
27739 }
27740 }
27741 if (filename[0] != '\0')
27742 pstrcat(filename, cap, "/");
27743 pstrcat(filename, cap, r);
27744 // printf("normalize: %s %s -> %s\n", base_name, name, filename);
27745 return filename;
27746}
27747
27748static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
27749{
27750 struct list_head *el;
27751 JSModuleDef *m;
27752
27753 /* first look at the loaded modules */
27754 list_for_each(el, &ctx->loaded_modules) {
27755 m = list_entry(el, JSModuleDef, link);
27756 if (m->module_name == name)
27757 return m;
27758 }
27759 return NULL;
27760}
27761
27762/* return NULL in case of exception (e.g. module could not be loaded) */
27763static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
27764 const char *base_cname,
27765 const char *cname1)
27766{
27767 JSRuntime *rt = ctx->rt;
27768 JSModuleDef *m;
27769 char *cname;
27770 JSAtom module_name;
27771
27772 if (!rt->module_normalize_func) {
27773 cname = js_default_module_normalize_name(ctx, base_cname, cname1);
27774 } else {
27775 cname = rt->module_normalize_func(ctx, base_cname, cname1,
27776 rt->module_loader_opaque);
27777 }
27778 if (!cname)
27779 return NULL;
27780
27781 module_name = JS_NewAtom(ctx, cname);
27782 if (module_name == JS_ATOM_NULL) {
27783 js_free(ctx, cname);
27784 return NULL;
27785 }
27786
27787 /* first look at the loaded modules */
27788 m = js_find_loaded_module(ctx, module_name);
27789 if (m) {
27790 js_free(ctx, cname);
27791 JS_FreeAtom(ctx, module_name);
27792 return m;
27793 }
27794
27795 JS_FreeAtom(ctx, module_name);
27796
27797 /* load the module */
27798 if (!rt->module_loader_func) {
27799 /* XXX: use a syntax error ? */
27800 JS_ThrowReferenceError(ctx, "could not load module '%s'",
27801 cname);
27802 js_free(ctx, cname);
27803 return NULL;
27804 }
27805
27806 m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
27807 js_free(ctx, cname);
27808 return m;
27809}
27810
27811static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
27812 JSAtom base_module_name,
27813 JSAtom module_name1)
27814{
27815 const char *base_cname, *cname;
27816 JSModuleDef *m;
27817
27818 base_cname = JS_AtomToCString(ctx, base_module_name);
27819 if (!base_cname)
27820 return NULL;
27821 cname = JS_AtomToCString(ctx, module_name1);
27822 if (!cname) {
27823 JS_FreeCString(ctx, base_cname);
27824 return NULL;
27825 }
27826 m = js_host_resolve_imported_module(ctx, base_cname, cname);
27827 JS_FreeCString(ctx, base_cname);
27828 JS_FreeCString(ctx, cname);
27829 return m;
27830}
27831
27832typedef struct JSResolveEntry {
27833 JSModuleDef *module;
27834 JSAtom name;
27835} JSResolveEntry;
27836
27837typedef struct JSResolveState {
27838 JSResolveEntry *array;
27839 int size;
27840 int count;
27841} JSResolveState;
27842
27843static int find_resolve_entry(JSResolveState *s,
27844 JSModuleDef *m, JSAtom name)
27845{
27846 int i;
27847 for(i = 0; i < s->count; i++) {
27848 JSResolveEntry *re = &s->array[i];
27849 if (re->module == m && re->name == name)
27850 return i;
27851 }
27852 return -1;
27853}
27854
27855static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
27856 JSModuleDef *m, JSAtom name)
27857{
27858 JSResolveEntry *re;
27859
27860 if (js_resize_array(ctx, (void **)&s->array,
27861 sizeof(JSResolveEntry),
27862 &s->size, s->count + 1))
27863 return -1;
27864 re = &s->array[s->count++];
27865 re->module = m;
27866 re->name = JS_DupAtom(ctx, name);
27867 return 0;
27868}
27869
27870typedef enum JSResolveResultEnum {
27871 JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
27872 JS_RESOLVE_RES_FOUND = 0,
27873 JS_RESOLVE_RES_NOT_FOUND,
27874 JS_RESOLVE_RES_CIRCULAR,
27875 JS_RESOLVE_RES_AMBIGUOUS,
27876} JSResolveResultEnum;
27877
27878static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
27879 JSModuleDef **pmodule,
27880 JSExportEntry **pme,
27881 JSModuleDef *m,
27882 JSAtom export_name,
27883 JSResolveState *s)
27884{
27885 JSExportEntry *me;
27886
27887 *pmodule = NULL;
27888 *pme = NULL;
27889 if (find_resolve_entry(s, m, export_name) >= 0)
27890 return JS_RESOLVE_RES_CIRCULAR;
27891 if (add_resolve_entry(ctx, s, m, export_name) < 0)
27892 return JS_RESOLVE_RES_EXCEPTION;
27893 me = find_export_entry(ctx, m, export_name);
27894 if (me) {
27895 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
27896 /* local export */
27897 *pmodule = m;
27898 *pme = me;
27899 return JS_RESOLVE_RES_FOUND;
27900 } else {
27901 /* indirect export */
27902 JSModuleDef *m1;
27903 m1 = m->req_module_entries[me->u.req_module_idx].module;
27904 if (me->local_name == JS_ATOM__star_) {
27905 /* export ns from */
27906 *pmodule = m;
27907 *pme = me;
27908 return JS_RESOLVE_RES_FOUND;
27909 } else {
27910 return js_resolve_export1(ctx, pmodule, pme, m1,
27911 me->local_name, s);
27912 }
27913 }
27914 } else {
27915 if (export_name != JS_ATOM_default) {
27916 /* not found in direct or indirect exports: try star exports */
27917 int i;
27918
27919 for(i = 0; i < m->star_export_entries_count; i++) {
27920 JSStarExportEntry *se = &m->star_export_entries[i];
27921 JSModuleDef *m1, *res_m;
27922 JSExportEntry *res_me;
27923 JSResolveResultEnum ret;
27924
27925 m1 = m->req_module_entries[se->req_module_idx].module;
27926 ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
27927 export_name, s);
27928 if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
27929 ret == JS_RESOLVE_RES_EXCEPTION) {
27930 return ret;
27931 } else if (ret == JS_RESOLVE_RES_FOUND) {
27932 if (*pme != NULL) {
27933 if (*pmodule != res_m ||
27934 res_me->local_name != (*pme)->local_name) {
27935 *pmodule = NULL;
27936 *pme = NULL;
27937 return JS_RESOLVE_RES_AMBIGUOUS;
27938 }
27939 } else {
27940 *pmodule = res_m;
27941 *pme = res_me;
27942 }
27943 }
27944 }
27945 if (*pme != NULL)
27946 return JS_RESOLVE_RES_FOUND;
27947 }
27948 return JS_RESOLVE_RES_NOT_FOUND;
27949 }
27950}
27951
27952/* If the return value is JS_RESOLVE_RES_FOUND, return the module
27953 (*pmodule) and the corresponding local export entry
27954 (*pme). Otherwise return (NULL, NULL) */
27955static JSResolveResultEnum js_resolve_export(JSContext *ctx,
27956 JSModuleDef **pmodule,
27957 JSExportEntry **pme,
27958 JSModuleDef *m,
27959 JSAtom export_name)
27960{
27961 JSResolveState ss, *s = &ss;
27962 int i;
27963 JSResolveResultEnum ret;
27964
27965 s->array = NULL;
27966 s->size = 0;
27967 s->count = 0;
27968
27969 ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
27970
27971 for(i = 0; i < s->count; i++)
27972 JS_FreeAtom(ctx, s->array[i].name);
27973 js_free(ctx, s->array);
27974
27975 return ret;
27976}
27977
27978static void js_resolve_export_throw_error(JSContext *ctx,
27979 JSResolveResultEnum res,
27980 JSModuleDef *m, JSAtom export_name)
27981{
27982 char buf1[ATOM_GET_STR_BUF_SIZE];
27983 char buf2[ATOM_GET_STR_BUF_SIZE];
27984 switch(res) {
27985 case JS_RESOLVE_RES_EXCEPTION:
27986 break;
27987 default:
27988 case JS_RESOLVE_RES_NOT_FOUND:
27989 JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
27990 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27991 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27992 break;
27993 case JS_RESOLVE_RES_CIRCULAR:
27994 JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
27995 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
27996 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
27997 break;
27998 case JS_RESOLVE_RES_AMBIGUOUS:
27999 JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
28000 JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
28001 JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
28002 break;
28003 }
28004}
28005
28006
28007typedef enum {
28008 EXPORTED_NAME_AMBIGUOUS,
28009 EXPORTED_NAME_NORMAL,
28010 EXPORTED_NAME_DELAYED,
28011} ExportedNameEntryEnum;
28012
28013typedef struct ExportedNameEntry {
28014 JSAtom export_name;
28015 ExportedNameEntryEnum export_type;
28016 union {
28017 JSExportEntry *me; /* using when the list is built */
28018 JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
28019 } u;
28020} ExportedNameEntry;
28021
28022typedef struct GetExportNamesState {
28023 JSModuleDef **modules;
28024 int modules_size;
28025 int modules_count;
28026
28027 ExportedNameEntry *exported_names;
28028 int exported_names_size;
28029 int exported_names_count;
28030} GetExportNamesState;
28031
28032static int find_exported_name(GetExportNamesState *s, JSAtom name)
28033{
28034 int i;
28035 for(i = 0; i < s->exported_names_count; i++) {
28036 if (s->exported_names[i].export_name == name)
28037 return i;
28038 }
28039 return -1;
28040}
28041
28042static __exception int get_exported_names(JSContext *ctx,
28043 GetExportNamesState *s,
28044 JSModuleDef *m, BOOL from_star)
28045{
28046 ExportedNameEntry *en;
28047 int i, j;
28048
28049 /* check circular reference */
28050 for(i = 0; i < s->modules_count; i++) {
28051 if (s->modules[i] == m)
28052 return 0;
28053 }
28054 if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
28055 &s->modules_size, s->modules_count + 1))
28056 return -1;
28057 s->modules[s->modules_count++] = m;
28058
28059 for(i = 0; i < m->export_entries_count; i++) {
28060 JSExportEntry *me = &m->export_entries[i];
28061 if (from_star && me->export_name == JS_ATOM_default)
28062 continue;
28063 j = find_exported_name(s, me->export_name);
28064 if (j < 0) {
28065 if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
28066 &s->exported_names_size,
28067 s->exported_names_count + 1))
28068 return -1;
28069 en = &s->exported_names[s->exported_names_count++];
28070 en->export_name = me->export_name;
28071 /* avoid a second lookup for simple module exports */
28072 if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
28073 en->u.me = NULL;
28074 else
28075 en->u.me = me;
28076 } else {
28077 en = &s->exported_names[j];
28078 en->u.me = NULL;
28079 }
28080 }
28081 for(i = 0; i < m->star_export_entries_count; i++) {
28082 JSStarExportEntry *se = &m->star_export_entries[i];
28083 JSModuleDef *m1;
28084 m1 = m->req_module_entries[se->req_module_idx].module;
28085 if (get_exported_names(ctx, s, m1, TRUE))
28086 return -1;
28087 }
28088 return 0;
28089}
28090
28091/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
28092static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
28093{
28094 return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
28095}
28096
28097static const JSClassExoticMethods js_module_ns_exotic_methods = {
28098 .has_property = js_module_ns_has,
28099};
28100
28101static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
28102{
28103 JSContext *ctx = opaque;
28104 const ExportedNameEntry *me1 = p1;
28105 const ExportedNameEntry *me2 = p2;
28106 JSValue str1, str2;
28107 int ret;
28108
28109 /* XXX: should avoid allocation memory in atom comparison */
28110 str1 = JS_AtomToString(ctx, me1->export_name);
28111 str2 = JS_AtomToString(ctx, me2->export_name);
28112 if (JS_IsException(str1) || JS_IsException(str2)) {
28113 /* XXX: raise an error ? */
28114 ret = 0;
28115 } else {
28116 ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
28117 JS_VALUE_GET_STRING(str2));
28118 }
28119 JS_FreeValue(ctx, str1);
28120 JS_FreeValue(ctx, str2);
28121 return ret;
28122}
28123
28124static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
28125 void *opaque)
28126{
28127 JSModuleDef *m = opaque;
28128 JSResolveResultEnum res;
28129 JSExportEntry *res_me;
28130 JSModuleDef *res_m;
28131 JSVarRef *var_ref;
28132
28133 res = js_resolve_export(ctx, &res_m, &res_me, m, atom);
28134 if (res != JS_RESOLVE_RES_FOUND) {
28135 /* fail safe: normally no error should happen here except for memory */
28136 js_resolve_export_throw_error(ctx, res, m, atom);
28137 return JS_EXCEPTION;
28138 }
28139 if (res_me->local_name == JS_ATOM__star_) {
28140 return JS_GetModuleNamespace(ctx, res_m->req_module_entries[res_me->u.req_module_idx].module);
28141 } else {
28142 if (res_me->u.local.var_ref) {
28143 var_ref = res_me->u.local.var_ref;
28144 } else {
28145 JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
28146 var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
28147 }
28148 /* WARNING: a varref is returned as a string ! */
28149 return JS_MKPTR(JS_TAG_STRING, var_ref);
28150 }
28151}
28152
28153static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
28154{
28155 JSValue obj;
28156 JSObject *p;
28157 GetExportNamesState s_s, *s = &s_s;
28158 int i, ret;
28159 JSProperty *pr;
28160
28161 obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
28162 if (JS_IsException(obj))
28163 return obj;
28164 p = JS_VALUE_GET_OBJ(obj);
28165
28166 memset(s, 0, sizeof(*s));
28167 ret = get_exported_names(ctx, s, m, FALSE);
28168 js_free(ctx, s->modules);
28169 if (ret)
28170 goto fail;
28171
28172 /* Resolve the exported names. The ambiguous exports are removed */
28173 for(i = 0; i < s->exported_names_count; i++) {
28174 ExportedNameEntry *en = &s->exported_names[i];
28175 JSResolveResultEnum res;
28176 JSExportEntry *res_me;
28177 JSModuleDef *res_m;
28178
28179 if (en->u.me) {
28180 res_me = en->u.me; /* fast case: no resolution needed */
28181 res_m = m;
28182 res = JS_RESOLVE_RES_FOUND;
28183 } else {
28184 res = js_resolve_export(ctx, &res_m, &res_me, m,
28185 en->export_name);
28186 }
28187 if (res != JS_RESOLVE_RES_FOUND) {
28188 if (res != JS_RESOLVE_RES_AMBIGUOUS) {
28189 js_resolve_export_throw_error(ctx, res, m, en->export_name);
28190 goto fail;
28191 }
28192 en->export_type = EXPORTED_NAME_AMBIGUOUS;
28193 } else {
28194 if (res_me->local_name == JS_ATOM__star_) {
28195 en->export_type = EXPORTED_NAME_DELAYED;
28196 } else {
28197 if (res_me->u.local.var_ref) {
28198 en->u.var_ref = res_me->u.local.var_ref;
28199 } else {
28200 JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
28201 en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
28202 }
28203 if (en->u.var_ref == NULL)
28204 en->export_type = EXPORTED_NAME_DELAYED;
28205 else
28206 en->export_type = EXPORTED_NAME_NORMAL;
28207 }
28208 }
28209 }
28210
28211 /* sort the exported names */
28212 rqsort(s->exported_names, s->exported_names_count,
28213 sizeof(s->exported_names[0]), exported_names_cmp, ctx);
28214
28215 for(i = 0; i < s->exported_names_count; i++) {
28216 ExportedNameEntry *en = &s->exported_names[i];
28217 switch(en->export_type) {
28218 case EXPORTED_NAME_NORMAL:
28219 {
28220 JSVarRef *var_ref = en->u.var_ref;
28221 pr = add_property(ctx, p, en->export_name,
28222 JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
28223 JS_PROP_VARREF);
28224 if (!pr)
28225 goto fail;
28226 var_ref->header.ref_count++;
28227 pr->u.var_ref = var_ref;
28228 }
28229 break;
28230 case EXPORTED_NAME_DELAYED:
28231 /* the exported namespace or reference may depend on
28232 circular references, so we resolve it lazily */
28233 if (JS_DefineAutoInitProperty(ctx, obj,
28234 en->export_name,
28235 JS_AUTOINIT_ID_MODULE_NS,
28236 m, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
28237 break;
28238 default:
28239 break;
28240 }
28241 }
28242
28243 js_free(ctx, s->exported_names);
28244
28245 JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
28246 JS_AtomToString(ctx, JS_ATOM_Module),
28247 0);
28248
28249 p->extensible = FALSE;
28250 return obj;
28251 fail:
28252 js_free(ctx, s->exported_names);
28253 JS_FreeValue(ctx, obj);
28254 return JS_EXCEPTION;
28255}
28256
28257JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m)
28258{
28259 if (JS_IsUndefined(m->module_ns)) {
28260 JSValue val;
28261 val = js_build_module_ns(ctx, m);
28262 if (JS_IsException(val))
28263 return JS_EXCEPTION;
28264 m->module_ns = val;
28265 }
28266 return JS_DupValue(ctx, m->module_ns);
28267}
28268
28269/* Load all the required modules for module 'm' */
28270static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
28271{
28272 int i;
28273 JSModuleDef *m1;
28274
28275 if (m->resolved)
28276 return 0;
28277#ifdef DUMP_MODULE_RESOLVE
28278 {
28279 char buf1[ATOM_GET_STR_BUF_SIZE];
28280 printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28281 }
28282#endif
28283 m->resolved = TRUE;
28284 /* resolve each requested module */
28285 for(i = 0; i < m->req_module_entries_count; i++) {
28286 JSReqModuleEntry *rme = &m->req_module_entries[i];
28287 m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
28288 rme->module_name);
28289 if (!m1)
28290 return -1;
28291 rme->module = m1;
28292 /* already done in js_host_resolve_imported_module() except if
28293 the module was loaded with JS_EvalBinary() */
28294 if (js_resolve_module(ctx, m1) < 0)
28295 return -1;
28296 }
28297 return 0;
28298}
28299
28300static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
28301{
28302 JSVarRef *var_ref;
28303 var_ref = js_malloc(ctx, sizeof(JSVarRef));
28304 if (!var_ref)
28305 return NULL;
28306 var_ref->header.ref_count = 1;
28307 if (is_lexical)
28308 var_ref->value = JS_UNINITIALIZED;
28309 else
28310 var_ref->value = JS_UNDEFINED;
28311 var_ref->pvalue = &var_ref->value;
28312 var_ref->is_detached = TRUE;
28313 add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
28314 return var_ref;
28315}
28316
28317/* Create the <eval> function associated with the module */
28318static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
28319{
28320 JSFunctionBytecode *b;
28321 int i;
28322 JSVarRef **var_refs;
28323 JSValue func_obj, bfunc;
28324 JSObject *p;
28325
28326 bfunc = m->func_obj;
28327 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
28328 JS_CLASS_BYTECODE_FUNCTION);
28329
28330 if (JS_IsException(func_obj))
28331 return -1;
28332 b = JS_VALUE_GET_PTR(bfunc);
28333
28334 p = JS_VALUE_GET_OBJ(func_obj);
28335 p->u.func.function_bytecode = b;
28336 b->header.ref_count++;
28337 p->u.func.home_object = NULL;
28338 p->u.func.var_refs = NULL;
28339 if (b->closure_var_count) {
28340 var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
28341 if (!var_refs)
28342 goto fail;
28343 p->u.func.var_refs = var_refs;
28344
28345 /* create the global variables. The other variables are
28346 imported from other modules */
28347 for(i = 0; i < b->closure_var_count; i++) {
28348 JSClosureVar *cv = &b->closure_var[i];
28349 JSVarRef *var_ref;
28350 if (cv->is_local) {
28351 var_ref = js_create_module_var(ctx, cv->is_lexical);
28352 if (!var_ref)
28353 goto fail;
28354#ifdef DUMP_MODULE_RESOLVE
28355 printf("local %d: %p\n", i, var_ref);
28356#endif
28357 var_refs[i] = var_ref;
28358 }
28359 }
28360 }
28361 m->func_obj = func_obj;
28362 JS_FreeValue(ctx, bfunc);
28363 return 0;
28364 fail:
28365 JS_FreeValue(ctx, func_obj);
28366 return -1;
28367}
28368
28369/* must be done before js_link_module() because of cyclic references */
28370static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
28371{
28372 BOOL is_c_module;
28373 int i;
28374 JSVarRef *var_ref;
28375
28376 if (m->func_created)
28377 return 0;
28378
28379 is_c_module = (m->init_func != NULL);
28380
28381 if (is_c_module) {
28382 /* initialize the exported variables */
28383 for(i = 0; i < m->export_entries_count; i++) {
28384 JSExportEntry *me = &m->export_entries[i];
28385 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
28386 var_ref = js_create_module_var(ctx, FALSE);
28387 if (!var_ref)
28388 return -1;
28389 me->u.local.var_ref = var_ref;
28390 }
28391 }
28392 } else {
28393 if (js_create_module_bytecode_function(ctx, m))
28394 return -1;
28395 }
28396 m->func_created = TRUE;
28397
28398 /* do it on the dependencies */
28399
28400 for(i = 0; i < m->req_module_entries_count; i++) {
28401 JSReqModuleEntry *rme = &m->req_module_entries[i];
28402 if (js_create_module_function(ctx, rme->module) < 0)
28403 return -1;
28404 }
28405
28406 return 0;
28407}
28408
28409
28410/* Prepare a module to be executed by resolving all the imported
28411 variables. */
28412static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m,
28413 JSModuleDef **pstack_top, int index)
28414{
28415 int i;
28416 JSImportEntry *mi;
28417 JSModuleDef *m1;
28418 JSVarRef **var_refs, *var_ref;
28419 JSObject *p;
28420 BOOL is_c_module;
28421 JSValue ret_val;
28422
28423 if (js_check_stack_overflow(ctx->rt, 0)) {
28424 JS_ThrowStackOverflow(ctx);
28425 return -1;
28426 }
28427
28428#ifdef DUMP_MODULE_RESOLVE
28429 {
28430 char buf1[ATOM_GET_STR_BUF_SIZE];
28431 printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28432 }
28433#endif
28434
28435 if (m->status == JS_MODULE_STATUS_LINKING ||
28436 m->status == JS_MODULE_STATUS_LINKED ||
28437 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
28438 m->status == JS_MODULE_STATUS_EVALUATED)
28439 return index;
28440
28441 assert(m->status == JS_MODULE_STATUS_UNLINKED);
28442 m->status = JS_MODULE_STATUS_LINKING;
28443 m->dfs_index = index;
28444 m->dfs_ancestor_index = index;
28445 index++;
28446 /* push 'm' on stack */
28447 m->stack_prev = *pstack_top;
28448 *pstack_top = m;
28449
28450 for(i = 0; i < m->req_module_entries_count; i++) {
28451 JSReqModuleEntry *rme = &m->req_module_entries[i];
28452 m1 = rme->module;
28453 index = js_inner_module_linking(ctx, m1, pstack_top, index);
28454 if (index < 0)
28455 goto fail;
28456 assert(m1->status == JS_MODULE_STATUS_LINKING ||
28457 m1->status == JS_MODULE_STATUS_LINKED ||
28458 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
28459 m1->status == JS_MODULE_STATUS_EVALUATED);
28460 if (m1->status == JS_MODULE_STATUS_LINKING) {
28461 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
28462 m1->dfs_ancestor_index);
28463 }
28464 }
28465
28466#ifdef DUMP_MODULE_RESOLVE
28467 {
28468 char buf1[ATOM_GET_STR_BUF_SIZE];
28469 printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28470 }
28471#endif
28472 /* check the indirect exports */
28473 for(i = 0; i < m->export_entries_count; i++) {
28474 JSExportEntry *me = &m->export_entries[i];
28475 if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
28476 me->local_name != JS_ATOM__star_) {
28477 JSResolveResultEnum ret;
28478 JSExportEntry *res_me;
28479 JSModuleDef *res_m, *m1;
28480 m1 = m->req_module_entries[me->u.req_module_idx].module;
28481 ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
28482 if (ret != JS_RESOLVE_RES_FOUND) {
28483 js_resolve_export_throw_error(ctx, ret, m, me->export_name);
28484 goto fail;
28485 }
28486 }
28487 }
28488
28489#ifdef DUMP_MODULE_RESOLVE
28490 {
28491 printf("exported bindings:\n");
28492 for(i = 0; i < m->export_entries_count; i++) {
28493 JSExportEntry *me = &m->export_entries[i];
28494 printf(" name="); print_atom(ctx, me->export_name);
28495 printf(" local="); print_atom(ctx, me->local_name);
28496 printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
28497 }
28498 }
28499#endif
28500
28501 is_c_module = (m->init_func != NULL);
28502
28503 if (!is_c_module) {
28504 p = JS_VALUE_GET_OBJ(m->func_obj);
28505 var_refs = p->u.func.var_refs;
28506
28507 for(i = 0; i < m->import_entries_count; i++) {
28508 mi = &m->import_entries[i];
28509#ifdef DUMP_MODULE_RESOLVE
28510 printf("import var_idx=%d name=", mi->var_idx);
28511 print_atom(ctx, mi->import_name);
28512 printf(": ");
28513#endif
28514 m1 = m->req_module_entries[mi->req_module_idx].module;
28515 if (mi->import_name == JS_ATOM__star_) {
28516 JSValue val;
28517 /* name space import */
28518 val = JS_GetModuleNamespace(ctx, m1);
28519 if (JS_IsException(val))
28520 goto fail;
28521 set_value(ctx, &var_refs[mi->var_idx]->value, val);
28522#ifdef DUMP_MODULE_RESOLVE
28523 printf("namespace\n");
28524#endif
28525 } else {
28526 JSResolveResultEnum ret;
28527 JSExportEntry *res_me;
28528 JSModuleDef *res_m;
28529 JSObject *p1;
28530
28531 ret = js_resolve_export(ctx, &res_m,
28532 &res_me, m1, mi->import_name);
28533 if (ret != JS_RESOLVE_RES_FOUND) {
28534 js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
28535 goto fail;
28536 }
28537 if (res_me->local_name == JS_ATOM__star_) {
28538 JSValue val;
28539 JSModuleDef *m2;
28540 /* name space import from */
28541 m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
28542 val = JS_GetModuleNamespace(ctx, m2);
28543 if (JS_IsException(val))
28544 goto fail;
28545 var_ref = js_create_module_var(ctx, TRUE);
28546 if (!var_ref) {
28547 JS_FreeValue(ctx, val);
28548 goto fail;
28549 }
28550 set_value(ctx, &var_ref->value, val);
28551 var_refs[mi->var_idx] = var_ref;
28552#ifdef DUMP_MODULE_RESOLVE
28553 printf("namespace from\n");
28554#endif
28555 } else {
28556 var_ref = res_me->u.local.var_ref;
28557 if (!var_ref) {
28558 p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
28559 var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
28560 }
28561 var_ref->header.ref_count++;
28562 var_refs[mi->var_idx] = var_ref;
28563#ifdef DUMP_MODULE_RESOLVE
28564 printf("local export (var_ref=%p)\n", var_ref);
28565#endif
28566 }
28567 }
28568 }
28569
28570 /* keep the exported variables in the module export entries (they
28571 are used when the eval function is deleted and cannot be
28572 initialized before in case imports are exported) */
28573 for(i = 0; i < m->export_entries_count; i++) {
28574 JSExportEntry *me = &m->export_entries[i];
28575 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
28576 var_ref = var_refs[me->u.local.var_idx];
28577 var_ref->header.ref_count++;
28578 me->u.local.var_ref = var_ref;
28579 }
28580 }
28581
28582 /* initialize the global variables */
28583 ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
28584 if (JS_IsException(ret_val))
28585 goto fail;
28586 JS_FreeValue(ctx, ret_val);
28587 }
28588
28589 assert(m->dfs_ancestor_index <= m->dfs_index);
28590 if (m->dfs_index == m->dfs_ancestor_index) {
28591 for(;;) {
28592 /* pop m1 from stack */
28593 m1 = *pstack_top;
28594 *pstack_top = m1->stack_prev;
28595 m1->status = JS_MODULE_STATUS_LINKED;
28596 if (m1 == m)
28597 break;
28598 }
28599 }
28600
28601#ifdef DUMP_MODULE_RESOLVE
28602 printf("js_inner_module_linking done\n");
28603#endif
28604 return index;
28605 fail:
28606 return -1;
28607}
28608
28609/* Prepare a module to be executed by resolving all the imported
28610 variables. */
28611static int js_link_module(JSContext *ctx, JSModuleDef *m)
28612{
28613 JSModuleDef *stack_top, *m1;
28614
28615#ifdef DUMP_MODULE_RESOLVE
28616 {
28617 char buf1[ATOM_GET_STR_BUF_SIZE];
28618 printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
28619 }
28620#endif
28621 assert(m->status == JS_MODULE_STATUS_UNLINKED ||
28622 m->status == JS_MODULE_STATUS_LINKED ||
28623 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
28624 m->status == JS_MODULE_STATUS_EVALUATED);
28625 stack_top = NULL;
28626 if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) {
28627 while (stack_top != NULL) {
28628 m1 = stack_top;
28629 assert(m1->status == JS_MODULE_STATUS_LINKING);
28630 m1->status = JS_MODULE_STATUS_UNLINKED;
28631 stack_top = m1->stack_prev;
28632 }
28633 return -1;
28634 }
28635 assert(stack_top == NULL);
28636 assert(m->status == JS_MODULE_STATUS_LINKED ||
28637 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
28638 m->status == JS_MODULE_STATUS_EVALUATED);
28639 return 0;
28640}
28641
28642/* return JS_ATOM_NULL if the name cannot be found. Only works with
28643 not striped bytecode functions. */
28644JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
28645{
28646 JSStackFrame *sf;
28647 JSFunctionBytecode *b;
28648 JSObject *p;
28649 /* XXX: currently we just use the filename of the englobing
28650 function from the debug info. May need to add a ScriptOrModule
28651 info in JSFunctionBytecode. */
28652 sf = ctx->rt->current_stack_frame;
28653 if (!sf)
28654 return JS_ATOM_NULL;
28655 while (n_stack_levels-- > 0) {
28656 sf = sf->prev_frame;
28657 if (!sf)
28658 return JS_ATOM_NULL;
28659 }
28660 for(;;) {
28661 if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
28662 return JS_ATOM_NULL;
28663 p = JS_VALUE_GET_OBJ(sf->cur_func);
28664 if (!js_class_has_bytecode(p->class_id))
28665 return JS_ATOM_NULL;
28666 b = p->u.func.function_bytecode;
28667 if (!b->is_direct_or_indirect_eval) {
28668 if (!b->has_debug)
28669 return JS_ATOM_NULL;
28670 return JS_DupAtom(ctx, b->debug.filename);
28671 } else {
28672 sf = sf->prev_frame;
28673 if (!sf)
28674 return JS_ATOM_NULL;
28675 }
28676 }
28677}
28678
28679JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
28680{
28681 return JS_DupAtom(ctx, m->module_name);
28682}
28683
28684JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
28685{
28686 JSValue obj;
28687 /* allocate meta_obj only if requested to save memory */
28688 obj = m->meta_obj;
28689 if (JS_IsUndefined(obj)) {
28690 obj = JS_NewObjectProto(ctx, JS_NULL);
28691 if (JS_IsException(obj))
28692 return JS_EXCEPTION;
28693 m->meta_obj = obj;
28694 }
28695 return JS_DupValue(ctx, obj);
28696}
28697
28698static JSValue js_import_meta(JSContext *ctx)
28699{
28700 JSAtom filename;
28701 JSModuleDef *m;
28702
28703 filename = JS_GetScriptOrModuleName(ctx, 0);
28704 if (filename == JS_ATOM_NULL)
28705 goto fail;
28706
28707 /* XXX: inefficient, need to add a module or script pointer in
28708 JSFunctionBytecode */
28709 m = js_find_loaded_module(ctx, filename);
28710 JS_FreeAtom(ctx, filename);
28711 if (!m) {
28712 fail:
28713 JS_ThrowTypeError(ctx, "import.meta not supported in this context");
28714 return JS_EXCEPTION;
28715 }
28716 return JS_GetImportMeta(ctx, m);
28717}
28718
28719static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m)
28720{
28721 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
28722}
28723
28724static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val,
28725 int argc, JSValueConst *argv, int magic, JSValue *func_data)
28726{
28727 JSValueConst *resolving_funcs = (JSValueConst *)func_data;
28728 JSValueConst error;
28729 JSValue ret;
28730
28731 /* XXX: check if the test is necessary */
28732 if (argc >= 1)
28733 error = argv[0];
28734 else
28735 error = JS_UNDEFINED;
28736 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
28737 1, &error);
28738 JS_FreeValue(ctx, ret);
28739 return JS_UNDEFINED;
28740}
28741
28742static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val,
28743 int argc, JSValueConst *argv, int magic, JSValue *func_data)
28744{
28745 JSValueConst *resolving_funcs = (JSValueConst *)func_data;
28746 JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]);
28747 JSValue ret, ns;
28748
28749 /* return the module namespace */
28750 ns = JS_GetModuleNamespace(ctx, m);
28751 if (JS_IsException(ns)) {
28752 JSValue err = JS_GetException(ctx);
28753 js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data);
28754 return JS_UNDEFINED;
28755 }
28756 ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
28757 1, (JSValueConst *)&ns);
28758 JS_FreeValue(ctx, ret);
28759 JS_FreeValue(ctx, ns);
28760 return JS_UNDEFINED;
28761}
28762
28763static void JS_LoadModuleInternal(JSContext *ctx, const char *basename,
28764 const char *filename,
28765 JSValueConst *resolving_funcs)
28766{
28767 JSValue evaluate_promise;
28768 JSModuleDef *m;
28769 JSValue ret, err, func_obj, evaluate_resolving_funcs[2];
28770 JSValueConst func_data[3];
28771
28772 m = js_host_resolve_imported_module(ctx, basename, filename);
28773 if (!m)
28774 goto fail;
28775
28776 if (js_resolve_module(ctx, m) < 0) {
28777 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
28778 goto fail;
28779 }
28780
28781 /* Evaluate the module code */
28782 func_obj = JS_NewModuleValue(ctx, m);
28783 evaluate_promise = JS_EvalFunction(ctx, func_obj);
28784 if (JS_IsException(evaluate_promise)) {
28785 fail:
28786 err = JS_GetException(ctx);
28787 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
28788 1, (JSValueConst *)&err);
28789 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28790 JS_FreeValue(ctx, err);
28791 return;
28792 }
28793
28794 func_obj = JS_NewModuleValue(ctx, m);
28795 func_data[0] = resolving_funcs[0];
28796 func_data[1] = resolving_funcs[1];
28797 func_data[2] = func_obj;
28798 evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data);
28799 evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data);
28800 JS_FreeValue(ctx, func_obj);
28801 ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs);
28802 JS_FreeValue(ctx, ret);
28803 JS_FreeValue(ctx, evaluate_resolving_funcs[0]);
28804 JS_FreeValue(ctx, evaluate_resolving_funcs[1]);
28805 JS_FreeValue(ctx, evaluate_promise);
28806}
28807
28808/* Return a promise or an exception in case of memory error. Used by
28809 os.Worker() */
28810JSValue JS_LoadModule(JSContext *ctx, const char *basename,
28811 const char *filename)
28812{
28813 JSValue promise, resolving_funcs[2];
28814
28815 promise = JS_NewPromiseCapability(ctx, resolving_funcs);
28816 if (JS_IsException(promise))
28817 return JS_EXCEPTION;
28818 JS_LoadModuleInternal(ctx, basename, filename,
28819 (JSValueConst *)resolving_funcs);
28820 JS_FreeValue(ctx, resolving_funcs[0]);
28821 JS_FreeValue(ctx, resolving_funcs[1]);
28822 return promise;
28823}
28824
28825static JSValue js_dynamic_import_job(JSContext *ctx,
28826 int argc, JSValueConst *argv)
28827{
28828 JSValueConst *resolving_funcs = argv;
28829 JSValueConst basename_val = argv[2];
28830 JSValueConst specifier = argv[3];
28831 const char *basename = NULL, *filename;
28832 JSValue ret, err;
28833
28834 if (!JS_IsString(basename_val)) {
28835 JS_ThrowTypeError(ctx, "no function filename for import()");
28836 goto exception;
28837 }
28838 basename = JS_ToCString(ctx, basename_val);
28839 if (!basename)
28840 goto exception;
28841
28842 filename = JS_ToCString(ctx, specifier);
28843 if (!filename)
28844 goto exception;
28845
28846 JS_LoadModuleInternal(ctx, basename, filename,
28847 resolving_funcs);
28848 JS_FreeCString(ctx, filename);
28849 JS_FreeCString(ctx, basename);
28850 return JS_UNDEFINED;
28851 exception:
28852 err = JS_GetException(ctx);
28853 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
28854 1, (JSValueConst *)&err);
28855 JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
28856 JS_FreeValue(ctx, err);
28857 JS_FreeCString(ctx, basename);
28858 return JS_UNDEFINED;
28859}
28860
28861static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
28862{
28863 JSAtom basename;
28864 JSValue promise, resolving_funcs[2], basename_val;
28865 JSValueConst args[4];
28866
28867 basename = JS_GetScriptOrModuleName(ctx, 0);
28868 if (basename == JS_ATOM_NULL)
28869 basename_val = JS_NULL;
28870 else
28871 basename_val = JS_AtomToValue(ctx, basename);
28872 JS_FreeAtom(ctx, basename);
28873 if (JS_IsException(basename_val))
28874 return basename_val;
28875
28876 promise = JS_NewPromiseCapability(ctx, resolving_funcs);
28877 if (JS_IsException(promise)) {
28878 JS_FreeValue(ctx, basename_val);
28879 return promise;
28880 }
28881
28882 args[0] = resolving_funcs[0];
28883 args[1] = resolving_funcs[1];
28884 args[2] = basename_val;
28885 args[3] = specifier;
28886
28887 /* cannot run JS_LoadModuleInternal synchronously because it would
28888 cause an unexpected recursion in js_evaluate_module() */
28889 JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
28890
28891 JS_FreeValue(ctx, basename_val);
28892 JS_FreeValue(ctx, resolving_funcs[0]);
28893 JS_FreeValue(ctx, resolving_funcs[1]);
28894 return promise;
28895}
28896
28897static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m)
28898{
28899 m->status = JS_MODULE_STATUS_EVALUATED;
28900 if (!JS_IsUndefined(m->promise)) {
28901 JSValue value, ret_val;
28902 assert(m->cycle_root == m);
28903 value = JS_UNDEFINED;
28904 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
28905 1, (JSValueConst *)&value);
28906 JS_FreeValue(ctx, ret_val);
28907 }
28908}
28909
28910typedef struct {
28911 JSModuleDef **tab;
28912 int count;
28913 int size;
28914} ExecModuleList;
28915
28916/* XXX: slow. Could use a linked list instead of ExecModuleList */
28917static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m)
28918{
28919 int i;
28920 for(i = 0; i < exec_list->count; i++) {
28921 if (exec_list->tab[i] == m)
28922 return TRUE;
28923 }
28924 return FALSE;
28925}
28926
28927static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module,
28928 ExecModuleList *exec_list)
28929{
28930 int i;
28931
28932 if (js_check_stack_overflow(ctx->rt, 0)) {
28933 JS_ThrowStackOverflow(ctx);
28934 return -1;
28935 }
28936 for(i = 0; i < module->async_parent_modules_count; i++) {
28937 JSModuleDef *m = module->async_parent_modules[i];
28938 if (!find_in_exec_module_list(exec_list, m) &&
28939 !m->cycle_root->eval_has_exception) {
28940 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
28941 assert(!m->eval_has_exception);
28942 assert(m->async_evaluation);
28943 assert(m->pending_async_dependencies > 0);
28944 m->pending_async_dependencies--;
28945 if (m->pending_async_dependencies == 0) {
28946 if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) {
28947 return -1;
28948 }
28949 exec_list->tab[exec_list->count++] = m;
28950 if (!m->has_tla) {
28951 if (gather_available_ancestors(ctx, m, exec_list))
28952 return -1;
28953 }
28954 }
28955 }
28956 }
28957 return 0;
28958}
28959
28960static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque)
28961{
28962 JSModuleDef *m1 = *(JSModuleDef **)p1;
28963 JSModuleDef *m2 = *(JSModuleDef **)p2;
28964 return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) -
28965 (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp);
28966}
28967
28968static int js_execute_async_module(JSContext *ctx, JSModuleDef *m);
28969static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
28970 JSValue *pvalue);
28971
28972static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val,
28973 int argc, JSValueConst *argv, int magic, JSValue *func_data)
28974{
28975 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
28976 JSValueConst error = argv[0];
28977 int i;
28978
28979 if (js_check_stack_overflow(ctx->rt, 0))
28980 return JS_ThrowStackOverflow(ctx);
28981
28982 if (module->status == JS_MODULE_STATUS_EVALUATED) {
28983 assert(module->eval_has_exception);
28984 return JS_UNDEFINED;
28985 }
28986
28987 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
28988 assert(!module->eval_has_exception);
28989 assert(module->async_evaluation);
28990
28991 module->eval_has_exception = TRUE;
28992 module->eval_exception = JS_DupValue(ctx, error);
28993 module->status = JS_MODULE_STATUS_EVALUATED;
28994
28995 for(i = 0; i < module->async_parent_modules_count; i++) {
28996 JSModuleDef *m = module->async_parent_modules[i];
28997 JSValue m_obj = JS_NewModuleValue(ctx, m);
28998 js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0,
28999 &m_obj);
29000 JS_FreeValue(ctx, m_obj);
29001 }
29002
29003 if (!JS_IsUndefined(module->promise)) {
29004 JSValue ret_val;
29005 assert(module->cycle_root == module);
29006 ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED,
29007 1, &error);
29008 JS_FreeValue(ctx, ret_val);
29009 }
29010 return JS_UNDEFINED;
29011}
29012
29013static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val,
29014 int argc, JSValueConst *argv, int magic, JSValue *func_data)
29015{
29016 JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]);
29017 ExecModuleList exec_list_s, *exec_list = &exec_list_s;
29018 int i;
29019
29020 if (module->status == JS_MODULE_STATUS_EVALUATED) {
29021 assert(module->eval_has_exception);
29022 return JS_UNDEFINED;
29023 }
29024 assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC);
29025 assert(!module->eval_has_exception);
29026 assert(module->async_evaluation);
29027 module->async_evaluation = FALSE;
29028 js_set_module_evaluated(ctx, module);
29029
29030 exec_list->tab = NULL;
29031 exec_list->count = 0;
29032 exec_list->size = 0;
29033
29034 if (gather_available_ancestors(ctx, module, exec_list) < 0) {
29035 js_free(ctx, exec_list->tab);
29036 return JS_EXCEPTION;
29037 }
29038
29039 /* sort by increasing async_evaluation timestamp */
29040 rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]),
29041 exec_module_list_cmp, NULL);
29042
29043 for(i = 0; i < exec_list->count; i++) {
29044 JSModuleDef *m = exec_list->tab[i];
29045 if (m->status == JS_MODULE_STATUS_EVALUATED) {
29046 assert(m->eval_has_exception);
29047 } else if (m->has_tla) {
29048 js_execute_async_module(ctx, m);
29049 } else {
29050 JSValue error;
29051 if (js_execute_sync_module(ctx, m, &error) < 0) {
29052 JSValue m_obj = JS_NewModuleValue(ctx, m);
29053 js_async_module_execution_rejected(ctx, JS_UNDEFINED,
29054 1, (JSValueConst *)&error, 0,
29055 &m_obj);
29056 JS_FreeValue(ctx, m_obj);
29057 JS_FreeValue(ctx, error);
29058 } else {
29059 js_set_module_evaluated(ctx, m);
29060 }
29061 }
29062 }
29063 js_free(ctx, exec_list->tab);
29064 return JS_UNDEFINED;
29065}
29066
29067static int js_execute_async_module(JSContext *ctx, JSModuleDef *m)
29068{
29069 JSValue promise, m_obj;
29070 JSValue resolve_funcs[2], ret_val;
29071 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
29072 if (JS_IsException(promise))
29073 return -1;
29074 m_obj = JS_NewModuleValue(ctx, m);
29075 resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj);
29076 resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj);
29077 ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs);
29078 JS_FreeValue(ctx, ret_val);
29079 JS_FreeValue(ctx, m_obj);
29080 JS_FreeValue(ctx, resolve_funcs[0]);
29081 JS_FreeValue(ctx, resolve_funcs[1]);
29082 JS_FreeValue(ctx, promise);
29083 return 0;
29084}
29085
29086/* return < 0 in case of exception. *pvalue contains the exception. */
29087static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m,
29088 JSValue *pvalue)
29089{
29090 if (m->init_func) {
29091 /* C module init : no asynchronous execution */
29092 if (m->init_func(ctx, m) < 0)
29093 goto fail;
29094 } else {
29095 JSValue promise;
29096 JSPromiseStateEnum state;
29097
29098 promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0);
29099 if (JS_IsException(promise))
29100 goto fail;
29101 state = JS_PromiseState(ctx, promise);
29102 if (state == JS_PROMISE_FULFILLED) {
29103 JS_FreeValue(ctx, promise);
29104 } else if (state == JS_PROMISE_REJECTED) {
29105 *pvalue = JS_PromiseResult(ctx, promise);
29106 JS_FreeValue(ctx, promise);
29107 return -1;
29108 } else {
29109 JS_FreeValue(ctx, promise);
29110 JS_ThrowTypeError(ctx, "promise is pending");
29111 fail:
29112 *pvalue = JS_GetException(ctx);
29113 return -1;
29114 }
29115 }
29116 *pvalue = JS_UNDEFINED;
29117 return 0;
29118}
29119
29120/* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1,
29121 exception) */
29122static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m,
29123 int index, JSModuleDef **pstack_top,
29124 JSValue *pvalue)
29125{
29126 JSModuleDef *m1;
29127 int i;
29128
29129 if (js_check_stack_overflow(ctx->rt, 0)) {
29130 JS_ThrowStackOverflow(ctx);
29131 *pvalue = JS_GetException(ctx);
29132 return -1;
29133 }
29134
29135#ifdef DUMP_MODULE_RESOLVE
29136 {
29137 char buf1[ATOM_GET_STR_BUF_SIZE];
29138 printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
29139 }
29140#endif
29141
29142 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29143 m->status == JS_MODULE_STATUS_EVALUATED) {
29144 if (m->eval_has_exception) {
29145 *pvalue = JS_DupValue(ctx, m->eval_exception);
29146 return -1;
29147 } else {
29148 *pvalue = JS_UNDEFINED;
29149 return index;
29150 }
29151 }
29152 if (m->status == JS_MODULE_STATUS_EVALUATING) {
29153 *pvalue = JS_UNDEFINED;
29154 return index;
29155 }
29156 assert(m->status == JS_MODULE_STATUS_LINKED);
29157
29158 m->status = JS_MODULE_STATUS_EVALUATING;
29159 m->dfs_index = index;
29160 m->dfs_ancestor_index = index;
29161 m->pending_async_dependencies = 0;
29162 index++;
29163 /* push 'm' on stack */
29164 m->stack_prev = *pstack_top;
29165 *pstack_top = m;
29166
29167 for(i = 0; i < m->req_module_entries_count; i++) {
29168 JSReqModuleEntry *rme = &m->req_module_entries[i];
29169 m1 = rme->module;
29170 index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue);
29171 if (index < 0)
29172 return -1;
29173 assert(m1->status == JS_MODULE_STATUS_EVALUATING ||
29174 m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29175 m1->status == JS_MODULE_STATUS_EVALUATED);
29176 if (m1->status == JS_MODULE_STATUS_EVALUATING) {
29177 m->dfs_ancestor_index = min_int(m->dfs_ancestor_index,
29178 m1->dfs_ancestor_index);
29179 } else {
29180 m1 = m1->cycle_root;
29181 assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29182 m1->status == JS_MODULE_STATUS_EVALUATED);
29183 if (m1->eval_has_exception) {
29184 *pvalue = JS_DupValue(ctx, m1->eval_exception);
29185 return -1;
29186 }
29187 }
29188 if (m1->async_evaluation) {
29189 m->pending_async_dependencies++;
29190 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)) {
29191 *pvalue = JS_GetException(ctx);
29192 return -1;
29193 }
29194 m1->async_parent_modules[m1->async_parent_modules_count++] = m;
29195 }
29196 }
29197
29198 if (m->pending_async_dependencies > 0) {
29199 assert(!m->async_evaluation);
29200 m->async_evaluation = TRUE;
29201 m->async_evaluation_timestamp =
29202 ctx->rt->module_async_evaluation_next_timestamp++;
29203 } else if (m->has_tla) {
29204 assert(!m->async_evaluation);
29205 m->async_evaluation = TRUE;
29206 m->async_evaluation_timestamp =
29207 ctx->rt->module_async_evaluation_next_timestamp++;
29208 js_execute_async_module(ctx, m);
29209 } else {
29210 if (js_execute_sync_module(ctx, m, pvalue) < 0)
29211 return -1;
29212 }
29213
29214 assert(m->dfs_ancestor_index <= m->dfs_index);
29215 if (m->dfs_index == m->dfs_ancestor_index) {
29216 for(;;) {
29217 /* pop m1 from stack */
29218 m1 = *pstack_top;
29219 *pstack_top = m1->stack_prev;
29220 if (!m1->async_evaluation) {
29221 m1->status = JS_MODULE_STATUS_EVALUATED;
29222 } else {
29223 m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC;
29224 }
29225 /* spec bug: cycle_root must be assigned before the test */
29226 m1->cycle_root = m;
29227 if (m1 == m)
29228 break;
29229 }
29230 }
29231 *pvalue = JS_UNDEFINED;
29232 return index;
29233}
29234
29235/* Run the <eval> function of the module and of all its requested
29236 modules. Return a promise or an exception. */
29237static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
29238{
29239 JSModuleDef *m1, *stack_top;
29240 JSValue ret_val, result;
29241
29242 assert(m->status == JS_MODULE_STATUS_LINKED ||
29243 m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29244 m->status == JS_MODULE_STATUS_EVALUATED);
29245 if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29246 m->status == JS_MODULE_STATUS_EVALUATED) {
29247 m = m->cycle_root;
29248 }
29249 /* a promise may be created only on the cycle_root of a cycle */
29250 if (!JS_IsUndefined(m->promise))
29251 return JS_DupValue(ctx, m->promise);
29252 m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs);
29253 if (JS_IsException(m->promise))
29254 return JS_EXCEPTION;
29255
29256 stack_top = NULL;
29257 if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) {
29258 while (stack_top != NULL) {
29259 m1 = stack_top;
29260 assert(m1->status == JS_MODULE_STATUS_EVALUATING);
29261 m1->status = JS_MODULE_STATUS_EVALUATED;
29262 m1->eval_has_exception = TRUE;
29263 m1->eval_exception = JS_DupValue(ctx, result);
29264 m1->cycle_root = m; /* spec bug: should be present */
29265 stack_top = m1->stack_prev;
29266 }
29267 JS_FreeValue(ctx, result);
29268 assert(m->status == JS_MODULE_STATUS_EVALUATED);
29269 assert(m->eval_has_exception);
29270 ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED,
29271 1, (JSValueConst *)&m->eval_exception);
29272 JS_FreeValue(ctx, ret_val);
29273 } else {
29274 assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC ||
29275 m->status == JS_MODULE_STATUS_EVALUATED);
29276 assert(!m->eval_has_exception);
29277 if (!m->async_evaluation) {
29278 JSValue value;
29279 assert(m->status == JS_MODULE_STATUS_EVALUATED);
29280 value = JS_UNDEFINED;
29281 ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED,
29282 1, (JSValueConst *)&value);
29283 JS_FreeValue(ctx, ret_val);
29284 }
29285 assert(stack_top == NULL);
29286 }
29287 return JS_DupValue(ctx, m->promise);
29288}
29289
29290static __exception JSAtom js_parse_from_clause(JSParseState *s)
29291{
29292 JSAtom module_name;
29293 if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
29294 js_parse_error(s, "from clause expected");
29295 return JS_ATOM_NULL;
29296 }
29297 if (next_token(s))
29298 return JS_ATOM_NULL;
29299 if (s->token.val != TOK_STRING) {
29300 js_parse_error(s, "string expected");
29301 return JS_ATOM_NULL;
29302 }
29303 module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
29304 if (module_name == JS_ATOM_NULL)
29305 return JS_ATOM_NULL;
29306 if (next_token(s)) {
29307 JS_FreeAtom(s->ctx, module_name);
29308 return JS_ATOM_NULL;
29309 }
29310 return module_name;
29311}
29312
29313static __exception int js_parse_export(JSParseState *s)
29314{
29315 JSContext *ctx = s->ctx;
29316 JSModuleDef *m = s->cur_func->module;
29317 JSAtom local_name, export_name;
29318 int first_export, idx, i, tok;
29319 JSAtom module_name;
29320 JSExportEntry *me;
29321
29322 if (next_token(s))
29323 return -1;
29324
29325 tok = s->token.val;
29326 if (tok == TOK_CLASS) {
29327 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
29328 } else if (tok == TOK_FUNCTION ||
29329 (token_is_pseudo_keyword(s, JS_ATOM_async) &&
29330 peek_token(s, TRUE) == TOK_FUNCTION)) {
29331 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
29332 JS_FUNC_NORMAL, JS_ATOM_NULL,
29333 s->token.ptr,
29334 JS_PARSE_EXPORT_NAMED, NULL);
29335 }
29336
29337 if (next_token(s))
29338 return -1;
29339
29340 switch(tok) {
29341 case '{':
29342 first_export = m->export_entries_count;
29343 while (s->token.val != '}') {
29344 if (!token_is_ident(s->token.val)) {
29345 js_parse_error(s, "identifier expected");
29346 return -1;
29347 }
29348 local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29349 export_name = JS_ATOM_NULL;
29350 if (next_token(s))
29351 goto fail;
29352 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
29353 if (next_token(s))
29354 goto fail;
29355 if (!token_is_ident(s->token.val)) {
29356 js_parse_error(s, "identifier expected");
29357 goto fail;
29358 }
29359 export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29360 if (next_token(s)) {
29361 fail:
29362 JS_FreeAtom(ctx, local_name);
29363 fail1:
29364 JS_FreeAtom(ctx, export_name);
29365 return -1;
29366 }
29367 } else {
29368 export_name = JS_DupAtom(ctx, local_name);
29369 }
29370 me = add_export_entry(s, m, local_name, export_name,
29371 JS_EXPORT_TYPE_LOCAL);
29372 JS_FreeAtom(ctx, local_name);
29373 JS_FreeAtom(ctx, export_name);
29374 if (!me)
29375 return -1;
29376 if (s->token.val != ',')
29377 break;
29378 if (next_token(s))
29379 return -1;
29380 }
29381 if (js_parse_expect(s, '}'))
29382 return -1;
29383 if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
29384 module_name = js_parse_from_clause(s);
29385 if (module_name == JS_ATOM_NULL)
29386 return -1;
29387 idx = add_req_module_entry(ctx, m, module_name);
29388 JS_FreeAtom(ctx, module_name);
29389 if (idx < 0)
29390 return -1;
29391 for(i = first_export; i < m->export_entries_count; i++) {
29392 me = &m->export_entries[i];
29393 me->export_type = JS_EXPORT_TYPE_INDIRECT;
29394 me->u.req_module_idx = idx;
29395 }
29396 }
29397 break;
29398 case '*':
29399 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
29400 /* export ns from */
29401 if (next_token(s))
29402 return -1;
29403 if (!token_is_ident(s->token.val)) {
29404 js_parse_error(s, "identifier expected");
29405 return -1;
29406 }
29407 export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29408 if (next_token(s))
29409 goto fail1;
29410 module_name = js_parse_from_clause(s);
29411 if (module_name == JS_ATOM_NULL)
29412 goto fail1;
29413 idx = add_req_module_entry(ctx, m, module_name);
29414 JS_FreeAtom(ctx, module_name);
29415 if (idx < 0)
29416 goto fail1;
29417 me = add_export_entry(s, m, JS_ATOM__star_, export_name,
29418 JS_EXPORT_TYPE_INDIRECT);
29419 JS_FreeAtom(ctx, export_name);
29420 if (!me)
29421 return -1;
29422 me->u.req_module_idx = idx;
29423 } else {
29424 module_name = js_parse_from_clause(s);
29425 if (module_name == JS_ATOM_NULL)
29426 return -1;
29427 idx = add_req_module_entry(ctx, m, module_name);
29428 JS_FreeAtom(ctx, module_name);
29429 if (idx < 0)
29430 return -1;
29431 if (add_star_export_entry(ctx, m, idx) < 0)
29432 return -1;
29433 }
29434 break;
29435 case TOK_DEFAULT:
29436 if (s->token.val == TOK_CLASS) {
29437 return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
29438 } else if (s->token.val == TOK_FUNCTION ||
29439 (token_is_pseudo_keyword(s, JS_ATOM_async) &&
29440 peek_token(s, TRUE) == TOK_FUNCTION)) {
29441 return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
29442 JS_FUNC_NORMAL, JS_ATOM_NULL,
29443 s->token.ptr,
29444 JS_PARSE_EXPORT_DEFAULT, NULL);
29445 } else {
29446 if (js_parse_assign_expr(s))
29447 return -1;
29448 }
29449 /* set the name of anonymous functions */
29450 set_object_name(s, JS_ATOM_default);
29451
29452 /* store the value in the _default_ global variable and export
29453 it */
29454 local_name = JS_ATOM__default_;
29455 if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
29456 return -1;
29457 emit_op(s, OP_scope_put_var_init);
29458 emit_atom(s, local_name);
29459 emit_u16(s, 0);
29460
29461 if (!add_export_entry(s, m, local_name, JS_ATOM_default,
29462 JS_EXPORT_TYPE_LOCAL))
29463 return -1;
29464 break;
29465 case TOK_VAR:
29466 case TOK_LET:
29467 case TOK_CONST:
29468 return js_parse_var(s, TRUE, tok, TRUE);
29469 default:
29470 return js_parse_error(s, "invalid export syntax");
29471 }
29472 return js_parse_expect_semi(s);
29473}
29474
29475static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
29476 BOOL is_local, BOOL is_arg,
29477 int var_idx, JSAtom var_name,
29478 BOOL is_const, BOOL is_lexical,
29479 JSVarKindEnum var_kind);
29480
29481static int add_import(JSParseState *s, JSModuleDef *m,
29482 JSAtom local_name, JSAtom import_name)
29483{
29484 JSContext *ctx = s->ctx;
29485 int i, var_idx;
29486 JSImportEntry *mi;
29487 BOOL is_local;
29488
29489 if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
29490 return js_parse_error(s, "invalid import binding");
29491
29492 if (local_name != JS_ATOM_default) {
29493 for (i = 0; i < s->cur_func->closure_var_count; i++) {
29494 if (s->cur_func->closure_var[i].var_name == local_name)
29495 return js_parse_error(s, "duplicate import binding");
29496 }
29497 }
29498
29499 is_local = (import_name == JS_ATOM__star_);
29500 var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
29501 m->import_entries_count,
29502 local_name, TRUE, TRUE, FALSE);
29503 if (var_idx < 0)
29504 return -1;
29505 if (js_resize_array(ctx, (void **)&m->import_entries,
29506 sizeof(JSImportEntry),
29507 &m->import_entries_size,
29508 m->import_entries_count + 1))
29509 return -1;
29510 mi = &m->import_entries[m->import_entries_count++];
29511 mi->import_name = JS_DupAtom(ctx, import_name);
29512 mi->var_idx = var_idx;
29513 return 0;
29514}
29515
29516static __exception int js_parse_import(JSParseState *s)
29517{
29518 JSContext *ctx = s->ctx;
29519 JSModuleDef *m = s->cur_func->module;
29520 JSAtom local_name, import_name, module_name;
29521 int first_import, i, idx;
29522
29523 if (next_token(s))
29524 return -1;
29525
29526 first_import = m->import_entries_count;
29527 if (s->token.val == TOK_STRING) {
29528 module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
29529 if (module_name == JS_ATOM_NULL)
29530 return -1;
29531 if (next_token(s)) {
29532 JS_FreeAtom(ctx, module_name);
29533 return -1;
29534 }
29535 } else {
29536 if (s->token.val == TOK_IDENT) {
29537 if (s->token.u.ident.is_reserved) {
29538 return js_parse_error_reserved_identifier(s);
29539 }
29540 /* "default" import */
29541 local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29542 import_name = JS_ATOM_default;
29543 if (next_token(s))
29544 goto fail;
29545 if (add_import(s, m, local_name, import_name))
29546 goto fail;
29547 JS_FreeAtom(ctx, local_name);
29548
29549 if (s->token.val != ',')
29550 goto end_import_clause;
29551 if (next_token(s))
29552 return -1;
29553 }
29554
29555 if (s->token.val == '*') {
29556 /* name space import */
29557 if (next_token(s))
29558 return -1;
29559 if (!token_is_pseudo_keyword(s, JS_ATOM_as))
29560 return js_parse_error(s, "expecting 'as'");
29561 if (next_token(s))
29562 return -1;
29563 if (!token_is_ident(s->token.val)) {
29564 js_parse_error(s, "identifier expected");
29565 return -1;
29566 }
29567 local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29568 import_name = JS_ATOM__star_;
29569 if (next_token(s))
29570 goto fail;
29571 if (add_import(s, m, local_name, import_name))
29572 goto fail;
29573 JS_FreeAtom(ctx, local_name);
29574 } else if (s->token.val == '{') {
29575 if (next_token(s))
29576 return -1;
29577
29578 while (s->token.val != '}') {
29579 if (!token_is_ident(s->token.val)) {
29580 js_parse_error(s, "identifier expected");
29581 return -1;
29582 }
29583 import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29584 local_name = JS_ATOM_NULL;
29585 if (next_token(s))
29586 goto fail;
29587 if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
29588 if (next_token(s))
29589 goto fail;
29590 if (!token_is_ident(s->token.val)) {
29591 js_parse_error(s, "identifier expected");
29592 goto fail;
29593 }
29594 local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
29595 if (next_token(s)) {
29596 fail:
29597 JS_FreeAtom(ctx, local_name);
29598 JS_FreeAtom(ctx, import_name);
29599 return -1;
29600 }
29601 } else {
29602 local_name = JS_DupAtom(ctx, import_name);
29603 }
29604 if (add_import(s, m, local_name, import_name))
29605 goto fail;
29606 JS_FreeAtom(ctx, local_name);
29607 JS_FreeAtom(ctx, import_name);
29608 if (s->token.val != ',')
29609 break;
29610 if (next_token(s))
29611 return -1;
29612 }
29613 if (js_parse_expect(s, '}'))
29614 return -1;
29615 }
29616 end_import_clause:
29617 module_name = js_parse_from_clause(s);
29618 if (module_name == JS_ATOM_NULL)
29619 return -1;
29620 }
29621 idx = add_req_module_entry(ctx, m, module_name);
29622 JS_FreeAtom(ctx, module_name);
29623 if (idx < 0)
29624 return -1;
29625 for(i = first_import; i < m->import_entries_count; i++)
29626 m->import_entries[i].req_module_idx = idx;
29627
29628 return js_parse_expect_semi(s);
29629}
29630
29631static __exception int js_parse_source_element(JSParseState *s)
29632{
29633 JSFunctionDef *fd = s->cur_func;
29634 int tok;
29635
29636 if (s->token.val == TOK_FUNCTION ||
29637 (token_is_pseudo_keyword(s, JS_ATOM_async) &&
29638 peek_token(s, TRUE) == TOK_FUNCTION)) {
29639 if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
29640 JS_FUNC_NORMAL, JS_ATOM_NULL,
29641 s->token.ptr))
29642 return -1;
29643 } else if (s->token.val == TOK_EXPORT && fd->module) {
29644 if (js_parse_export(s))
29645 return -1;
29646 } else if (s->token.val == TOK_IMPORT && fd->module &&
29647 ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {
29648 /* the peek_token is needed to avoid confusion with ImportCall
29649 (dynamic import) or import.meta */
29650 if (js_parse_import(s))
29651 return -1;
29652 } else {
29653 if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
29654 return -1;
29655 }
29656 return 0;
29657}
29658
29659static JSFunctionDef *js_new_function_def(JSContext *ctx,
29660 JSFunctionDef *parent,
29661 BOOL is_eval,
29662 BOOL is_func_expr,
29663 const char *filename,
29664 const uint8_t *source_ptr,
29665 GetLineColCache *get_line_col_cache)
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 fd->strip_debug = ((ctx->rt->strip_flags & JS_STRIP_DEBUG) != 0);
29685 fd->strip_source = ((ctx->rt->strip_flags & (JS_STRIP_DEBUG | JS_STRIP_SOURCE)) != 0);
29686
29687 fd->is_eval = is_eval;
29688 fd->is_func_expr = is_func_expr;
29689 js_dbuf_init(ctx, &fd->byte_code);
29690 fd->last_opcode_pos = -1;
29691 fd->func_name = JS_ATOM_NULL;
29692 fd->var_object_idx = -1;
29693 fd->arg_var_object_idx = -1;
29694 fd->arguments_var_idx = -1;
29695 fd->arguments_arg_idx = -1;
29696 fd->func_var_idx = -1;
29697 fd->eval_ret_idx = -1;
29698 fd->this_var_idx = -1;
29699 fd->new_target_var_idx = -1;
29700 fd->this_active_func_var_idx = -1;
29701 fd->home_object_var_idx = -1;
29702
29703 /* XXX: should distinguish arg, var and var object and body scopes */
29704 fd->scopes = fd->def_scope_array;
29705 fd->scope_size = countof(fd->def_scope_array);
29706 fd->scope_count = 1;
29707 fd->scopes[0].first = -1;
29708 fd->scopes[0].parent = -1;
29709 fd->scope_level = 0; /* 0: var/arg scope */
29710 fd->scope_first = -1;
29711 fd->body_scope = -1;
29712
29713 fd->filename = JS_NewAtom(ctx, filename);
29714 fd->source_pos = source_ptr - get_line_col_cache->buf_start;
29715 fd->get_line_col_cache = get_line_col_cache;
29716
29717 js_dbuf_init(ctx, &fd->pc2line);
29718 //fd->pc2line_last_line_num = line_num;
29719 //fd->pc2line_last_pc = 0;
29720 fd->last_opcode_source_ptr = source_ptr;
29721 return fd;
29722}
29723
29724static void free_bytecode_atoms(JSRuntime *rt,
29725 const uint8_t *bc_buf, int bc_len,
29726 BOOL use_short_opcodes)
29727{
29728 int pos, len, op;
29729 JSAtom atom;
29730 const JSOpCode *oi;
29731
29732 pos = 0;
29733 while (pos < bc_len) {
29734 op = bc_buf[pos];
29735 if (use_short_opcodes)
29736 oi = &short_opcode_info(op);
29737 else
29738 oi = &opcode_info[op];
29739
29740 len = oi->size;
29741 switch(oi->fmt) {
29742 case OP_FMT_atom:
29743 case OP_FMT_atom_u8:
29744 case OP_FMT_atom_u16:
29745 case OP_FMT_atom_label_u8:
29746 case OP_FMT_atom_label_u16:
29747 atom = get_u32(bc_buf + pos + 1);
29748 JS_FreeAtomRT(rt, atom);
29749 break;
29750 default:
29751 break;
29752 }
29753 pos += len;
29754 }
29755}
29756
29757static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
29758{
29759 int i;
29760 struct list_head *el, *el1;
29761
29762 /* free the child functions */
29763 list_for_each_safe(el, el1, &fd->child_list) {
29764 JSFunctionDef *fd1;
29765 fd1 = list_entry(el, JSFunctionDef, link);
29766 js_free_function_def(ctx, fd1);
29767 }
29768
29769 free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
29770 fd->use_short_opcodes);
29771 dbuf_free(&fd->byte_code);
29772 js_free(ctx, fd->jump_slots);
29773 js_free(ctx, fd->label_slots);
29774 js_free(ctx, fd->line_number_slots);
29775
29776 for(i = 0; i < fd->cpool_count; i++) {
29777 JS_FreeValue(ctx, fd->cpool[i]);
29778 }
29779 js_free(ctx, fd->cpool);
29780
29781 JS_FreeAtom(ctx, fd->func_name);
29782
29783 for(i = 0; i < fd->var_count; i++) {
29784 JS_FreeAtom(ctx, fd->vars[i].var_name);
29785 }
29786 js_free(ctx, fd->vars);
29787 for(i = 0; i < fd->arg_count; i++) {
29788 JS_FreeAtom(ctx, fd->args[i].var_name);
29789 }
29790 js_free(ctx, fd->args);
29791
29792 for(i = 0; i < fd->global_var_count; i++) {
29793 JS_FreeAtom(ctx, fd->global_vars[i].var_name);
29794 }
29795 js_free(ctx, fd->global_vars);
29796
29797 for(i = 0; i < fd->closure_var_count; i++) {
29798 JSClosureVar *cv = &fd->closure_var[i];
29799 JS_FreeAtom(ctx, cv->var_name);
29800 }
29801 js_free(ctx, fd->closure_var);
29802
29803 if (fd->scopes != fd->def_scope_array)
29804 js_free(ctx, fd->scopes);
29805
29806 JS_FreeAtom(ctx, fd->filename);
29807 dbuf_free(&fd->pc2line);
29808
29809 js_free(ctx, fd->source);
29810
29811 if (fd->parent) {
29812 /* remove in parent list */
29813 list_del(&fd->link);
29814 }
29815 js_free(ctx, fd);
29816}
29817
29818#ifdef DUMP_BYTECODE
29819static const char *skip_lines(const char *p, int n) {
29820 while (n-- > 0 && *p) {
29821 while (*p && *p++ != '\n')
29822 continue;
29823 }
29824 return p;
29825}
29826
29827static void print_lines(const char *source, int line, int line1) {
29828 const char *s = source;
29829 const char *p = skip_lines(s, line);
29830 if (*p) {
29831 while (line++ < line1) {
29832 p = skip_lines(s = p, 1);
29833 printf(";; %.*s", (int)(p - s), s);
29834 if (!*p) {
29835 if (p[-1] != '\n')
29836 printf("\n");
29837 break;
29838 }
29839 }
29840 }
29841}
29842
29843static void dump_byte_code(JSContext *ctx, int pass,
29844 const uint8_t *tab, int len,
29845 const JSVarDef *args, int arg_count,
29846 const JSVarDef *vars, int var_count,
29847 const JSClosureVar *closure_var, int closure_var_count,
29848 const JSValue *cpool, uint32_t cpool_count,
29849 const char *source,
29850 const LabelSlot *label_slots, JSFunctionBytecode *b)
29851{
29852 const JSOpCode *oi;
29853 int pos, pos_next, op, size, idx, addr, line, line1, in_source, line_num;
29854 uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
29855 BOOL use_short_opcodes = (b != NULL);
29856
29857 if (b) {
29858 int col_num;
29859 line_num = find_line_num(ctx, b, -1, &col_num);
29860 }
29861
29862 /* scan for jump targets */
29863 for (pos = 0; pos < len; pos = pos_next) {
29864 op = tab[pos];
29865 if (use_short_opcodes)
29866 oi = &short_opcode_info(op);
29867 else
29868 oi = &opcode_info[op];
29869 pos_next = pos + oi->size;
29870 if (op < OP_COUNT) {
29871 switch (oi->fmt) {
29872#if SHORT_OPCODES
29873 case OP_FMT_label8:
29874 pos++;
29875 addr = (int8_t)tab[pos];
29876 goto has_addr;
29877 case OP_FMT_label16:
29878 pos++;
29879 addr = (int16_t)get_u16(tab + pos);
29880 goto has_addr;
29881#endif
29882 case OP_FMT_atom_label_u8:
29883 case OP_FMT_atom_label_u16:
29884 pos += 4;
29885 /* fall thru */
29886 case OP_FMT_label:
29887 case OP_FMT_label_u16:
29888 pos++;
29889 addr = get_u32(tab + pos);
29890 goto has_addr;
29891 has_addr:
29892 if (pass == 1)
29893 addr = label_slots[addr].pos;
29894 if (pass == 2)
29895 addr = label_slots[addr].pos2;
29896 if (pass == 3)
29897 addr += pos;
29898 if (addr >= 0 && addr < len)
29899 bits[addr] |= 1;
29900 break;
29901 }
29902 }
29903 }
29904 in_source = 0;
29905 if (source) {
29906 /* Always print first line: needed if single line */
29907 print_lines(source, 0, 1);
29908 in_source = 1;
29909 }
29910 line1 = line = 1;
29911 pos = 0;
29912 while (pos < len) {
29913 op = tab[pos];
29914 if (source && b) {
29915 int col_num;
29916 if (b) {
29917 line1 = find_line_num(ctx, b, pos, &col_num) - line_num + 1;
29918 } else if (op == OP_line_num) {
29919 /* XXX: no longer works */
29920 line1 = get_u32(tab + pos + 1) - line_num + 1;
29921 }
29922 if (line1 > line) {
29923 if (!in_source)
29924 printf("\n");
29925 in_source = 1;
29926 print_lines(source, line, line1);
29927 line = line1;
29928 //bits[pos] |= 2;
29929 }
29930 }
29931 if (in_source)
29932 printf("\n");
29933 in_source = 0;
29934 if (op >= OP_COUNT) {
29935 printf("invalid opcode (0x%02x)\n", op);
29936 pos++;
29937 continue;
29938 }
29939 if (use_short_opcodes)
29940 oi = &short_opcode_info(op);
29941 else
29942 oi = &opcode_info[op];
29943 size = oi->size;
29944 if (pos + size > len) {
29945 printf("truncated opcode (0x%02x)\n", op);
29946 break;
29947 }
29948#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
29949 {
29950 int i, x, x0;
29951 x = x0 = printf("%5d ", pos);
29952 for (i = 0; i < size; i++) {
29953 if (i == 6) {
29954 printf("\n%*s", x = x0, "");
29955 }
29956 x += printf(" %02X", tab[pos + i]);
29957 }
29958 printf("%*s", x0 + 20 - x, "");
29959 }
29960#endif
29961 if (bits[pos]) {
29962 printf("%5d: ", pos);
29963 } else {
29964 printf(" ");
29965 }
29966 printf("%s", oi->name);
29967 pos++;
29968 switch(oi->fmt) {
29969 case OP_FMT_none_int:
29970 printf(" %d", op - OP_push_0);
29971 break;
29972 case OP_FMT_npopx:
29973 printf(" %d", op - OP_call0);
29974 break;
29975 case OP_FMT_u8:
29976 printf(" %u", get_u8(tab + pos));
29977 break;
29978 case OP_FMT_i8:
29979 printf(" %d", get_i8(tab + pos));
29980 break;
29981 case OP_FMT_u16:
29982 case OP_FMT_npop:
29983 printf(" %u", get_u16(tab + pos));
29984 break;
29985 case OP_FMT_npop_u16:
29986 printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
29987 break;
29988 case OP_FMT_i16:
29989 printf(" %d", get_i16(tab + pos));
29990 break;
29991 case OP_FMT_i32:
29992 printf(" %d", get_i32(tab + pos));
29993 break;
29994 case OP_FMT_u32:
29995 printf(" %u", get_u32(tab + pos));
29996 break;
29997#if SHORT_OPCODES
29998 case OP_FMT_label8:
29999 addr = get_i8(tab + pos);
30000 goto has_addr1;
30001 case OP_FMT_label16:
30002 addr = get_i16(tab + pos);
30003 goto has_addr1;
30004#endif
30005 case OP_FMT_label:
30006 addr = get_u32(tab + pos);
30007 goto has_addr1;
30008 has_addr1:
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 break;
30016 case OP_FMT_label_u16:
30017 addr = get_u32(tab + pos);
30018 if (pass == 1)
30019 printf(" %u:%u", addr, label_slots[addr].pos);
30020 if (pass == 2)
30021 printf(" %u:%u", addr, label_slots[addr].pos2);
30022 if (pass == 3)
30023 printf(" %u", addr + pos);
30024 printf(",%u", get_u16(tab + pos + 4));
30025 break;
30026#if SHORT_OPCODES
30027 case OP_FMT_const8:
30028 idx = get_u8(tab + pos);
30029 goto has_pool_idx;
30030#endif
30031 case OP_FMT_const:
30032 idx = get_u32(tab + pos);
30033 goto has_pool_idx;
30034 has_pool_idx:
30035 printf(" %u: ", idx);
30036 if (idx < cpool_count) {
30037 JS_DumpValue(ctx, cpool[idx]);
30038 }
30039 break;
30040 case OP_FMT_atom:
30041 printf(" ");
30042 print_atom(ctx, get_u32(tab + pos));
30043 break;
30044 case OP_FMT_atom_u8:
30045 printf(" ");
30046 print_atom(ctx, get_u32(tab + pos));
30047 printf(",%d", get_u8(tab + pos + 4));
30048 break;
30049 case OP_FMT_atom_u16:
30050 printf(" ");
30051 print_atom(ctx, get_u32(tab + pos));
30052 printf(",%d", get_u16(tab + pos + 4));
30053 break;
30054 case OP_FMT_atom_label_u8:
30055 case OP_FMT_atom_label_u16:
30056 printf(" ");
30057 print_atom(ctx, get_u32(tab + pos));
30058 addr = get_u32(tab + pos + 4);
30059 if (pass == 1)
30060 printf(",%u:%u", addr, label_slots[addr].pos);
30061 if (pass == 2)
30062 printf(",%u:%u", addr, label_slots[addr].pos2);
30063 if (pass == 3)
30064 printf(",%u", addr + pos + 4);
30065 if (oi->fmt == OP_FMT_atom_label_u8)
30066 printf(",%u", get_u8(tab + pos + 8));
30067 else
30068 printf(",%u", get_u16(tab + pos + 8));
30069 break;
30070 case OP_FMT_none_loc:
30071 idx = (op - OP_get_loc0) % 4;
30072 goto has_loc;
30073 case OP_FMT_loc8:
30074 idx = get_u8(tab + pos);
30075 goto has_loc;
30076 case OP_FMT_loc:
30077 idx = get_u16(tab + pos);
30078 has_loc:
30079 printf(" %d: ", idx);
30080 if (idx < var_count) {
30081 print_atom(ctx, vars[idx].var_name);
30082 }
30083 break;
30084 case OP_FMT_none_arg:
30085 idx = (op - OP_get_arg0) % 4;
30086 goto has_arg;
30087 case OP_FMT_arg:
30088 idx = get_u16(tab + pos);
30089 has_arg:
30090 printf(" %d: ", idx);
30091 if (idx < arg_count) {
30092 print_atom(ctx, args[idx].var_name);
30093 }
30094 break;
30095 case OP_FMT_none_var_ref:
30096 idx = (op - OP_get_var_ref0) % 4;
30097 goto has_var_ref;
30098 case OP_FMT_var_ref:
30099 idx = get_u16(tab + pos);
30100 has_var_ref:
30101 printf(" %d: ", idx);
30102 if (idx < closure_var_count) {
30103 print_atom(ctx, closure_var[idx].var_name);
30104 }
30105 break;
30106 default:
30107 break;
30108 }
30109 printf("\n");
30110 pos += oi->size - 1;
30111 }
30112 if (source) {
30113 if (!in_source)
30114 printf("\n");
30115 print_lines(source, line, INT32_MAX);
30116 }
30117 js_free(ctx, bits);
30118}
30119
30120static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len)
30121{
30122 const uint8_t *p_end, *p;
30123 int pc, v, line_num, col_num, ret;
30124 unsigned int op;
30125 uint32_t val;
30126
30127 if (len <= 0)
30128 return;
30129
30130 printf("%5s %5s %5s\n", "PC", "LINE", "COL");
30131
30132 p = buf;
30133 p_end = buf + len;
30134
30135 /* get the function line and column numbers */
30136 ret = get_leb128(&val, p, p_end);
30137 if (ret < 0)
30138 goto fail;
30139 p += ret;
30140 line_num = val + 1;
30141
30142 ret = get_leb128(&val, p, p_end);
30143 if (ret < 0)
30144 goto fail;
30145 p += ret;
30146 col_num = val + 1;
30147
30148 printf("%5s %5d %5d\n", "-", line_num, col_num);
30149
30150 pc = 0;
30151 while (p < p_end) {
30152 op = *p++;
30153 if (op == 0) {
30154 ret = get_leb128(&val, p, p_end);
30155 if (ret < 0)
30156 goto fail;
30157 pc += val;
30158 p += ret;
30159 ret = get_sleb128(&v, p, p_end);
30160 if (ret < 0)
30161 goto fail;
30162 p += ret;
30163 line_num += v;
30164 } else {
30165 op -= PC2LINE_OP_FIRST;
30166 pc += (op / PC2LINE_RANGE);
30167 line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
30168 }
30169 ret = get_sleb128(&v, p, p_end);
30170 if (ret < 0)
30171 goto fail;
30172 p += ret;
30173 col_num += v;
30174
30175 printf("%5d %5d %5d\n", pc, line_num, col_num);
30176 }
30177 fail: ;
30178}
30179
30180static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
30181{
30182 int i;
30183 char atom_buf[ATOM_GET_STR_BUF_SIZE];
30184 const char *str;
30185
30186 if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
30187 int line_num, col_num;
30188 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
30189 line_num = find_line_num(ctx, b, -1, &col_num);
30190 printf("%s:%d:%d: ", str, line_num, col_num);
30191 }
30192
30193 str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
30194 printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
30195 if (b->js_mode) {
30196 printf(" mode:");
30197 if (b->js_mode & JS_MODE_STRICT)
30198 printf(" strict");
30199 printf("\n");
30200 }
30201 if (b->arg_count && b->vardefs) {
30202 printf(" args:");
30203 for(i = 0; i < b->arg_count; i++) {
30204 printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
30205 b->vardefs[i].var_name));
30206 }
30207 printf("\n");
30208 }
30209 if (b->var_count && b->vardefs) {
30210 printf(" locals:\n");
30211 for(i = 0; i < b->var_count; i++) {
30212 JSVarDef *vd = &b->vardefs[b->arg_count + i];
30213 printf("%5d: %s %s", i,
30214 vd->var_kind == JS_VAR_CATCH ? "catch" :
30215 (vd->var_kind == JS_VAR_FUNCTION_DECL ||
30216 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
30217 vd->is_const ? "const" :
30218 vd->is_lexical ? "let" : "var",
30219 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
30220 if (vd->scope_level)
30221 printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
30222 printf("\n");
30223 }
30224 }
30225 if (b->closure_var_count) {
30226 printf(" closure vars:\n");
30227 for(i = 0; i < b->closure_var_count; i++) {
30228 JSClosureVar *cv = &b->closure_var[i];
30229 printf("%5d: %s %s:%s%d %s\n", i,
30230 JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
30231 cv->is_local ? "local" : "parent",
30232 cv->is_arg ? "arg" : "loc", cv->var_idx,
30233 cv->is_const ? "const" :
30234 cv->is_lexical ? "let" : "var");
30235 }
30236 }
30237 printf(" stack_size: %d\n", b->stack_size);
30238 printf(" opcodes:\n");
30239 dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
30240 b->vardefs, b->arg_count,
30241 b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
30242 b->closure_var, b->closure_var_count,
30243 b->cpool, b->cpool_count,
30244 b->has_debug ? b->debug.source : NULL,
30245 NULL, b);
30246#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
30247 if (b->has_debug)
30248 dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len);
30249#endif
30250 printf("\n");
30251}
30252#endif
30253
30254static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
30255 BOOL is_local, BOOL is_arg,
30256 int var_idx, JSAtom var_name,
30257 BOOL is_const, BOOL is_lexical,
30258 JSVarKindEnum var_kind)
30259{
30260 JSClosureVar *cv;
30261
30262 /* the closure variable indexes are currently stored on 16 bits */
30263 if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
30264 JS_ThrowInternalError(ctx, "too many closure variables");
30265 return -1;
30266 }
30267
30268 if (js_resize_array(ctx, (void **)&s->closure_var,
30269 sizeof(s->closure_var[0]),
30270 &s->closure_var_size, s->closure_var_count + 1))
30271 return -1;
30272 cv = &s->closure_var[s->closure_var_count++];
30273 cv->is_local = is_local;
30274 cv->is_arg = is_arg;
30275 cv->is_const = is_const;
30276 cv->is_lexical = is_lexical;
30277 cv->var_kind = var_kind;
30278 cv->var_idx = var_idx;
30279 cv->var_name = JS_DupAtom(ctx, var_name);
30280 return s->closure_var_count - 1;
30281}
30282
30283static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
30284 JSAtom var_name)
30285{
30286 int i;
30287 for(i = 0; i < s->closure_var_count; i++) {
30288 JSClosureVar *cv = &s->closure_var[i];
30289 if (cv->var_name == var_name)
30290 return i;
30291 }
30292 return -1;
30293}
30294
30295/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
30296 local variable (is_local = TRUE) or a closure (is_local = FALSE) in
30297 'fd' */
30298static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
30299 JSFunctionDef *fd, BOOL is_local,
30300 BOOL is_arg, int var_idx, JSAtom var_name,
30301 BOOL is_const, BOOL is_lexical,
30302 JSVarKindEnum var_kind)
30303{
30304 int i;
30305
30306 if (fd != s->parent) {
30307 var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
30308 is_arg, var_idx, var_name,
30309 is_const, is_lexical, var_kind);
30310 if (var_idx < 0)
30311 return -1;
30312 is_local = FALSE;
30313 }
30314 for(i = 0; i < s->closure_var_count; i++) {
30315 JSClosureVar *cv = &s->closure_var[i];
30316 if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
30317 cv->is_local == is_local)
30318 return i;
30319 }
30320 return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
30321 is_const, is_lexical, var_kind);
30322}
30323
30324static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
30325 JSFunctionDef *fd, BOOL is_arg,
30326 int var_idx, JSAtom var_name,
30327 BOOL is_const, BOOL is_lexical,
30328 JSVarKindEnum var_kind)
30329{
30330 return get_closure_var2(ctx, s, fd, TRUE, is_arg,
30331 var_idx, var_name, is_const, is_lexical,
30332 var_kind);
30333}
30334
30335static int get_with_scope_opcode(int op)
30336{
30337 if (op == OP_scope_get_var_undef)
30338 return OP_with_get_var;
30339 else
30340 return OP_with_get_var + (op - OP_scope_get_var);
30341}
30342
30343static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
30344{
30345 int opcode = bc_buf[pos];
30346 return (bc_buf[pos + 1] == OP_put_ref_value &&
30347 (opcode == OP_insert3 ||
30348 opcode == OP_perm4 ||
30349 opcode == OP_nop ||
30350 opcode == OP_rot3l));
30351}
30352
30353static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
30354{
30355 int opcode = bc_buf[pos];
30356 return (bc_buf[pos + 1] == OP_put_ref_value &&
30357 (opcode == OP_insert3 ||
30358 opcode == OP_perm4 ||
30359 opcode == OP_nop ||
30360 opcode == OP_rot3l));
30361}
30362
30363static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
30364 DynBuf *bc, uint8_t *bc_buf,
30365 LabelSlot *ls, int pos_next,
30366 int get_op, int var_idx)
30367{
30368 int label_pos, end_pos, pos;
30369
30370 /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
30371 but only if expr does not modify `a`.
30372 should scan the code between pos_next and label_pos
30373 for operations that can potentially change `a`:
30374 OP_scope_make_ref(a), function calls, jumps and gosub.
30375 */
30376 /* replace the reference get/put with normal variable
30377 accesses */
30378 if (bc_buf[pos_next] == OP_get_ref_value) {
30379 dbuf_putc(bc, get_op);
30380 dbuf_put_u16(bc, var_idx);
30381 pos_next++;
30382 }
30383 /* remove the OP_label to make room for replacement */
30384 /* label should have a refcount of 0 anyway */
30385 /* XXX: should avoid this patch by inserting nops in phase 1 */
30386 label_pos = ls->pos;
30387 pos = label_pos - 5;
30388 assert(bc_buf[pos] == OP_label);
30389 /* label points to an instruction pair:
30390 - insert3 / put_ref_value
30391 - perm4 / put_ref_value
30392 - rot3l / put_ref_value
30393 - nop / put_ref_value
30394 */
30395 end_pos = label_pos + 2;
30396 if (bc_buf[label_pos] == OP_insert3)
30397 bc_buf[pos++] = OP_dup;
30398 bc_buf[pos] = get_op + 1;
30399 put_u16(bc_buf + pos + 1, var_idx);
30400 pos += 3;
30401 /* pad with OP_nop */
30402 while (pos < end_pos)
30403 bc_buf[pos++] = OP_nop;
30404 return pos_next;
30405}
30406
30407static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
30408 DynBuf *bc, uint8_t *bc_buf,
30409 LabelSlot *ls, int pos_next,
30410 JSAtom var_name)
30411{
30412 int label_pos, end_pos, pos, op;
30413 BOOL is_strict;
30414 is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
30415
30416 /* replace the reference get/put with normal variable
30417 accesses */
30418 if (is_strict) {
30419 /* need to check if the variable exists before evaluating the right
30420 expression */
30421 /* XXX: need an extra OP_true if destructuring an array */
30422 dbuf_putc(bc, OP_check_var);
30423 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30424 } else {
30425 /* XXX: need 2 extra OP_true if destructuring an array */
30426 }
30427 if (bc_buf[pos_next] == OP_get_ref_value) {
30428 dbuf_putc(bc, OP_get_var);
30429 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30430 pos_next++;
30431 }
30432 /* remove the OP_label to make room for replacement */
30433 /* label should have a refcount of 0 anyway */
30434 /* XXX: should have emitted several OP_nop to avoid this kludge */
30435 label_pos = ls->pos;
30436 pos = label_pos - 5;
30437 assert(bc_buf[pos] == OP_label);
30438 end_pos = label_pos + 2;
30439 op = bc_buf[label_pos];
30440 if (is_strict) {
30441 if (op != OP_nop) {
30442 switch(op) {
30443 case OP_insert3:
30444 op = OP_insert2;
30445 break;
30446 case OP_perm4:
30447 op = OP_perm3;
30448 break;
30449 case OP_rot3l:
30450 op = OP_swap;
30451 break;
30452 default:
30453 abort();
30454 }
30455 bc_buf[pos++] = op;
30456 }
30457 } else {
30458 if (op == OP_insert3)
30459 bc_buf[pos++] = OP_dup;
30460 }
30461 if (is_strict) {
30462 bc_buf[pos] = OP_put_var_strict;
30463 /* XXX: need 1 extra OP_drop if destructuring an array */
30464 } else {
30465 bc_buf[pos] = OP_put_var;
30466 /* XXX: need 2 extra OP_drop if destructuring an array */
30467 }
30468 put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
30469 pos += 5;
30470 /* pad with OP_nop */
30471 while (pos < end_pos)
30472 bc_buf[pos++] = OP_nop;
30473 return pos_next;
30474}
30475
30476static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
30477{
30478 int idx;
30479 idx = add_var(ctx, fd, JS_ATOM_this);
30480 if (idx >= 0 && fd->is_derived_class_constructor) {
30481 JSVarDef *vd = &fd->vars[idx];
30482 /* XXX: should have is_this flag or var type */
30483 vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
30484 in a derived class constructor */
30485 }
30486 return idx;
30487}
30488
30489static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
30490 JSAtom var_name)
30491{
30492 int var_idx;
30493
30494 if (!s->has_this_binding)
30495 return -1;
30496 switch(var_name) {
30497 case JS_ATOM_home_object:
30498 /* 'home_object' pseudo variable */
30499 if (s->home_object_var_idx < 0)
30500 s->home_object_var_idx = add_var(ctx, s, var_name);
30501 var_idx = s->home_object_var_idx;
30502 break;
30503 case JS_ATOM_this_active_func:
30504 /* 'this.active_func' pseudo variable */
30505 if (s->this_active_func_var_idx < 0)
30506 s->this_active_func_var_idx = add_var(ctx, s, var_name);
30507 var_idx = s->this_active_func_var_idx;
30508 break;
30509 case JS_ATOM_new_target:
30510 /* 'new.target' pseudo variable */
30511 if (s->new_target_var_idx < 0)
30512 s->new_target_var_idx = add_var(ctx, s, var_name);
30513 var_idx = s->new_target_var_idx;
30514 break;
30515 case JS_ATOM_this:
30516 /* 'this' pseudo variable */
30517 if (s->this_var_idx < 0)
30518 s->this_var_idx = add_var_this(ctx, s);
30519 var_idx = s->this_var_idx;
30520 break;
30521 default:
30522 var_idx = -1;
30523 break;
30524 }
30525 return var_idx;
30526}
30527
30528/* test if 'var_name' is in the variable object on the stack. If is it
30529 the case, handle it and jump to 'label_done' */
30530static void var_object_test(JSContext *ctx, JSFunctionDef *s,
30531 JSAtom var_name, int op, DynBuf *bc,
30532 int *plabel_done, BOOL is_with)
30533{
30534 dbuf_putc(bc, get_with_scope_opcode(op));
30535 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30536 *plabel_done = new_label_fd(s, *plabel_done);
30537 dbuf_put_u32(bc, *plabel_done);
30538 dbuf_putc(bc, is_with);
30539 update_label(s, *plabel_done, 1);
30540 s->jump_size++;
30541}
30542
30543/* return the position of the next opcode */
30544static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
30545 JSAtom var_name, int scope_level, int op,
30546 DynBuf *bc, uint8_t *bc_buf,
30547 LabelSlot *ls, int pos_next)
30548{
30549 int idx, var_idx, is_put;
30550 int label_done;
30551 JSFunctionDef *fd;
30552 JSVarDef *vd;
30553 BOOL is_pseudo_var, is_arg_scope;
30554
30555 label_done = -1;
30556
30557 /* XXX: could be simpler to use a specific function to
30558 resolve the pseudo variables */
30559 is_pseudo_var = (var_name == JS_ATOM_home_object ||
30560 var_name == JS_ATOM_this_active_func ||
30561 var_name == JS_ATOM_new_target ||
30562 var_name == JS_ATOM_this);
30563
30564 /* resolve local scoped variables */
30565 var_idx = -1;
30566 for (idx = s->scopes[scope_level].first; idx >= 0;) {
30567 vd = &s->vars[idx];
30568 if (vd->var_name == var_name) {
30569 if (op == OP_scope_put_var || op == OP_scope_make_ref) {
30570 if (vd->is_const) {
30571 dbuf_putc(bc, OP_throw_error);
30572 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30573 dbuf_putc(bc, JS_THROW_VAR_RO);
30574 goto done;
30575 }
30576 }
30577 var_idx = idx;
30578 break;
30579 } else
30580 if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
30581 dbuf_putc(bc, OP_get_loc);
30582 dbuf_put_u16(bc, idx);
30583 var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
30584 }
30585 idx = vd->scope_next;
30586 }
30587 is_arg_scope = (idx == ARG_SCOPE_END);
30588 if (var_idx < 0) {
30589 /* argument scope: variables are not visible but pseudo
30590 variables are visible */
30591 if (!is_arg_scope) {
30592 var_idx = find_var(ctx, s, var_name);
30593 }
30594
30595 if (var_idx < 0 && is_pseudo_var)
30596 var_idx = resolve_pseudo_var(ctx, s, var_name);
30597
30598 if (var_idx < 0 && var_name == JS_ATOM_arguments &&
30599 s->has_arguments_binding) {
30600 /* 'arguments' pseudo variable */
30601 var_idx = add_arguments_var(ctx, s);
30602 }
30603 if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
30604 /* add a new variable with the function name */
30605 var_idx = add_func_var(ctx, s, var_name);
30606 }
30607 }
30608 if (var_idx >= 0) {
30609 if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
30610 !(var_idx & ARGUMENT_VAR_OFFSET) &&
30611 s->vars[var_idx].is_const) {
30612 /* only happens when assigning a function expression name
30613 in strict mode */
30614 dbuf_putc(bc, OP_throw_error);
30615 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30616 dbuf_putc(bc, JS_THROW_VAR_RO);
30617 goto done;
30618 }
30619 /* OP_scope_put_var_init is only used to initialize a
30620 lexical variable, so it is never used in a with or var object. It
30621 can be used with a closure (module global variable case). */
30622 switch (op) {
30623 case OP_scope_make_ref:
30624 if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
30625 s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
30626 /* Create a dummy object reference for the func_var */
30627 dbuf_putc(bc, OP_object);
30628 dbuf_putc(bc, OP_get_loc);
30629 dbuf_put_u16(bc, var_idx);
30630 dbuf_putc(bc, OP_define_field);
30631 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30632 dbuf_putc(bc, OP_push_atom_value);
30633 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30634 } else
30635 if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
30636 int get_op;
30637 if (var_idx & ARGUMENT_VAR_OFFSET) {
30638 get_op = OP_get_arg;
30639 var_idx -= ARGUMENT_VAR_OFFSET;
30640 } else {
30641 if (s->vars[var_idx].is_lexical)
30642 get_op = OP_get_loc_check;
30643 else
30644 get_op = OP_get_loc;
30645 }
30646 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
30647 pos_next, get_op, var_idx);
30648 } else {
30649 /* Create a dummy object with a named slot that is
30650 a reference to the local variable */
30651 if (var_idx & ARGUMENT_VAR_OFFSET) {
30652 dbuf_putc(bc, OP_make_arg_ref);
30653 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30654 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
30655 } else {
30656 dbuf_putc(bc, OP_make_loc_ref);
30657 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30658 dbuf_put_u16(bc, var_idx);
30659 }
30660 }
30661 break;
30662 case OP_scope_get_ref:
30663 dbuf_putc(bc, OP_undefined);
30664 /* fall thru */
30665 case OP_scope_get_var_checkthis:
30666 case OP_scope_get_var_undef:
30667 case OP_scope_get_var:
30668 case OP_scope_put_var:
30669 case OP_scope_put_var_init:
30670 is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
30671 if (var_idx & ARGUMENT_VAR_OFFSET) {
30672 dbuf_putc(bc, OP_get_arg + is_put);
30673 dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
30674 } else {
30675 if (is_put) {
30676 if (s->vars[var_idx].is_lexical) {
30677 if (op == OP_scope_put_var_init) {
30678 /* 'this' can only be initialized once */
30679 if (var_name == JS_ATOM_this)
30680 dbuf_putc(bc, OP_put_loc_check_init);
30681 else
30682 dbuf_putc(bc, OP_put_loc);
30683 } else {
30684 dbuf_putc(bc, OP_put_loc_check);
30685 }
30686 } else {
30687 dbuf_putc(bc, OP_put_loc);
30688 }
30689 } else {
30690 if (s->vars[var_idx].is_lexical) {
30691 if (op == OP_scope_get_var_checkthis) {
30692 /* only used for 'this' return in derived class constructors */
30693 dbuf_putc(bc, OP_get_loc_checkthis);
30694 } else {
30695 dbuf_putc(bc, OP_get_loc_check);
30696 }
30697 } else {
30698 dbuf_putc(bc, OP_get_loc);
30699 }
30700 }
30701 dbuf_put_u16(bc, var_idx);
30702 }
30703 break;
30704 case OP_scope_delete_var:
30705 dbuf_putc(bc, OP_push_false);
30706 break;
30707 }
30708 goto done;
30709 }
30710 /* check eval object */
30711 if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
30712 dbuf_putc(bc, OP_get_loc);
30713 dbuf_put_u16(bc, s->var_object_idx);
30714 var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
30715 }
30716 /* check eval object in argument scope */
30717 if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
30718 dbuf_putc(bc, OP_get_loc);
30719 dbuf_put_u16(bc, s->arg_var_object_idx);
30720 var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
30721 }
30722
30723 /* check parent scopes */
30724 for (fd = s; fd->parent;) {
30725 scope_level = fd->parent_scope_level;
30726 fd = fd->parent;
30727 for (idx = fd->scopes[scope_level].first; idx >= 0;) {
30728 vd = &fd->vars[idx];
30729 if (vd->var_name == var_name) {
30730 if (op == OP_scope_put_var || op == OP_scope_make_ref) {
30731 if (vd->is_const) {
30732 dbuf_putc(bc, OP_throw_error);
30733 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30734 dbuf_putc(bc, JS_THROW_VAR_RO);
30735 goto done;
30736 }
30737 }
30738 var_idx = idx;
30739 break;
30740 } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
30741 vd->is_captured = 1;
30742 idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
30743 if (idx >= 0) {
30744 dbuf_putc(bc, OP_get_var_ref);
30745 dbuf_put_u16(bc, idx);
30746 var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
30747 }
30748 }
30749 idx = vd->scope_next;
30750 }
30751 is_arg_scope = (idx == ARG_SCOPE_END);
30752 if (var_idx >= 0)
30753 break;
30754
30755 if (!is_arg_scope) {
30756 var_idx = find_var(ctx, fd, var_name);
30757 if (var_idx >= 0)
30758 break;
30759 }
30760 if (is_pseudo_var) {
30761 var_idx = resolve_pseudo_var(ctx, fd, var_name);
30762 if (var_idx >= 0)
30763 break;
30764 }
30765 if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
30766 var_idx = add_arguments_var(ctx, fd);
30767 break;
30768 }
30769 if (fd->is_func_expr && fd->func_name == var_name) {
30770 /* add a new variable with the function name */
30771 var_idx = add_func_var(ctx, fd, var_name);
30772 break;
30773 }
30774
30775 /* check eval object */
30776 if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
30777 vd = &fd->vars[fd->var_object_idx];
30778 vd->is_captured = 1;
30779 idx = get_closure_var(ctx, s, fd, FALSE,
30780 fd->var_object_idx, vd->var_name,
30781 FALSE, FALSE, JS_VAR_NORMAL);
30782 dbuf_putc(bc, OP_get_var_ref);
30783 dbuf_put_u16(bc, idx);
30784 var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
30785 }
30786
30787 /* check eval object in argument scope */
30788 if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
30789 vd = &fd->vars[fd->arg_var_object_idx];
30790 vd->is_captured = 1;
30791 idx = get_closure_var(ctx, s, fd, FALSE,
30792 fd->arg_var_object_idx, vd->var_name,
30793 FALSE, FALSE, JS_VAR_NORMAL);
30794 dbuf_putc(bc, OP_get_var_ref);
30795 dbuf_put_u16(bc, idx);
30796 var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
30797 }
30798
30799 if (fd->is_eval)
30800 break; /* it it necessarily the top level function */
30801 }
30802
30803 /* check direct eval scope (in the closure of the eval function
30804 which is necessarily at the top level) */
30805 if (!fd)
30806 fd = s;
30807 if (var_idx < 0 && fd->is_eval) {
30808 int idx1;
30809 for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
30810 JSClosureVar *cv = &fd->closure_var[idx1];
30811 if (var_name == cv->var_name) {
30812 if (fd != s) {
30813 idx = get_closure_var2(ctx, s, fd,
30814 FALSE,
30815 cv->is_arg, idx1,
30816 cv->var_name, cv->is_const,
30817 cv->is_lexical, cv->var_kind);
30818 } else {
30819 idx = idx1;
30820 }
30821 goto has_idx;
30822 } else if ((cv->var_name == JS_ATOM__var_ ||
30823 cv->var_name == JS_ATOM__arg_var_ ||
30824 cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
30825 int is_with = (cv->var_name == JS_ATOM__with_);
30826 if (fd != s) {
30827 idx = get_closure_var2(ctx, s, fd,
30828 FALSE,
30829 cv->is_arg, idx1,
30830 cv->var_name, FALSE, FALSE,
30831 JS_VAR_NORMAL);
30832 } else {
30833 idx = idx1;
30834 }
30835 dbuf_putc(bc, OP_get_var_ref);
30836 dbuf_put_u16(bc, idx);
30837 var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
30838 }
30839 }
30840 }
30841
30842 if (var_idx >= 0) {
30843 /* find the corresponding closure variable */
30844 if (var_idx & ARGUMENT_VAR_OFFSET) {
30845 fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
30846 idx = get_closure_var(ctx, s, fd,
30847 TRUE, var_idx - ARGUMENT_VAR_OFFSET,
30848 var_name, FALSE, FALSE, JS_VAR_NORMAL);
30849 } else {
30850 fd->vars[var_idx].is_captured = 1;
30851 idx = get_closure_var(ctx, s, fd,
30852 FALSE, var_idx,
30853 var_name,
30854 fd->vars[var_idx].is_const,
30855 fd->vars[var_idx].is_lexical,
30856 fd->vars[var_idx].var_kind);
30857 }
30858 if (idx >= 0) {
30859 has_idx:
30860 if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
30861 s->closure_var[idx].is_const) {
30862 dbuf_putc(bc, OP_throw_error);
30863 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30864 dbuf_putc(bc, JS_THROW_VAR_RO);
30865 goto done;
30866 }
30867 switch (op) {
30868 case OP_scope_make_ref:
30869 if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
30870 /* Create a dummy object reference for the func_var */
30871 dbuf_putc(bc, OP_object);
30872 dbuf_putc(bc, OP_get_var_ref);
30873 dbuf_put_u16(bc, idx);
30874 dbuf_putc(bc, OP_define_field);
30875 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30876 dbuf_putc(bc, OP_push_atom_value);
30877 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30878 } else
30879 if (label_done == -1 &&
30880 can_opt_put_ref_value(bc_buf, ls->pos)) {
30881 int get_op;
30882 if (s->closure_var[idx].is_lexical)
30883 get_op = OP_get_var_ref_check;
30884 else
30885 get_op = OP_get_var_ref;
30886 pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
30887 pos_next,
30888 get_op, idx);
30889 } else {
30890 /* Create a dummy object with a named slot that is
30891 a reference to the closure variable */
30892 dbuf_putc(bc, OP_make_var_ref_ref);
30893 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30894 dbuf_put_u16(bc, idx);
30895 }
30896 break;
30897 case OP_scope_get_ref:
30898 /* XXX: should create a dummy object with a named slot that is
30899 a reference to the closure variable */
30900 dbuf_putc(bc, OP_undefined);
30901 /* fall thru */
30902 case OP_scope_get_var_undef:
30903 case OP_scope_get_var:
30904 case OP_scope_put_var:
30905 case OP_scope_put_var_init:
30906 is_put = (op == OP_scope_put_var ||
30907 op == OP_scope_put_var_init);
30908 if (is_put) {
30909 if (s->closure_var[idx].is_lexical) {
30910 if (op == OP_scope_put_var_init) {
30911 /* 'this' can only be initialized once */
30912 if (var_name == JS_ATOM_this)
30913 dbuf_putc(bc, OP_put_var_ref_check_init);
30914 else
30915 dbuf_putc(bc, OP_put_var_ref);
30916 } else {
30917 dbuf_putc(bc, OP_put_var_ref_check);
30918 }
30919 } else {
30920 dbuf_putc(bc, OP_put_var_ref);
30921 }
30922 } else {
30923 if (s->closure_var[idx].is_lexical) {
30924 dbuf_putc(bc, OP_get_var_ref_check);
30925 } else {
30926 dbuf_putc(bc, OP_get_var_ref);
30927 }
30928 }
30929 dbuf_put_u16(bc, idx);
30930 break;
30931 case OP_scope_delete_var:
30932 dbuf_putc(bc, OP_push_false);
30933 break;
30934 }
30935 goto done;
30936 }
30937 }
30938
30939 /* global variable access */
30940
30941 switch (op) {
30942 case OP_scope_make_ref:
30943 if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
30944 pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
30945 pos_next, var_name);
30946 } else {
30947 dbuf_putc(bc, OP_make_var_ref);
30948 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30949 }
30950 break;
30951 case OP_scope_get_ref:
30952 /* XXX: should create a dummy object with a named slot that is
30953 a reference to the global variable */
30954 dbuf_putc(bc, OP_undefined);
30955 dbuf_putc(bc, OP_get_var);
30956 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30957 break;
30958 case OP_scope_get_var_undef:
30959 case OP_scope_get_var:
30960 case OP_scope_put_var:
30961 dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
30962 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30963 break;
30964 case OP_scope_put_var_init:
30965 dbuf_putc(bc, OP_put_var_init);
30966 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30967 break;
30968 case OP_scope_delete_var:
30969 dbuf_putc(bc, OP_delete_var);
30970 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
30971 break;
30972 }
30973done:
30974 if (label_done >= 0) {
30975 dbuf_putc(bc, OP_label);
30976 dbuf_put_u32(bc, label_done);
30977 s->label_slots[label_done].pos2 = bc->size;
30978 }
30979 return pos_next;
30980}
30981
30982/* search in all scopes */
30983static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
30984 JSAtom name, int scope_level)
30985{
30986 int idx;
30987
30988 idx = fd->scopes[scope_level].first;
30989 while (idx >= 0) {
30990 if (fd->vars[idx].var_name == name)
30991 return idx;
30992 idx = fd->vars[idx].scope_next;
30993 }
30994 return -1;
30995}
30996
30997static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
30998{
30999 /* if the field is not initialized, the error is catched when
31000 accessing it */
31001 if (is_ref)
31002 dbuf_putc(bc, OP_get_var_ref);
31003 else
31004 dbuf_putc(bc, OP_get_loc);
31005 dbuf_put_u16(bc, idx);
31006}
31007
31008static int resolve_scope_private_field1(JSContext *ctx,
31009 BOOL *pis_ref, int *pvar_kind,
31010 JSFunctionDef *s,
31011 JSAtom var_name, int scope_level)
31012{
31013 int idx, var_kind;
31014 JSFunctionDef *fd;
31015 BOOL is_ref;
31016
31017 fd = s;
31018 is_ref = FALSE;
31019 for(;;) {
31020 idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
31021 if (idx >= 0) {
31022 var_kind = fd->vars[idx].var_kind;
31023 if (is_ref) {
31024 idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
31025 TRUE, TRUE, JS_VAR_NORMAL);
31026 if (idx < 0)
31027 return -1;
31028 }
31029 break;
31030 }
31031 scope_level = fd->parent_scope_level;
31032 if (!fd->parent) {
31033 if (fd->is_eval) {
31034 /* closure of the eval function (top level) */
31035 for (idx = 0; idx < fd->closure_var_count; idx++) {
31036 JSClosureVar *cv = &fd->closure_var[idx];
31037 if (cv->var_name == var_name) {
31038 var_kind = cv->var_kind;
31039 is_ref = TRUE;
31040 if (fd != s) {
31041 idx = get_closure_var2(ctx, s, fd,
31042 FALSE,
31043 cv->is_arg, idx,
31044 cv->var_name, cv->is_const,
31045 cv->is_lexical,
31046 cv->var_kind);
31047 if (idx < 0)
31048 return -1;
31049 }
31050 goto done;
31051 }
31052 }
31053 }
31054 /* XXX: no line number info */
31055 JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
31056 var_name);
31057 return -1;
31058 } else {
31059 fd = fd->parent;
31060 }
31061 is_ref = TRUE;
31062 }
31063 done:
31064 *pis_ref = is_ref;
31065 *pvar_kind = var_kind;
31066 return idx;
31067}
31068
31069/* return 0 if OK or -1 if the private field could not be resolved */
31070static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
31071 JSAtom var_name, int scope_level, int op,
31072 DynBuf *bc)
31073{
31074 int idx, var_kind;
31075 BOOL is_ref;
31076
31077 idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
31078 var_name, scope_level);
31079 if (idx < 0)
31080 return -1;
31081 assert(var_kind != JS_VAR_NORMAL);
31082 switch (op) {
31083 case OP_scope_get_private_field:
31084 case OP_scope_get_private_field2:
31085 switch(var_kind) {
31086 case JS_VAR_PRIVATE_FIELD:
31087 if (op == OP_scope_get_private_field2)
31088 dbuf_putc(bc, OP_dup);
31089 get_loc_or_ref(bc, is_ref, idx);
31090 dbuf_putc(bc, OP_get_private_field);
31091 break;
31092 case JS_VAR_PRIVATE_METHOD:
31093 get_loc_or_ref(bc, is_ref, idx);
31094 dbuf_putc(bc, OP_check_brand);
31095 if (op != OP_scope_get_private_field2)
31096 dbuf_putc(bc, OP_nip);
31097 break;
31098 case JS_VAR_PRIVATE_GETTER:
31099 case JS_VAR_PRIVATE_GETTER_SETTER:
31100 if (op == OP_scope_get_private_field2)
31101 dbuf_putc(bc, OP_dup);
31102 get_loc_or_ref(bc, is_ref, idx);
31103 dbuf_putc(bc, OP_check_brand);
31104 dbuf_putc(bc, OP_call_method);
31105 dbuf_put_u16(bc, 0);
31106 break;
31107 case JS_VAR_PRIVATE_SETTER:
31108 /* XXX: add clearer error message */
31109 dbuf_putc(bc, OP_throw_error);
31110 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
31111 dbuf_putc(bc, JS_THROW_VAR_RO);
31112 break;
31113 default:
31114 abort();
31115 }
31116 break;
31117 case OP_scope_put_private_field:
31118 switch(var_kind) {
31119 case JS_VAR_PRIVATE_FIELD:
31120 get_loc_or_ref(bc, is_ref, idx);
31121 dbuf_putc(bc, OP_put_private_field);
31122 break;
31123 case JS_VAR_PRIVATE_METHOD:
31124 case JS_VAR_PRIVATE_GETTER:
31125 /* XXX: add clearer error message */
31126 dbuf_putc(bc, OP_throw_error);
31127 dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
31128 dbuf_putc(bc, JS_THROW_VAR_RO);
31129 break;
31130 case JS_VAR_PRIVATE_SETTER:
31131 case JS_VAR_PRIVATE_GETTER_SETTER:
31132 {
31133 JSAtom setter_name = get_private_setter_name(ctx, var_name);
31134 if (setter_name == JS_ATOM_NULL)
31135 return -1;
31136 idx = resolve_scope_private_field1(ctx, &is_ref,
31137 &var_kind, s,
31138 setter_name, scope_level);
31139 JS_FreeAtom(ctx, setter_name);
31140 if (idx < 0)
31141 return -1;
31142 assert(var_kind == JS_VAR_PRIVATE_SETTER);
31143 get_loc_or_ref(bc, is_ref, idx);
31144 dbuf_putc(bc, OP_swap);
31145 /* obj func value */
31146 dbuf_putc(bc, OP_rot3r);
31147 /* value obj func */
31148 dbuf_putc(bc, OP_check_brand);
31149 dbuf_putc(bc, OP_rot3l);
31150 /* obj func value */
31151 dbuf_putc(bc, OP_call_method);
31152 dbuf_put_u16(bc, 1);
31153 dbuf_putc(bc, OP_drop);
31154 }
31155 break;
31156 default:
31157 abort();
31158 }
31159 break;
31160 case OP_scope_in_private_field:
31161 get_loc_or_ref(bc, is_ref, idx);
31162 dbuf_putc(bc, OP_private_in);
31163 break;
31164 default:
31165 abort();
31166 }
31167 return 0;
31168}
31169
31170static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
31171 int scope_level)
31172{
31173 int idx;
31174 JSVarDef *vd;
31175
31176 for (idx = s->scopes[scope_level].first; idx >= 0;) {
31177 vd = &s->vars[idx];
31178 vd->is_captured = 1;
31179 idx = vd->scope_next;
31180 }
31181}
31182
31183/* XXX: should handle the argument scope generically */
31184static BOOL is_var_in_arg_scope(const JSVarDef *vd)
31185{
31186 return (vd->var_name == JS_ATOM_home_object ||
31187 vd->var_name == JS_ATOM_this_active_func ||
31188 vd->var_name == JS_ATOM_new_target ||
31189 vd->var_name == JS_ATOM_this ||
31190 vd->var_name == JS_ATOM__arg_var_ ||
31191 vd->var_kind == JS_VAR_FUNCTION_NAME);
31192}
31193
31194static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
31195{
31196 JSFunctionDef *fd;
31197 JSVarDef *vd;
31198 int i, scope_level, scope_idx;
31199 BOOL has_arguments_binding, has_this_binding, is_arg_scope;
31200
31201 /* in non strict mode, variables are created in the caller's
31202 environment object */
31203 if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
31204 s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
31205 if (s->has_parameter_expressions) {
31206 /* an additional variable object is needed for the
31207 argument scope */
31208 s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
31209 }
31210 }
31211
31212 /* eval can potentially use 'arguments' so we must define it */
31213 has_this_binding = s->has_this_binding;
31214 if (has_this_binding) {
31215 if (s->this_var_idx < 0)
31216 s->this_var_idx = add_var_this(ctx, s);
31217 if (s->new_target_var_idx < 0)
31218 s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
31219 if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
31220 s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
31221 if (s->has_home_object && s->home_object_var_idx < 0)
31222 s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
31223 }
31224 has_arguments_binding = s->has_arguments_binding;
31225 if (has_arguments_binding) {
31226 add_arguments_var(ctx, s);
31227 /* also add an arguments binding in the argument scope to
31228 raise an error if a direct eval in the argument scope tries
31229 to redefine it */
31230 if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
31231 add_arguments_arg(ctx, s);
31232 }
31233 if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
31234 add_func_var(ctx, s, s->func_name);
31235
31236 /* eval can use all the variables of the enclosing functions, so
31237 they must be all put in the closure. The closure variables are
31238 ordered by scope. It works only because no closure are created
31239 before. */
31240 assert(s->is_eval || s->closure_var_count == 0);
31241
31242 /* XXX: inefficient, but eval performance is less critical */
31243 fd = s;
31244 for(;;) {
31245 scope_level = fd->parent_scope_level;
31246 fd = fd->parent;
31247 if (!fd)
31248 break;
31249 /* add 'this' if it was not previously added */
31250 if (!has_this_binding && fd->has_this_binding) {
31251 if (fd->this_var_idx < 0)
31252 fd->this_var_idx = add_var_this(ctx, fd);
31253 if (fd->new_target_var_idx < 0)
31254 fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
31255 if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
31256 fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
31257 if (fd->has_home_object && fd->home_object_var_idx < 0)
31258 fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
31259 has_this_binding = TRUE;
31260 }
31261 /* add 'arguments' if it was not previously added */
31262 if (!has_arguments_binding && fd->has_arguments_binding) {
31263 add_arguments_var(ctx, fd);
31264 has_arguments_binding = TRUE;
31265 }
31266 /* add function name */
31267 if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
31268 add_func_var(ctx, fd, fd->func_name);
31269
31270 /* add lexical variables */
31271 scope_idx = fd->scopes[scope_level].first;
31272 while (scope_idx >= 0) {
31273 vd = &fd->vars[scope_idx];
31274 vd->is_captured = 1;
31275 get_closure_var(ctx, s, fd, FALSE, scope_idx,
31276 vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
31277 scope_idx = vd->scope_next;
31278 }
31279 is_arg_scope = (scope_idx == ARG_SCOPE_END);
31280 if (!is_arg_scope) {
31281 /* add unscoped variables */
31282 /* XXX: propagate is_const and var_kind too ? */
31283 for(i = 0; i < fd->arg_count; i++) {
31284 vd = &fd->args[i];
31285 if (vd->var_name != JS_ATOM_NULL) {
31286 get_closure_var(ctx, s, fd,
31287 TRUE, i, vd->var_name, FALSE,
31288 vd->is_lexical, JS_VAR_NORMAL);
31289 }
31290 }
31291 for(i = 0; i < fd->var_count; i++) {
31292 vd = &fd->vars[i];
31293 /* do not close top level last result */
31294 if (vd->scope_level == 0 &&
31295 vd->var_name != JS_ATOM__ret_ &&
31296 vd->var_name != JS_ATOM_NULL) {
31297 get_closure_var(ctx, s, fd,
31298 FALSE, i, vd->var_name, FALSE,
31299 vd->is_lexical, JS_VAR_NORMAL);
31300 }
31301 }
31302 } else {
31303 for(i = 0; i < fd->var_count; i++) {
31304 vd = &fd->vars[i];
31305 /* do not close top level last result */
31306 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
31307 get_closure_var(ctx, s, fd,
31308 FALSE, i, vd->var_name, FALSE,
31309 vd->is_lexical, JS_VAR_NORMAL);
31310 }
31311 }
31312 }
31313 if (fd->is_eval) {
31314 int idx;
31315 /* add direct eval variables (we are necessarily at the
31316 top level) */
31317 for (idx = 0; idx < fd->closure_var_count; idx++) {
31318 JSClosureVar *cv = &fd->closure_var[idx];
31319 get_closure_var2(ctx, s, fd,
31320 FALSE, cv->is_arg,
31321 idx, cv->var_name, cv->is_const,
31322 cv->is_lexical, cv->var_kind);
31323 }
31324 }
31325 }
31326}
31327
31328static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
31329 JSVarDef *vd, int var_idx)
31330{
31331 cv->is_local = TRUE;
31332 cv->is_arg = FALSE;
31333 cv->is_const = vd->is_const;
31334 cv->is_lexical = vd->is_lexical;
31335 cv->var_kind = vd->var_kind;
31336 cv->var_idx = var_idx;
31337 cv->var_name = JS_DupAtom(ctx, vd->var_name);
31338}
31339
31340/* for direct eval compilation: add references to the variables of the
31341 calling function */
31342static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
31343 JSFunctionBytecode *b, int scope_idx)
31344{
31345 int i, count;
31346 JSVarDef *vd;
31347 BOOL is_arg_scope;
31348
31349 count = b->arg_count + b->var_count + b->closure_var_count;
31350 s->closure_var = NULL;
31351 s->closure_var_count = 0;
31352 s->closure_var_size = count;
31353 if (count == 0)
31354 return 0;
31355 s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
31356 if (!s->closure_var)
31357 return -1;
31358 /* Add lexical variables in scope at the point of evaluation */
31359 for (i = scope_idx; i >= 0;) {
31360 vd = &b->vardefs[b->arg_count + i];
31361 if (vd->scope_level > 0) {
31362 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
31363 set_closure_from_var(ctx, cv, vd, i);
31364 }
31365 i = vd->scope_next;
31366 }
31367 is_arg_scope = (i == ARG_SCOPE_END);
31368 if (!is_arg_scope) {
31369 /* Add argument variables */
31370 for(i = 0; i < b->arg_count; i++) {
31371 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
31372 vd = &b->vardefs[i];
31373 cv->is_local = TRUE;
31374 cv->is_arg = TRUE;
31375 cv->is_const = FALSE;
31376 cv->is_lexical = FALSE;
31377 cv->var_kind = JS_VAR_NORMAL;
31378 cv->var_idx = i;
31379 cv->var_name = JS_DupAtom(ctx, vd->var_name);
31380 }
31381 /* Add local non lexical variables */
31382 for(i = 0; i < b->var_count; i++) {
31383 vd = &b->vardefs[b->arg_count + i];
31384 if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
31385 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
31386 set_closure_from_var(ctx, cv, vd, i);
31387 }
31388 }
31389 } else {
31390 /* only add pseudo variables */
31391 for(i = 0; i < b->var_count; i++) {
31392 vd = &b->vardefs[b->arg_count + i];
31393 if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
31394 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
31395 set_closure_from_var(ctx, cv, vd, i);
31396 }
31397 }
31398 }
31399 for(i = 0; i < b->closure_var_count; i++) {
31400 JSClosureVar *cv0 = &b->closure_var[i];
31401 JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
31402 cv->is_local = FALSE;
31403 cv->is_arg = cv0->is_arg;
31404 cv->is_const = cv0->is_const;
31405 cv->is_lexical = cv0->is_lexical;
31406 cv->var_kind = cv0->var_kind;
31407 cv->var_idx = i;
31408 cv->var_name = JS_DupAtom(ctx, cv0->var_name);
31409 }
31410 return 0;
31411}
31412
31413typedef struct CodeContext {
31414 const uint8_t *bc_buf; /* code buffer */
31415 int bc_len; /* length of the code buffer */
31416 int pos; /* position past the matched code pattern */
31417 int line_num; /* last visited OP_line_num parameter or -1 */
31418 int op;
31419 int idx;
31420 int label;
31421 int val;
31422 JSAtom atom;
31423} CodeContext;
31424
31425#define M2(op1, op2) ((op1) | ((op2) << 8))
31426#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))
31427#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
31428
31429static BOOL code_match(CodeContext *s, int pos, ...)
31430{
31431 const uint8_t *tab = s->bc_buf;
31432 int op, len, op1, line_num, pos_next;
31433 va_list ap;
31434 BOOL ret = FALSE;
31435
31436 line_num = -1;
31437 va_start(ap, pos);
31438
31439 for(;;) {
31440 op1 = va_arg(ap, int);
31441 if (op1 == -1) {
31442 s->pos = pos;
31443 s->line_num = line_num;
31444 ret = TRUE;
31445 break;
31446 }
31447 for (;;) {
31448 if (pos >= s->bc_len)
31449 goto done;
31450 op = tab[pos];
31451 len = opcode_info[op].size;
31452 pos_next = pos + len;
31453 if (pos_next > s->bc_len)
31454 goto done;
31455 if (op == OP_line_num) {
31456 line_num = get_u32(tab + pos + 1);
31457 pos = pos_next;
31458 } else {
31459 break;
31460 }
31461 }
31462 if (op != op1) {
31463 if (op1 == (uint8_t)op1 || !op)
31464 break;
31465 if (op != (uint8_t)op1
31466 && op != (uint8_t)(op1 >> 8)
31467 && op != (uint8_t)(op1 >> 16)
31468 && op != (uint8_t)(op1 >> 24)) {
31469 break;
31470 }
31471 s->op = op;
31472 }
31473
31474 pos++;
31475 switch(opcode_info[op].fmt) {
31476 case OP_FMT_loc8:
31477 case OP_FMT_u8:
31478 {
31479 int idx = tab[pos];
31480 int arg = va_arg(ap, int);
31481 if (arg == -1) {
31482 s->idx = idx;
31483 } else {
31484 if (arg != idx)
31485 goto done;
31486 }
31487 break;
31488 }
31489 case OP_FMT_u16:
31490 case OP_FMT_npop:
31491 case OP_FMT_loc:
31492 case OP_FMT_arg:
31493 case OP_FMT_var_ref:
31494 {
31495 int idx = get_u16(tab + pos);
31496 int arg = va_arg(ap, int);
31497 if (arg == -1) {
31498 s->idx = idx;
31499 } else {
31500 if (arg != idx)
31501 goto done;
31502 }
31503 break;
31504 }
31505 case OP_FMT_i32:
31506 case OP_FMT_u32:
31507 case OP_FMT_label:
31508 case OP_FMT_const:
31509 {
31510 s->label = get_u32(tab + pos);
31511 break;
31512 }
31513 case OP_FMT_label_u16:
31514 {
31515 s->label = get_u32(tab + pos);
31516 s->val = get_u16(tab + pos + 4);
31517 break;
31518 }
31519 case OP_FMT_atom:
31520 {
31521 s->atom = get_u32(tab + pos);
31522 break;
31523 }
31524 case OP_FMT_atom_u8:
31525 {
31526 s->atom = get_u32(tab + pos);
31527 s->val = get_u8(tab + pos + 4);
31528 break;
31529 }
31530 case OP_FMT_atom_u16:
31531 {
31532 s->atom = get_u32(tab + pos);
31533 s->val = get_u16(tab + pos + 4);
31534 break;
31535 }
31536 case OP_FMT_atom_label_u8:
31537 {
31538 s->atom = get_u32(tab + pos);
31539 s->label = get_u32(tab + pos + 4);
31540 s->val = get_u8(tab + pos + 8);
31541 break;
31542 }
31543 default:
31544 break;
31545 }
31546 pos = pos_next;
31547 }
31548 done:
31549 va_end(ap);
31550 return ret;
31551}
31552
31553static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
31554{
31555 int i, idx, label_next = -1;
31556
31557 /* add the hoisted functions in arguments and local variables */
31558 for(i = 0; i < s->arg_count; i++) {
31559 JSVarDef *vd = &s->args[i];
31560 if (vd->func_pool_idx >= 0) {
31561 dbuf_putc(bc, OP_fclosure);
31562 dbuf_put_u32(bc, vd->func_pool_idx);
31563 dbuf_putc(bc, OP_put_arg);
31564 dbuf_put_u16(bc, i);
31565 }
31566 }
31567 for(i = 0; i < s->var_count; i++) {
31568 JSVarDef *vd = &s->vars[i];
31569 if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
31570 dbuf_putc(bc, OP_fclosure);
31571 dbuf_put_u32(bc, vd->func_pool_idx);
31572 dbuf_putc(bc, OP_put_loc);
31573 dbuf_put_u16(bc, i);
31574 }
31575 }
31576
31577 /* the module global variables must be initialized before
31578 evaluating the module so that the exported functions are
31579 visible if there are cyclic module references */
31580 if (s->module) {
31581 label_next = new_label_fd(s, -1);
31582
31583 /* if 'this' is true, initialize the global variables and return */
31584 dbuf_putc(bc, OP_push_this);
31585 dbuf_putc(bc, OP_if_false);
31586 dbuf_put_u32(bc, label_next);
31587 update_label(s, label_next, 1);
31588 s->jump_size++;
31589 }
31590
31591 /* add the global variables (only happens if s->is_global_var is
31592 true) */
31593 for(i = 0; i < s->global_var_count; i++) {
31594 JSGlobalVar *hf = &s->global_vars[i];
31595 int has_closure = 0;
31596 BOOL force_init = hf->force_init;
31597 /* we are in an eval, so the closure contains all the
31598 enclosing variables */
31599 /* If the outer function has a variable environment,
31600 create a property for the variable there */
31601 for(idx = 0; idx < s->closure_var_count; idx++) {
31602 JSClosureVar *cv = &s->closure_var[idx];
31603 if (cv->var_name == hf->var_name) {
31604 has_closure = 2;
31605 force_init = FALSE;
31606 break;
31607 }
31608 if (cv->var_name == JS_ATOM__var_ ||
31609 cv->var_name == JS_ATOM__arg_var_) {
31610 dbuf_putc(bc, OP_get_var_ref);
31611 dbuf_put_u16(bc, idx);
31612 has_closure = 1;
31613 force_init = TRUE;
31614 break;
31615 }
31616 }
31617 if (!has_closure) {
31618 int flags;
31619
31620 flags = 0;
31621 if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
31622 flags |= JS_PROP_CONFIGURABLE;
31623 if (hf->cpool_idx >= 0 && !hf->is_lexical) {
31624 /* global function definitions need a specific handling */
31625 dbuf_putc(bc, OP_fclosure);
31626 dbuf_put_u32(bc, hf->cpool_idx);
31627
31628 dbuf_putc(bc, OP_define_func);
31629 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
31630 dbuf_putc(bc, flags);
31631
31632 goto done_global_var;
31633 } else {
31634 if (hf->is_lexical) {
31635 flags |= DEFINE_GLOBAL_LEX_VAR;
31636 if (!hf->is_const)
31637 flags |= JS_PROP_WRITABLE;
31638 }
31639 dbuf_putc(bc, OP_define_var);
31640 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
31641 dbuf_putc(bc, flags);
31642 }
31643 }
31644 if (hf->cpool_idx >= 0 || force_init) {
31645 if (hf->cpool_idx >= 0) {
31646 dbuf_putc(bc, OP_fclosure);
31647 dbuf_put_u32(bc, hf->cpool_idx);
31648 if (hf->var_name == JS_ATOM__default_) {
31649 /* set default export function name */
31650 dbuf_putc(bc, OP_set_name);
31651 dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
31652 }
31653 } else {
31654 dbuf_putc(bc, OP_undefined);
31655 }
31656 if (has_closure == 2) {
31657 dbuf_putc(bc, OP_put_var_ref);
31658 dbuf_put_u16(bc, idx);
31659 } else if (has_closure == 1) {
31660 dbuf_putc(bc, OP_define_field);
31661 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
31662 dbuf_putc(bc, OP_drop);
31663 } else {
31664 /* XXX: Check if variable is writable and enumerable */
31665 dbuf_putc(bc, OP_put_var);
31666 dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
31667 }
31668 }
31669 done_global_var:
31670 JS_FreeAtom(ctx, hf->var_name);
31671 }
31672
31673 if (s->module) {
31674 dbuf_putc(bc, OP_return_undef);
31675
31676 dbuf_putc(bc, OP_label);
31677 dbuf_put_u32(bc, label_next);
31678 s->label_slots[label_next].pos2 = bc->size;
31679 }
31680
31681 js_free(ctx, s->global_vars);
31682 s->global_vars = NULL;
31683 s->global_var_count = 0;
31684 s->global_var_size = 0;
31685}
31686
31687static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
31688 int pos, int *linep)
31689{
31690 int op, len, label;
31691
31692 for (; pos < bc_len; pos += len) {
31693 op = bc_buf[pos];
31694 len = opcode_info[op].size;
31695 if (op == OP_line_num) {
31696 *linep = get_u32(bc_buf + pos + 1);
31697 } else
31698 if (op == OP_label) {
31699 label = get_u32(bc_buf + pos + 1);
31700 if (update_label(s, label, 0) > 0)
31701 break;
31702#if 0
31703 if (s->label_slots[label].first_reloc) {
31704 printf("line %d: unreferenced label %d:%d has relocations\n",
31705 *linep, label, s->label_slots[label].pos2);
31706 }
31707#endif
31708 assert(s->label_slots[label].first_reloc == NULL);
31709 } else {
31710 /* XXX: output a warning for unreachable code? */
31711 JSAtom atom;
31712 switch(opcode_info[op].fmt) {
31713 case OP_FMT_label:
31714 case OP_FMT_label_u16:
31715 label = get_u32(bc_buf + pos + 1);
31716 update_label(s, label, -1);
31717 break;
31718 case OP_FMT_atom_label_u8:
31719 case OP_FMT_atom_label_u16:
31720 label = get_u32(bc_buf + pos + 5);
31721 update_label(s, label, -1);
31722 /* fall thru */
31723 case OP_FMT_atom:
31724 case OP_FMT_atom_u8:
31725 case OP_FMT_atom_u16:
31726 atom = get_u32(bc_buf + pos + 1);
31727 JS_FreeAtom(s->ctx, atom);
31728 break;
31729 default:
31730 break;
31731 }
31732 }
31733 }
31734 return pos;
31735}
31736
31737static int get_label_pos(JSFunctionDef *s, int label)
31738{
31739 int i, pos;
31740 for (i = 0; i < 20; i++) {
31741 pos = s->label_slots[label].pos;
31742 for (;;) {
31743 switch (s->byte_code.buf[pos]) {
31744 case OP_line_num:
31745 case OP_label:
31746 pos += 5;
31747 continue;
31748 case OP_goto:
31749 label = get_u32(s->byte_code.buf + pos + 1);
31750 break;
31751 default:
31752 return pos;
31753 }
31754 break;
31755 }
31756 }
31757 return pos;
31758}
31759
31760/* convert global variable accesses to local variables or closure
31761 variables when necessary */
31762static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
31763{
31764 int pos, pos_next, bc_len, op, len, i, idx, line_num;
31765 uint8_t *bc_buf;
31766 JSAtom var_name;
31767 DynBuf bc_out;
31768 CodeContext cc;
31769 int scope;
31770
31771 cc.bc_buf = bc_buf = s->byte_code.buf;
31772 cc.bc_len = bc_len = s->byte_code.size;
31773 js_dbuf_init(ctx, &bc_out);
31774
31775 /* first pass for runtime checks (must be done before the
31776 variables are created) */
31777 for(i = 0; i < s->global_var_count; i++) {
31778 JSGlobalVar *hf = &s->global_vars[i];
31779 int flags;
31780
31781 /* check if global variable (XXX: simplify) */
31782 for(idx = 0; idx < s->closure_var_count; idx++) {
31783 JSClosureVar *cv = &s->closure_var[idx];
31784 if (cv->var_name == hf->var_name) {
31785 if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
31786 cv->is_lexical) {
31787 /* Check if a lexical variable is
31788 redefined as 'var'. XXX: Could abort
31789 compilation here, but for consistency
31790 with the other checks, we delay the
31791 error generation. */
31792 dbuf_putc(&bc_out, OP_throw_error);
31793 dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
31794 dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
31795 }
31796 goto next;
31797 }
31798 if (cv->var_name == JS_ATOM__var_ ||
31799 cv->var_name == JS_ATOM__arg_var_)
31800 goto next;
31801 }
31802
31803 dbuf_putc(&bc_out, OP_check_define_var);
31804 dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
31805 flags = 0;
31806 if (hf->is_lexical)
31807 flags |= DEFINE_GLOBAL_LEX_VAR;
31808 if (hf->cpool_idx >= 0)
31809 flags |= DEFINE_GLOBAL_FUNC_VAR;
31810 dbuf_putc(&bc_out, flags);
31811 next: ;
31812 }
31813
31814 line_num = 0; /* avoid warning */
31815 for (pos = 0; pos < bc_len; pos = pos_next) {
31816 op = bc_buf[pos];
31817 len = opcode_info[op].size;
31818 pos_next = pos + len;
31819 switch(op) {
31820 case OP_line_num:
31821 line_num = get_u32(bc_buf + pos + 1);
31822 s->line_number_size++;
31823 goto no_change;
31824
31825 case OP_eval: /* convert scope index to adjusted variable index */
31826 {
31827 int call_argc = get_u16(bc_buf + pos + 1);
31828 scope = get_u16(bc_buf + pos + 1 + 2);
31829 mark_eval_captured_variables(ctx, s, scope);
31830 dbuf_putc(&bc_out, op);
31831 dbuf_put_u16(&bc_out, call_argc);
31832 dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
31833 }
31834 break;
31835 case OP_apply_eval: /* convert scope index to adjusted variable index */
31836 scope = get_u16(bc_buf + pos + 1);
31837 mark_eval_captured_variables(ctx, s, scope);
31838 dbuf_putc(&bc_out, op);
31839 dbuf_put_u16(&bc_out, s->scopes[scope].first - ARG_SCOPE_END);
31840 break;
31841 case OP_scope_get_var_checkthis:
31842 case OP_scope_get_var_undef:
31843 case OP_scope_get_var:
31844 case OP_scope_put_var:
31845 case OP_scope_delete_var:
31846 case OP_scope_get_ref:
31847 case OP_scope_put_var_init:
31848 var_name = get_u32(bc_buf + pos + 1);
31849 scope = get_u16(bc_buf + pos + 5);
31850 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
31851 NULL, NULL, pos_next);
31852 JS_FreeAtom(ctx, var_name);
31853 break;
31854 case OP_scope_make_ref:
31855 {
31856 int label;
31857 LabelSlot *ls;
31858 var_name = get_u32(bc_buf + pos + 1);
31859 label = get_u32(bc_buf + pos + 5);
31860 scope = get_u16(bc_buf + pos + 9);
31861 ls = &s->label_slots[label];
31862 ls->ref_count--; /* always remove label reference */
31863 pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
31864 bc_buf, ls, pos_next);
31865 JS_FreeAtom(ctx, var_name);
31866 }
31867 break;
31868 case OP_scope_get_private_field:
31869 case OP_scope_get_private_field2:
31870 case OP_scope_put_private_field:
31871 case OP_scope_in_private_field:
31872 {
31873 int ret;
31874 var_name = get_u32(bc_buf + pos + 1);
31875 scope = get_u16(bc_buf + pos + 5);
31876 ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
31877 if (ret < 0)
31878 goto fail;
31879 JS_FreeAtom(ctx, var_name);
31880 }
31881 break;
31882 case OP_gosub:
31883 s->jump_size++;
31884 if (OPTIMIZE) {
31885 /* remove calls to empty finalizers */
31886 int label;
31887 LabelSlot *ls;
31888
31889 label = get_u32(bc_buf + pos + 1);
31890 assert(label >= 0 && label < s->label_count);
31891 ls = &s->label_slots[label];
31892 if (code_match(&cc, ls->pos, OP_ret, -1)) {
31893 ls->ref_count--;
31894 break;
31895 }
31896 }
31897 goto no_change;
31898 case OP_drop:
31899 if (0) {
31900 /* remove drops before return_undef */
31901 /* do not perform this optimization in pass2 because
31902 it breaks patterns recognised in resolve_labels */
31903 int pos1 = pos_next;
31904 int line1 = line_num;
31905 while (code_match(&cc, pos1, OP_drop, -1)) {
31906 if (cc.line_num >= 0) line1 = cc.line_num;
31907 pos1 = cc.pos;
31908 }
31909 if (code_match(&cc, pos1, OP_return_undef, -1)) {
31910 pos_next = pos1;
31911 if (line1 != -1 && line1 != line_num) {
31912 line_num = line1;
31913 s->line_number_size++;
31914 dbuf_putc(&bc_out, OP_line_num);
31915 dbuf_put_u32(&bc_out, line_num);
31916 }
31917 break;
31918 }
31919 }
31920 goto no_change;
31921 case OP_insert3:
31922 if (OPTIMIZE) {
31923 /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
31924 if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
31925 dbuf_putc(&bc_out, cc.op);
31926 pos_next = cc.pos;
31927 if (cc.line_num != -1 && cc.line_num != line_num) {
31928 line_num = cc.line_num;
31929 s->line_number_size++;
31930 dbuf_putc(&bc_out, OP_line_num);
31931 dbuf_put_u32(&bc_out, line_num);
31932 }
31933 break;
31934 }
31935 }
31936 goto no_change;
31937
31938 case OP_goto:
31939 s->jump_size++;
31940 /* fall thru */
31941 case OP_tail_call:
31942 case OP_tail_call_method:
31943 case OP_return:
31944 case OP_return_undef:
31945 case OP_throw:
31946 case OP_throw_error:
31947 case OP_ret:
31948 if (OPTIMIZE) {
31949 /* remove dead code */
31950 int line = -1;
31951 dbuf_put(&bc_out, bc_buf + pos, len);
31952 pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
31953 pos_next = pos;
31954 if (pos < bc_len && line >= 0 && line_num != line) {
31955 line_num = line;
31956 s->line_number_size++;
31957 dbuf_putc(&bc_out, OP_line_num);
31958 dbuf_put_u32(&bc_out, line_num);
31959 }
31960 break;
31961 }
31962 goto no_change;
31963
31964 case OP_label:
31965 {
31966 int label;
31967 LabelSlot *ls;
31968
31969 label = get_u32(bc_buf + pos + 1);
31970 assert(label >= 0 && label < s->label_count);
31971 ls = &s->label_slots[label];
31972 ls->pos2 = bc_out.size + opcode_info[op].size;
31973 }
31974 goto no_change;
31975
31976 case OP_enter_scope:
31977 {
31978 int scope_idx, scope = get_u16(bc_buf + pos + 1);
31979
31980 if (scope == s->body_scope) {
31981 instantiate_hoisted_definitions(ctx, s, &bc_out);
31982 }
31983
31984 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
31985 JSVarDef *vd = &s->vars[scope_idx];
31986 if (vd->scope_level == scope) {
31987 if (scope_idx != s->arguments_arg_idx) {
31988 if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
31989 vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
31990 /* Initialize lexical variable upon entering scope */
31991 dbuf_putc(&bc_out, OP_fclosure);
31992 dbuf_put_u32(&bc_out, vd->func_pool_idx);
31993 dbuf_putc(&bc_out, OP_put_loc);
31994 dbuf_put_u16(&bc_out, scope_idx);
31995 } else {
31996 /* XXX: should check if variable can be used
31997 before initialization */
31998 dbuf_putc(&bc_out, OP_set_loc_uninitialized);
31999 dbuf_put_u16(&bc_out, scope_idx);
32000 }
32001 }
32002 scope_idx = vd->scope_next;
32003 } else {
32004 break;
32005 }
32006 }
32007 }
32008 break;
32009
32010 case OP_leave_scope:
32011 {
32012 int scope_idx, scope = get_u16(bc_buf + pos + 1);
32013
32014 for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
32015 JSVarDef *vd = &s->vars[scope_idx];
32016 if (vd->scope_level == scope) {
32017 if (vd->is_captured) {
32018 dbuf_putc(&bc_out, OP_close_loc);
32019 dbuf_put_u16(&bc_out, scope_idx);
32020 }
32021 scope_idx = vd->scope_next;
32022 } else {
32023 break;
32024 }
32025 }
32026 }
32027 break;
32028
32029 case OP_set_name:
32030 {
32031 /* remove dummy set_name opcodes */
32032 JSAtom name = get_u32(bc_buf + pos + 1);
32033 if (name == JS_ATOM_NULL)
32034 break;
32035 }
32036 goto no_change;
32037
32038 case OP_if_false:
32039 case OP_if_true:
32040 case OP_catch:
32041 s->jump_size++;
32042 goto no_change;
32043
32044 case OP_dup:
32045 if (OPTIMIZE) {
32046 /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
32047 /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
32048 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
32049 int lab0, lab1, op1, pos1, line1, pos2;
32050 lab0 = lab1 = cc.label;
32051 assert(lab1 >= 0 && lab1 < s->label_count);
32052 op1 = cc.op;
32053 pos1 = cc.pos;
32054 line1 = cc.line_num;
32055 while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
32056 lab1 = cc.label;
32057 }
32058 if (code_match(&cc, pos2, op1, -1)) {
32059 s->jump_size++;
32060 update_label(s, lab0, -1);
32061 update_label(s, cc.label, +1);
32062 dbuf_putc(&bc_out, op1);
32063 dbuf_put_u32(&bc_out, cc.label);
32064 pos_next = pos1;
32065 if (line1 != -1 && line1 != line_num) {
32066 line_num = line1;
32067 s->line_number_size++;
32068 dbuf_putc(&bc_out, OP_line_num);
32069 dbuf_put_u32(&bc_out, line_num);
32070 }
32071 break;
32072 }
32073 }
32074 }
32075 goto no_change;
32076
32077 case OP_nop:
32078 /* remove erased code */
32079 break;
32080 case OP_set_class_name:
32081 /* only used during parsing */
32082 break;
32083
32084 case OP_get_field_opt_chain: /* equivalent to OP_get_field */
32085 {
32086 JSAtom name = get_u32(bc_buf + pos + 1);
32087 dbuf_putc(&bc_out, OP_get_field);
32088 dbuf_put_u32(&bc_out, name);
32089 }
32090 break;
32091 case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */
32092 dbuf_putc(&bc_out, OP_get_array_el);
32093 break;
32094
32095 default:
32096 no_change:
32097 dbuf_put(&bc_out, bc_buf + pos, len);
32098 break;
32099 }
32100 }
32101
32102 /* set the new byte code */
32103 dbuf_free(&s->byte_code);
32104 s->byte_code = bc_out;
32105 if (dbuf_error(&s->byte_code)) {
32106 JS_ThrowOutOfMemory(ctx);
32107 return -1;
32108 }
32109 return 0;
32110 fail:
32111 /* continue the copy to keep the atom refcounts consistent */
32112 /* XXX: find a better solution ? */
32113 for (; pos < bc_len; pos = pos_next) {
32114 op = bc_buf[pos];
32115 len = opcode_info[op].size;
32116 pos_next = pos + len;
32117 dbuf_put(&bc_out, bc_buf + pos, len);
32118 }
32119 dbuf_free(&s->byte_code);
32120 s->byte_code = bc_out;
32121 return -1;
32122}
32123
32124/* the pc2line table gives a source position for each PC value */
32125static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, uint32_t source_pos)
32126{
32127 if (s->line_number_slots != NULL
32128 && s->line_number_count < s->line_number_size
32129 && pc >= s->line_number_last_pc
32130 && source_pos != s->line_number_last) {
32131 s->line_number_slots[s->line_number_count].pc = pc;
32132 s->line_number_slots[s->line_number_count].source_pos = source_pos;
32133 s->line_number_count++;
32134 s->line_number_last_pc = pc;
32135 s->line_number_last = source_pos;
32136 }
32137}
32138
32139/* XXX: could use a more compact storage */
32140/* XXX: get_line_col_cached() is slow. For more predictable
32141 performance, line/cols could be stored every N source
32142 bytes. Alternatively, get_line_col_cached() could be issued in
32143 emit_source_pos() so that the deltas are more likely to be
32144 small. */
32145static void compute_pc2line_info(JSFunctionDef *s)
32146{
32147 if (!s->strip_debug) {
32148 int last_line_num, last_col_num;
32149 uint32_t last_pc = 0;
32150 int i, line_num, col_num;
32151 const uint8_t *buf_start = s->get_line_col_cache->buf_start;
32152 js_dbuf_init(s->ctx, &s->pc2line);
32153
32154 last_line_num = get_line_col_cached(s->get_line_col_cache,
32155 &last_col_num,
32156 buf_start + s->source_pos);
32157 dbuf_put_leb128(&s->pc2line, last_line_num); /* line number minus 1 */
32158 dbuf_put_leb128(&s->pc2line, last_col_num); /* column number minus 1 */
32159
32160 for (i = 0; i < s->line_number_count; i++) {
32161 uint32_t pc = s->line_number_slots[i].pc;
32162 uint32_t source_pos = s->line_number_slots[i].source_pos;
32163 int diff_pc, diff_line, diff_col;
32164
32165 if (source_pos == -1)
32166 continue;
32167 diff_pc = pc - last_pc;
32168 if (diff_pc < 0)
32169 continue;
32170
32171 line_num = get_line_col_cached(s->get_line_col_cache, &col_num,
32172 buf_start + source_pos);
32173 diff_line = line_num - last_line_num;
32174 diff_col = col_num - last_col_num;
32175 if (diff_line == 0 && diff_col == 0)
32176 continue;
32177
32178 if (diff_line >= PC2LINE_BASE &&
32179 diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
32180 diff_pc <= PC2LINE_DIFF_PC_MAX) {
32181 dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
32182 diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
32183 } else {
32184 /* longer encoding */
32185 dbuf_putc(&s->pc2line, 0);
32186 dbuf_put_leb128(&s->pc2line, diff_pc);
32187 dbuf_put_sleb128(&s->pc2line, diff_line);
32188 }
32189 dbuf_put_sleb128(&s->pc2line, diff_col);
32190
32191 last_pc = pc;
32192 last_line_num = line_num;
32193 last_col_num = col_num;
32194 }
32195 }
32196}
32197
32198static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
32199{
32200 RelocEntry *re;
32201 re = js_malloc(ctx, sizeof(*re));
32202 if (!re)
32203 return NULL;
32204 re->addr = addr;
32205 re->size = size;
32206 re->next = ls->first_reloc;
32207 ls->first_reloc = re;
32208 return re;
32209}
32210
32211static BOOL code_has_label(CodeContext *s, int pos, int label)
32212{
32213 while (pos < s->bc_len) {
32214 int op = s->bc_buf[pos];
32215 if (op == OP_line_num) {
32216 pos += 5;
32217 continue;
32218 }
32219 if (op == OP_label) {
32220 int lab = get_u32(s->bc_buf + pos + 1);
32221 if (lab == label)
32222 return TRUE;
32223 pos += 5;
32224 continue;
32225 }
32226 if (op == OP_goto) {
32227 int lab = get_u32(s->bc_buf + pos + 1);
32228 if (lab == label)
32229 return TRUE;
32230 }
32231 break;
32232 }
32233 return FALSE;
32234}
32235
32236/* return the target label, following the OP_goto jumps
32237 the first opcode at destination is stored in *pop
32238 */
32239static int find_jump_target(JSFunctionDef *s, int label0, int *pop, int *pline)
32240{
32241 int i, pos, op, label;
32242
32243 label = label0;
32244 update_label(s, label, -1);
32245 for (i = 0; i < 10; i++) {
32246 assert(label >= 0 && label < s->label_count);
32247 pos = s->label_slots[label].pos2;
32248 for (;;) {
32249 switch(op = s->byte_code.buf[pos]) {
32250 case OP_line_num:
32251 if (pline)
32252 *pline = get_u32(s->byte_code.buf + pos + 1);
32253 /* fall thru */
32254 case OP_label:
32255 pos += opcode_info[op].size;
32256 continue;
32257 case OP_goto:
32258 label = get_u32(s->byte_code.buf + pos + 1);
32259 break;
32260 case OP_drop:
32261 /* ignore drop opcodes if followed by OP_return_undef */
32262 while (s->byte_code.buf[++pos] == OP_drop)
32263 continue;
32264 if (s->byte_code.buf[pos] == OP_return_undef)
32265 op = OP_return_undef;
32266 /* fall thru */
32267 default:
32268 goto done;
32269 }
32270 break;
32271 }
32272 }
32273 /* cycle detected, could issue a warning */
32274 /* XXX: the combination of find_jump_target() and skip_dead_code()
32275 seems incorrect with cyclic labels. See for exemple:
32276
32277 for (;;) {
32278 l:break l;
32279 l:break l;
32280 l:break l;
32281 l:break l;
32282 }
32283
32284 Avoiding changing the target is just a workaround and might not
32285 suffice to completely fix the problem. */
32286 label = label0;
32287 done:
32288 *pop = op;
32289 update_label(s, label, +1);
32290 return label;
32291}
32292
32293static void push_short_int(DynBuf *bc_out, int val)
32294{
32295#if SHORT_OPCODES
32296 if (val >= -1 && val <= 7) {
32297 dbuf_putc(bc_out, OP_push_0 + val);
32298 return;
32299 }
32300 if (val == (int8_t)val) {
32301 dbuf_putc(bc_out, OP_push_i8);
32302 dbuf_putc(bc_out, val);
32303 return;
32304 }
32305 if (val == (int16_t)val) {
32306 dbuf_putc(bc_out, OP_push_i16);
32307 dbuf_put_u16(bc_out, val);
32308 return;
32309 }
32310#endif
32311 dbuf_putc(bc_out, OP_push_i32);
32312 dbuf_put_u32(bc_out, val);
32313}
32314
32315static void put_short_code(DynBuf *bc_out, int op, int idx)
32316{
32317#if SHORT_OPCODES
32318 if (idx < 4) {
32319 switch (op) {
32320 case OP_get_loc:
32321 dbuf_putc(bc_out, OP_get_loc0 + idx);
32322 return;
32323 case OP_put_loc:
32324 dbuf_putc(bc_out, OP_put_loc0 + idx);
32325 return;
32326 case OP_set_loc:
32327 dbuf_putc(bc_out, OP_set_loc0 + idx);
32328 return;
32329 case OP_get_arg:
32330 dbuf_putc(bc_out, OP_get_arg0 + idx);
32331 return;
32332 case OP_put_arg:
32333 dbuf_putc(bc_out, OP_put_arg0 + idx);
32334 return;
32335 case OP_set_arg:
32336 dbuf_putc(bc_out, OP_set_arg0 + idx);
32337 return;
32338 case OP_get_var_ref:
32339 dbuf_putc(bc_out, OP_get_var_ref0 + idx);
32340 return;
32341 case OP_put_var_ref:
32342 dbuf_putc(bc_out, OP_put_var_ref0 + idx);
32343 return;
32344 case OP_set_var_ref:
32345 dbuf_putc(bc_out, OP_set_var_ref0 + idx);
32346 return;
32347 case OP_call:
32348 dbuf_putc(bc_out, OP_call0 + idx);
32349 return;
32350 }
32351 }
32352 if (idx < 256) {
32353 switch (op) {
32354 case OP_get_loc:
32355 dbuf_putc(bc_out, OP_get_loc8);
32356 dbuf_putc(bc_out, idx);
32357 return;
32358 case OP_put_loc:
32359 dbuf_putc(bc_out, OP_put_loc8);
32360 dbuf_putc(bc_out, idx);
32361 return;
32362 case OP_set_loc:
32363 dbuf_putc(bc_out, OP_set_loc8);
32364 dbuf_putc(bc_out, idx);
32365 return;
32366 }
32367 }
32368#endif
32369 dbuf_putc(bc_out, op);
32370 dbuf_put_u16(bc_out, idx);
32371}
32372
32373/* peephole optimizations and resolve goto/labels */
32374static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
32375{
32376 int pos, pos_next, bc_len, op, op1, len, i, line_num;
32377 const uint8_t *bc_buf;
32378 DynBuf bc_out;
32379 LabelSlot *label_slots, *ls;
32380 RelocEntry *re, *re_next;
32381 CodeContext cc;
32382 int label;
32383#if SHORT_OPCODES
32384 JumpSlot *jp;
32385#endif
32386
32387 label_slots = s->label_slots;
32388
32389 line_num = s->source_pos;
32390
32391 cc.bc_buf = bc_buf = s->byte_code.buf;
32392 cc.bc_len = bc_len = s->byte_code.size;
32393 js_dbuf_init(ctx, &bc_out);
32394
32395#if SHORT_OPCODES
32396 if (s->jump_size) {
32397 s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
32398 if (s->jump_slots == NULL)
32399 return -1;
32400 }
32401#endif
32402 /* XXX: Should skip this phase if not generating SHORT_OPCODES */
32403 if (s->line_number_size && !s->strip_debug) {
32404 s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
32405 if (s->line_number_slots == NULL)
32406 return -1;
32407 s->line_number_last = s->source_pos;
32408 s->line_number_last_pc = 0;
32409 }
32410
32411 /* initialize the 'home_object' variable if needed */
32412 if (s->home_object_var_idx >= 0) {
32413 dbuf_putc(&bc_out, OP_special_object);
32414 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
32415 put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
32416 }
32417 /* initialize the 'this.active_func' variable if needed */
32418 if (s->this_active_func_var_idx >= 0) {
32419 dbuf_putc(&bc_out, OP_special_object);
32420 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
32421 put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
32422 }
32423 /* initialize the 'new.target' variable if needed */
32424 if (s->new_target_var_idx >= 0) {
32425 dbuf_putc(&bc_out, OP_special_object);
32426 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
32427 put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
32428 }
32429 /* initialize the 'this' variable if needed. In a derived class
32430 constructor, this is initially uninitialized. */
32431 if (s->this_var_idx >= 0) {
32432 if (s->is_derived_class_constructor) {
32433 dbuf_putc(&bc_out, OP_set_loc_uninitialized);
32434 dbuf_put_u16(&bc_out, s->this_var_idx);
32435 } else {
32436 dbuf_putc(&bc_out, OP_push_this);
32437 put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
32438 }
32439 }
32440 /* initialize the 'arguments' variable if needed */
32441 if (s->arguments_var_idx >= 0) {
32442 if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
32443 dbuf_putc(&bc_out, OP_special_object);
32444 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
32445 } else {
32446 dbuf_putc(&bc_out, OP_special_object);
32447 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
32448 }
32449 if (s->arguments_arg_idx >= 0)
32450 put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
32451 put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
32452 }
32453 /* initialize a reference to the current function if needed */
32454 if (s->func_var_idx >= 0) {
32455 dbuf_putc(&bc_out, OP_special_object);
32456 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
32457 put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
32458 }
32459 /* initialize the variable environment object if needed */
32460 if (s->var_object_idx >= 0) {
32461 dbuf_putc(&bc_out, OP_special_object);
32462 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
32463 put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
32464 }
32465 if (s->arg_var_object_idx >= 0) {
32466 dbuf_putc(&bc_out, OP_special_object);
32467 dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
32468 put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
32469 }
32470
32471 for (pos = 0; pos < bc_len; pos = pos_next) {
32472 int val;
32473 op = bc_buf[pos];
32474 len = opcode_info[op].size;
32475 pos_next = pos + len;
32476 switch(op) {
32477 case OP_line_num:
32478 /* line number info (for debug). We put it in a separate
32479 compressed table to reduce memory usage and get better
32480 performance */
32481 line_num = get_u32(bc_buf + pos + 1);
32482 break;
32483
32484 case OP_label:
32485 {
32486 label = get_u32(bc_buf + pos + 1);
32487 assert(label >= 0 && label < s->label_count);
32488 ls = &label_slots[label];
32489 assert(ls->addr == -1);
32490 ls->addr = bc_out.size;
32491 /* resolve the relocation entries */
32492 for(re = ls->first_reloc; re != NULL; re = re_next) {
32493 int diff = ls->addr - re->addr;
32494 re_next = re->next;
32495 switch (re->size) {
32496 case 4:
32497 put_u32(bc_out.buf + re->addr, diff);
32498 break;
32499 case 2:
32500 assert(diff == (int16_t)diff);
32501 put_u16(bc_out.buf + re->addr, diff);
32502 break;
32503 case 1:
32504 assert(diff == (int8_t)diff);
32505 put_u8(bc_out.buf + re->addr, diff);
32506 break;
32507 }
32508 js_free(ctx, re);
32509 }
32510 ls->first_reloc = NULL;
32511 }
32512 break;
32513
32514 case OP_call:
32515 case OP_call_method:
32516 {
32517 /* detect and transform tail calls */
32518 int argc;
32519 argc = get_u16(bc_buf + pos + 1);
32520 if (code_match(&cc, pos_next, OP_return, -1)) {
32521 if (cc.line_num >= 0) line_num = cc.line_num;
32522 add_pc2line_info(s, bc_out.size, line_num);
32523 put_short_code(&bc_out, op + 1, argc);
32524 pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
32525 break;
32526 }
32527 add_pc2line_info(s, bc_out.size, line_num);
32528 put_short_code(&bc_out, op, argc);
32529 break;
32530 }
32531 goto no_change;
32532
32533 case OP_return:
32534 case OP_return_undef:
32535 case OP_return_async:
32536 case OP_throw:
32537 case OP_throw_error:
32538 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
32539 goto no_change;
32540
32541 case OP_goto:
32542 label = get_u32(bc_buf + pos + 1);
32543 has_goto:
32544 if (OPTIMIZE) {
32545 int line1 = -1;
32546 /* Use custom matcher because multiple labels can follow */
32547 label = find_jump_target(s, label, &op1, &line1);
32548 if (code_has_label(&cc, pos_next, label)) {
32549 /* jump to next instruction: remove jump */
32550 update_label(s, label, -1);
32551 break;
32552 }
32553 if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
32554 /* jump to return/throw: remove jump, append return/throw */
32555 /* updating the line number obfuscates assembly listing */
32556 //if (line1 != -1) line_num = line1;
32557 update_label(s, label, -1);
32558 add_pc2line_info(s, bc_out.size, line_num);
32559 dbuf_putc(&bc_out, op1);
32560 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
32561 break;
32562 }
32563 /* XXX: should duplicate single instructions followed by goto or return */
32564 /* For example, can match one of these followed by return:
32565 push_i32 / push_const / push_atom_value / get_var /
32566 undefined / null / push_false / push_true / get_ref_value /
32567 get_loc / get_arg / get_var_ref
32568 */
32569 }
32570 goto has_label;
32571
32572 case OP_gosub:
32573 label = get_u32(bc_buf + pos + 1);
32574 if (0 && OPTIMIZE) {
32575 label = find_jump_target(s, label, &op1, NULL);
32576 if (op1 == OP_ret) {
32577 update_label(s, label, -1);
32578 /* empty finally clause: remove gosub */
32579 break;
32580 }
32581 }
32582 goto has_label;
32583
32584 case OP_catch:
32585 label = get_u32(bc_buf + pos + 1);
32586 goto has_label;
32587
32588 case OP_if_true:
32589 case OP_if_false:
32590 label = get_u32(bc_buf + pos + 1);
32591 if (OPTIMIZE) {
32592 label = find_jump_target(s, label, &op1, NULL);
32593 /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
32594 if (code_has_label(&cc, pos_next, label)) {
32595 update_label(s, label, -1);
32596 dbuf_putc(&bc_out, OP_drop);
32597 break;
32598 }
32599 /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
32600 if (code_match(&cc, pos_next, OP_goto, -1)) {
32601 int pos1 = cc.pos;
32602 int line1 = cc.line_num;
32603 if (code_has_label(&cc, pos1, label)) {
32604 if (line1 != -1) line_num = line1;
32605 pos_next = pos1;
32606 update_label(s, label, -1);
32607 label = cc.label;
32608 op ^= OP_if_true ^ OP_if_false;
32609 }
32610 }
32611 }
32612 has_label:
32613 add_pc2line_info(s, bc_out.size, line_num);
32614 if (op == OP_goto) {
32615 pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
32616 }
32617 assert(label >= 0 && label < s->label_count);
32618 ls = &label_slots[label];
32619#if SHORT_OPCODES
32620 jp = &s->jump_slots[s->jump_count++];
32621 jp->op = op;
32622 jp->size = 4;
32623 jp->pos = bc_out.size + 1;
32624 jp->label = label;
32625
32626 if (ls->addr == -1) {
32627 int diff = ls->pos2 - pos - 1;
32628 if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
32629 jp->size = 1;
32630 jp->op = OP_if_false8 + (op - OP_if_false);
32631 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
32632 dbuf_putc(&bc_out, 0);
32633 if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
32634 goto fail;
32635 break;
32636 }
32637 if (diff < 32768 && op == OP_goto) {
32638 jp->size = 2;
32639 jp->op = OP_goto16;
32640 dbuf_putc(&bc_out, OP_goto16);
32641 dbuf_put_u16(&bc_out, 0);
32642 if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
32643 goto fail;
32644 break;
32645 }
32646 } else {
32647 int diff = ls->addr - bc_out.size - 1;
32648 if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
32649 jp->size = 1;
32650 jp->op = OP_if_false8 + (op - OP_if_false);
32651 dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
32652 dbuf_putc(&bc_out, diff);
32653 break;
32654 }
32655 if (diff == (int16_t)diff && op == OP_goto) {
32656 jp->size = 2;
32657 jp->op = OP_goto16;
32658 dbuf_putc(&bc_out, OP_goto16);
32659 dbuf_put_u16(&bc_out, diff);
32660 break;
32661 }
32662 }
32663#endif
32664 dbuf_putc(&bc_out, op);
32665 dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
32666 if (ls->addr == -1) {
32667 /* unresolved yet: create a new relocation entry */
32668 if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
32669 goto fail;
32670 }
32671 break;
32672 case OP_with_get_var:
32673 case OP_with_put_var:
32674 case OP_with_delete_var:
32675 case OP_with_make_ref:
32676 case OP_with_get_ref:
32677 {
32678 JSAtom atom;
32679 int is_with;
32680
32681 atom = get_u32(bc_buf + pos + 1);
32682 label = get_u32(bc_buf + pos + 5);
32683 is_with = bc_buf[pos + 9];
32684 if (OPTIMIZE) {
32685 label = find_jump_target(s, label, &op1, NULL);
32686 }
32687 assert(label >= 0 && label < s->label_count);
32688 ls = &label_slots[label];
32689 add_pc2line_info(s, bc_out.size, line_num);
32690#if SHORT_OPCODES
32691 jp = &s->jump_slots[s->jump_count++];
32692 jp->op = op;
32693 jp->size = 4;
32694 jp->pos = bc_out.size + 5;
32695 jp->label = label;
32696#endif
32697 dbuf_putc(&bc_out, op);
32698 dbuf_put_u32(&bc_out, atom);
32699 dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
32700 if (ls->addr == -1) {
32701 /* unresolved yet: create a new relocation entry */
32702 if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
32703 goto fail;
32704 }
32705 dbuf_putc(&bc_out, is_with);
32706 }
32707 break;
32708
32709 case OP_drop:
32710 if (OPTIMIZE) {
32711 /* remove useless drops before return */
32712 if (code_match(&cc, pos_next, OP_return_undef, -1)) {
32713 if (cc.line_num >= 0) line_num = cc.line_num;
32714 break;
32715 }
32716 }
32717 goto no_change;
32718
32719 case OP_null:
32720#if SHORT_OPCODES
32721 if (OPTIMIZE) {
32722 /* transform null strict_eq into is_null */
32723 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
32724 if (cc.line_num >= 0) line_num = cc.line_num;
32725 add_pc2line_info(s, bc_out.size, line_num);
32726 dbuf_putc(&bc_out, OP_is_null);
32727 pos_next = cc.pos;
32728 break;
32729 }
32730 /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
32731 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
32732 if (cc.line_num >= 0) line_num = cc.line_num;
32733 add_pc2line_info(s, bc_out.size, line_num);
32734 dbuf_putc(&bc_out, OP_is_null);
32735 pos_next = cc.pos;
32736 label = cc.label;
32737 op = cc.op ^ OP_if_false ^ OP_if_true;
32738 goto has_label;
32739 }
32740 }
32741#endif
32742 /* fall thru */
32743 case OP_push_false:
32744 case OP_push_true:
32745 if (OPTIMIZE) {
32746 val = (op == OP_push_true);
32747 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
32748 has_constant_test:
32749 if (cc.line_num >= 0) line_num = cc.line_num;
32750 if (val == cc.op - OP_if_false) {
32751 /* transform null if_false(l1) -> goto l1 */
32752 /* transform false if_false(l1) -> goto l1 */
32753 /* transform true if_true(l1) -> goto l1 */
32754 pos_next = cc.pos;
32755 op = OP_goto;
32756 label = cc.label;
32757 goto has_goto;
32758 } else {
32759 /* transform null if_true(l1) -> nop */
32760 /* transform false if_true(l1) -> nop */
32761 /* transform true if_false(l1) -> nop */
32762 pos_next = cc.pos;
32763 update_label(s, cc.label, -1);
32764 break;
32765 }
32766 }
32767 }
32768 goto no_change;
32769
32770 case OP_push_i32:
32771 if (OPTIMIZE) {
32772 /* transform i32(val) neg -> i32(-val) */
32773 val = get_i32(bc_buf + pos + 1);
32774 if ((val != INT32_MIN && val != 0)
32775 && code_match(&cc, pos_next, OP_neg, -1)) {
32776 if (cc.line_num >= 0) line_num = cc.line_num;
32777 if (code_match(&cc, cc.pos, OP_drop, -1)) {
32778 if (cc.line_num >= 0) line_num = cc.line_num;
32779 } else {
32780 add_pc2line_info(s, bc_out.size, line_num);
32781 push_short_int(&bc_out, -val);
32782 }
32783 pos_next = cc.pos;
32784 break;
32785 }
32786 /* remove push/drop pairs generated by the parser */
32787 if (code_match(&cc, pos_next, OP_drop, -1)) {
32788 if (cc.line_num >= 0) line_num = cc.line_num;
32789 pos_next = cc.pos;
32790 break;
32791 }
32792 /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
32793 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
32794 val = (val != 0);
32795 goto has_constant_test;
32796 }
32797 add_pc2line_info(s, bc_out.size, line_num);
32798 push_short_int(&bc_out, val);
32799 break;
32800 }
32801 goto no_change;
32802
32803 case OP_push_bigint_i32:
32804 if (OPTIMIZE) {
32805 /* transform i32(val) neg -> i32(-val) */
32806 val = get_i32(bc_buf + pos + 1);
32807 if (val != INT32_MIN
32808 && code_match(&cc, pos_next, OP_neg, -1)) {
32809 if (cc.line_num >= 0) line_num = cc.line_num;
32810 if (code_match(&cc, cc.pos, OP_drop, -1)) {
32811 if (cc.line_num >= 0) line_num = cc.line_num;
32812 } else {
32813 add_pc2line_info(s, bc_out.size, line_num);
32814 dbuf_putc(&bc_out, OP_push_bigint_i32);
32815 dbuf_put_u32(&bc_out, -val);
32816 }
32817 pos_next = cc.pos;
32818 break;
32819 }
32820 }
32821 goto no_change;
32822
32823#if SHORT_OPCODES
32824 case OP_push_const:
32825 case OP_fclosure:
32826 if (OPTIMIZE) {
32827 int idx = get_u32(bc_buf + pos + 1);
32828 if (idx < 256) {
32829 add_pc2line_info(s, bc_out.size, line_num);
32830 dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
32831 dbuf_putc(&bc_out, idx);
32832 break;
32833 }
32834 }
32835 goto no_change;
32836
32837 case OP_get_field:
32838 if (OPTIMIZE) {
32839 JSAtom atom = get_u32(bc_buf + pos + 1);
32840 if (atom == JS_ATOM_length) {
32841 JS_FreeAtom(ctx, atom);
32842 add_pc2line_info(s, bc_out.size, line_num);
32843 dbuf_putc(&bc_out, OP_get_length);
32844 break;
32845 }
32846 }
32847 goto no_change;
32848#endif
32849 case OP_push_atom_value:
32850 if (OPTIMIZE) {
32851 JSAtom atom = get_u32(bc_buf + pos + 1);
32852 /* remove push/drop pairs generated by the parser */
32853 if (code_match(&cc, pos_next, OP_drop, -1)) {
32854 JS_FreeAtom(ctx, atom);
32855 if (cc.line_num >= 0) line_num = cc.line_num;
32856 pos_next = cc.pos;
32857 break;
32858 }
32859#if SHORT_OPCODES
32860 if (atom == JS_ATOM_empty_string) {
32861 JS_FreeAtom(ctx, atom);
32862 add_pc2line_info(s, bc_out.size, line_num);
32863 dbuf_putc(&bc_out, OP_push_empty_string);
32864 break;
32865 }
32866#endif
32867 }
32868 goto no_change;
32869
32870 case OP_to_propkey:
32871 case OP_to_propkey2:
32872 if (OPTIMIZE) {
32873 /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
32874 if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
32875 || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
32876 || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
32877 break;
32878 }
32879 }
32880 goto no_change;
32881
32882 case OP_undefined:
32883 if (OPTIMIZE) {
32884 /* remove push/drop pairs generated by the parser */
32885 if (code_match(&cc, pos_next, OP_drop, -1)) {
32886 if (cc.line_num >= 0) line_num = cc.line_num;
32887 pos_next = cc.pos;
32888 break;
32889 }
32890 /* transform undefined return -> return_undefined */
32891 if (code_match(&cc, pos_next, OP_return, -1)) {
32892 if (cc.line_num >= 0) line_num = cc.line_num;
32893 add_pc2line_info(s, bc_out.size, line_num);
32894 dbuf_putc(&bc_out, OP_return_undef);
32895 pos_next = cc.pos;
32896 break;
32897 }
32898 /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
32899 if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
32900 val = 0;
32901 goto has_constant_test;
32902 }
32903#if SHORT_OPCODES
32904 /* transform undefined strict_eq -> is_undefined */
32905 if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
32906 if (cc.line_num >= 0) line_num = cc.line_num;
32907 add_pc2line_info(s, bc_out.size, line_num);
32908 dbuf_putc(&bc_out, OP_is_undefined);
32909 pos_next = cc.pos;
32910 break;
32911 }
32912 /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
32913 if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
32914 if (cc.line_num >= 0) line_num = cc.line_num;
32915 add_pc2line_info(s, bc_out.size, line_num);
32916 dbuf_putc(&bc_out, OP_is_undefined);
32917 pos_next = cc.pos;
32918 label = cc.label;
32919 op = cc.op ^ OP_if_false ^ OP_if_true;
32920 goto has_label;
32921 }
32922#endif
32923 }
32924 goto no_change;
32925
32926 case OP_insert2:
32927 if (OPTIMIZE) {
32928 /* Transformation:
32929 insert2 put_field(a) drop -> put_field(a)
32930 insert2 put_var_strict(a) drop -> put_var_strict(a)
32931 */
32932 if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
32933 if (cc.line_num >= 0) line_num = cc.line_num;
32934 add_pc2line_info(s, bc_out.size, line_num);
32935 dbuf_putc(&bc_out, cc.op);
32936 dbuf_put_u32(&bc_out, cc.atom);
32937 pos_next = cc.pos;
32938 break;
32939 }
32940 }
32941 goto no_change;
32942
32943 case OP_dup:
32944 if (OPTIMIZE) {
32945 /* Transformation: dup put_x(n) drop -> put_x(n) */
32946 int op1, line2 = -1;
32947 /* Transformation: dup put_x(n) -> set_x(n) */
32948 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
32949 if (cc.line_num >= 0) line_num = cc.line_num;
32950 op1 = cc.op + 1; /* put_x -> set_x */
32951 pos_next = cc.pos;
32952 if (code_match(&cc, cc.pos, OP_drop, -1)) {
32953 if (cc.line_num >= 0) line_num = cc.line_num;
32954 op1 -= 1; /* set_x drop -> put_x */
32955 pos_next = cc.pos;
32956 if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
32957 line2 = cc.line_num; /* delay line number update */
32958 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
32959 pos_next = cc.pos;
32960 }
32961 }
32962 add_pc2line_info(s, bc_out.size, line_num);
32963 put_short_code(&bc_out, op1, cc.idx);
32964 if (line2 >= 0) line_num = line2;
32965 break;
32966 }
32967 }
32968 goto no_change;
32969
32970 case OP_get_loc:
32971 if (OPTIMIZE) {
32972 /* transformation:
32973 get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
32974 get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
32975 get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
32976 get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
32977 */
32978 int idx;
32979 idx = get_u16(bc_buf + pos + 1);
32980 if (idx >= 256)
32981 goto no_change;
32982 if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
32983 code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
32984 if (cc.line_num >= 0) line_num = cc.line_num;
32985 add_pc2line_info(s, bc_out.size, line_num);
32986 dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
32987 dbuf_putc(&bc_out, idx);
32988 pos_next = cc.pos;
32989 break;
32990 }
32991 /* transformation:
32992 get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
32993 */
32994 if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
32995 if (cc.line_num >= 0) line_num = cc.line_num;
32996 add_pc2line_info(s, bc_out.size, line_num);
32997#if SHORT_OPCODES
32998 if (cc.atom == JS_ATOM_empty_string) {
32999 JS_FreeAtom(ctx, cc.atom);
33000 dbuf_putc(&bc_out, OP_push_empty_string);
33001 } else
33002#endif
33003 {
33004 dbuf_putc(&bc_out, OP_push_atom_value);
33005 dbuf_put_u32(&bc_out, cc.atom);
33006 }
33007 dbuf_putc(&bc_out, OP_add_loc);
33008 dbuf_putc(&bc_out, idx);
33009 pos_next = cc.pos;
33010 break;
33011 }
33012 /* transformation:
33013 get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
33014 */
33015 if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
33016 if (cc.line_num >= 0) line_num = cc.line_num;
33017 add_pc2line_info(s, bc_out.size, line_num);
33018 push_short_int(&bc_out, cc.label);
33019 dbuf_putc(&bc_out, OP_add_loc);
33020 dbuf_putc(&bc_out, idx);
33021 pos_next = cc.pos;
33022 break;
33023 }
33024 /* transformation: XXX: also do these:
33025 get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
33026 get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
33027 get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
33028 */
33029 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)) {
33030 if (cc.line_num >= 0) line_num = cc.line_num;
33031 add_pc2line_info(s, bc_out.size, line_num);
33032 put_short_code(&bc_out, cc.op, cc.idx);
33033 dbuf_putc(&bc_out, OP_add_loc);
33034 dbuf_putc(&bc_out, idx);
33035 pos_next = cc.pos;
33036 break;
33037 }
33038 add_pc2line_info(s, bc_out.size, line_num);
33039 put_short_code(&bc_out, op, idx);
33040 break;
33041 }
33042 goto no_change;
33043#if SHORT_OPCODES
33044 case OP_get_arg:
33045 case OP_get_var_ref:
33046 if (OPTIMIZE) {
33047 int idx;
33048 idx = get_u16(bc_buf + pos + 1);
33049 add_pc2line_info(s, bc_out.size, line_num);
33050 put_short_code(&bc_out, op, idx);
33051 break;
33052 }
33053 goto no_change;
33054#endif
33055 case OP_put_loc:
33056 case OP_put_arg:
33057 case OP_put_var_ref:
33058 if (OPTIMIZE) {
33059 /* transformation: put_x(n) get_x(n) -> set_x(n) */
33060 int idx;
33061 idx = get_u16(bc_buf + pos + 1);
33062 if (code_match(&cc, pos_next, op - 1, idx, -1)) {
33063 if (cc.line_num >= 0) line_num = cc.line_num;
33064 add_pc2line_info(s, bc_out.size, line_num);
33065 put_short_code(&bc_out, op + 1, idx);
33066 pos_next = cc.pos;
33067 break;
33068 }
33069 add_pc2line_info(s, bc_out.size, line_num);
33070 put_short_code(&bc_out, op, idx);
33071 break;
33072 }
33073 goto no_change;
33074
33075 case OP_post_inc:
33076 case OP_post_dec:
33077 if (OPTIMIZE) {
33078 /* transformation:
33079 post_inc put_x drop -> inc put_x
33080 post_inc perm3 put_field drop -> inc put_field
33081 post_inc perm3 put_var_strict drop -> inc put_var_strict
33082 post_inc perm4 put_array_el drop -> inc put_array_el
33083 */
33084 int op1, idx;
33085 if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
33086 if (cc.line_num >= 0) line_num = cc.line_num;
33087 op1 = cc.op;
33088 idx = cc.idx;
33089 pos_next = cc.pos;
33090 if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
33091 if (cc.line_num >= 0) line_num = cc.line_num;
33092 op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
33093 pos_next = cc.pos;
33094 }
33095 add_pc2line_info(s, bc_out.size, line_num);
33096 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
33097 put_short_code(&bc_out, op1, idx);
33098 break;
33099 }
33100 if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
33101 if (cc.line_num >= 0) line_num = cc.line_num;
33102 add_pc2line_info(s, bc_out.size, line_num);
33103 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
33104 dbuf_putc(&bc_out, cc.op);
33105 dbuf_put_u32(&bc_out, cc.atom);
33106 pos_next = cc.pos;
33107 break;
33108 }
33109 if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
33110 if (cc.line_num >= 0) line_num = cc.line_num;
33111 add_pc2line_info(s, bc_out.size, line_num);
33112 dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
33113 dbuf_putc(&bc_out, OP_put_array_el);
33114 pos_next = cc.pos;
33115 break;
33116 }
33117 }
33118 goto no_change;
33119
33120#if SHORT_OPCODES
33121 case OP_typeof:
33122 if (OPTIMIZE) {
33123 /* simplify typeof tests */
33124 if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
33125 if (cc.line_num >= 0) line_num = cc.line_num;
33126 int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
33127 int op2 = -1;
33128 switch (cc.atom) {
33129 case JS_ATOM_undefined:
33130 op2 = OP_typeof_is_undefined;
33131 break;
33132 case JS_ATOM_function:
33133 op2 = OP_typeof_is_function;
33134 break;
33135 }
33136 if (op2 >= 0) {
33137 /* transform typeof(s) == "<type>" into is_<type> */
33138 if (op1 == OP_strict_eq) {
33139 add_pc2line_info(s, bc_out.size, line_num);
33140 dbuf_putc(&bc_out, op2);
33141 JS_FreeAtom(ctx, cc.atom);
33142 pos_next = cc.pos;
33143 break;
33144 }
33145 if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
33146 /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
33147 if (cc.line_num >= 0) line_num = cc.line_num;
33148 add_pc2line_info(s, bc_out.size, line_num);
33149 dbuf_putc(&bc_out, op2);
33150 JS_FreeAtom(ctx, cc.atom);
33151 pos_next = cc.pos;
33152 label = cc.label;
33153 op = OP_if_true;
33154 goto has_label;
33155 }
33156 }
33157 }
33158 }
33159 goto no_change;
33160#endif
33161
33162 default:
33163 no_change:
33164 add_pc2line_info(s, bc_out.size, line_num);
33165 dbuf_put(&bc_out, bc_buf + pos, len);
33166 break;
33167 }
33168 }
33169
33170 /* check that there were no missing labels */
33171 for(i = 0; i < s->label_count; i++) {
33172 assert(label_slots[i].first_reloc == NULL);
33173 }
33174#if SHORT_OPCODES
33175 if (OPTIMIZE) {
33176 /* more jump optimizations */
33177 int patch_offsets = 0;
33178 for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
33179 LabelSlot *ls;
33180 JumpSlot *jp1;
33181 int j, pos, diff, delta;
33182
33183 delta = 3;
33184 switch (op = jp->op) {
33185 case OP_goto16:
33186 delta = 1;
33187 /* fall thru */
33188 case OP_if_false:
33189 case OP_if_true:
33190 case OP_goto:
33191 pos = jp->pos;
33192 diff = s->label_slots[jp->label].addr - pos;
33193 if (diff >= -128 && diff <= 127 + delta) {
33194 //put_u8(bc_out.buf + pos, diff);
33195 jp->size = 1;
33196 if (op == OP_goto16) {
33197 bc_out.buf[pos - 1] = jp->op = OP_goto8;
33198 } else {
33199 bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
33200 }
33201 goto shrink;
33202 } else
33203 if (diff == (int16_t)diff && op == OP_goto) {
33204 //put_u16(bc_out.buf + pos, diff);
33205 jp->size = 2;
33206 delta = 2;
33207 bc_out.buf[pos - 1] = jp->op = OP_goto16;
33208 shrink:
33209 /* XXX: should reduce complexity, using 2 finger copy scheme */
33210 memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
33211 bc_out.size - pos - jp->size - delta);
33212 bc_out.size -= delta;
33213 patch_offsets++;
33214 for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
33215 if (ls->addr > pos)
33216 ls->addr -= delta;
33217 }
33218 for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
33219 if (jp1->pos > pos)
33220 jp1->pos -= delta;
33221 }
33222 for (j = 0; j < s->line_number_count; j++) {
33223 if (s->line_number_slots[j].pc > pos)
33224 s->line_number_slots[j].pc -= delta;
33225 }
33226 continue;
33227 }
33228 break;
33229 }
33230 }
33231 if (patch_offsets) {
33232 JumpSlot *jp1;
33233 int j;
33234 for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
33235 int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
33236 switch (jp1->size) {
33237 case 1:
33238 put_u8(bc_out.buf + jp1->pos, diff1);
33239 break;
33240 case 2:
33241 put_u16(bc_out.buf + jp1->pos, diff1);
33242 break;
33243 case 4:
33244 put_u32(bc_out.buf + jp1->pos, diff1);
33245 break;
33246 }
33247 }
33248 }
33249 }
33250 js_free(ctx, s->jump_slots);
33251 s->jump_slots = NULL;
33252#endif
33253 js_free(ctx, s->label_slots);
33254 s->label_slots = NULL;
33255 /* XXX: should delay until copying to runtime bytecode function */
33256 compute_pc2line_info(s);
33257 js_free(ctx, s->line_number_slots);
33258 s->line_number_slots = NULL;
33259 /* set the new byte code */
33260 dbuf_free(&s->byte_code);
33261 s->byte_code = bc_out;
33262 s->use_short_opcodes = TRUE;
33263 if (dbuf_error(&s->byte_code)) {
33264 JS_ThrowOutOfMemory(ctx);
33265 return -1;
33266 }
33267 return 0;
33268 fail:
33269 /* XXX: not safe */
33270 dbuf_free(&bc_out);
33271 return -1;
33272}
33273
33274/* compute the maximum stack size needed by the function */
33275
33276typedef struct StackSizeState {
33277 int bc_len;
33278 int stack_len_max;
33279 uint16_t *stack_level_tab;
33280 int32_t *catch_pos_tab;
33281 int *pc_stack;
33282 int pc_stack_len;
33283 int pc_stack_size;
33284} StackSizeState;
33285
33286/* 'op' is only used for error indication */
33287static __exception int ss_check(JSContext *ctx, StackSizeState *s,
33288 int pos, int op, int stack_len, int catch_pos)
33289{
33290 if ((unsigned)pos >= s->bc_len) {
33291 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
33292 return -1;
33293 }
33294 if (stack_len > s->stack_len_max) {
33295 s->stack_len_max = stack_len;
33296 if (s->stack_len_max > JS_STACK_SIZE_MAX) {
33297 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
33298 return -1;
33299 }
33300 }
33301 if (s->stack_level_tab[pos] != 0xffff) {
33302 /* already explored: check that the stack size is consistent */
33303 if (s->stack_level_tab[pos] != stack_len) {
33304 JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
33305 s->stack_level_tab[pos], stack_len, pos);
33306 return -1;
33307 } else if (s->catch_pos_tab[pos] != catch_pos) {
33308 JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
33309 s->catch_pos_tab[pos], catch_pos, pos);
33310 return -1;
33311 } else {
33312 return 0;
33313 }
33314 }
33315
33316 /* mark as explored and store the stack size */
33317 s->stack_level_tab[pos] = stack_len;
33318 s->catch_pos_tab[pos] = catch_pos;
33319
33320 /* queue the new PC to explore */
33321 if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
33322 &s->pc_stack_size, s->pc_stack_len + 1))
33323 return -1;
33324 s->pc_stack[s->pc_stack_len++] = pos;
33325 return 0;
33326}
33327
33328static __exception int compute_stack_size(JSContext *ctx,
33329 JSFunctionDef *fd,
33330 int *pstack_size)
33331{
33332 StackSizeState s_s, *s = &s_s;
33333 int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level;
33334 const JSOpCode *oi;
33335 const uint8_t *bc_buf;
33336
33337 bc_buf = fd->byte_code.buf;
33338 s->bc_len = fd->byte_code.size;
33339 /* bc_len > 0 */
33340 s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
33341 s->bc_len);
33342 if (!s->stack_level_tab)
33343 return -1;
33344 for(i = 0; i < s->bc_len; i++)
33345 s->stack_level_tab[i] = 0xffff;
33346 s->pc_stack = NULL;
33347 s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) *
33348 s->bc_len);
33349 if (!s->catch_pos_tab)
33350 goto fail;
33351
33352 s->stack_len_max = 0;
33353 s->pc_stack_len = 0;
33354 s->pc_stack_size = 0;
33355
33356 /* breadth-first graph exploration */
33357 if (ss_check(ctx, s, 0, OP_invalid, 0, -1))
33358 goto fail;
33359
33360 while (s->pc_stack_len > 0) {
33361 pos = s->pc_stack[--s->pc_stack_len];
33362 stack_len = s->stack_level_tab[pos];
33363 catch_pos = s->catch_pos_tab[pos];
33364 op = bc_buf[pos];
33365 if (op == 0 || op >= OP_COUNT) {
33366 JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
33367 goto fail;
33368 }
33369 oi = &short_opcode_info(op);
33370#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
33371 printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
33372#endif
33373 pos_next = pos + oi->size;
33374 if (pos_next > s->bc_len) {
33375 JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
33376 goto fail;
33377 }
33378 n_pop = oi->n_pop;
33379 /* call pops a variable number of arguments */
33380 if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
33381 n_pop += get_u16(bc_buf + pos + 1);
33382 } else {
33383#if SHORT_OPCODES
33384 if (oi->fmt == OP_FMT_npopx) {
33385 n_pop += op - OP_call0;
33386 }
33387#endif
33388 }
33389
33390 if (stack_len < n_pop) {
33391 JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
33392 goto fail;
33393 }
33394 stack_len += oi->n_push - n_pop;
33395 if (stack_len > s->stack_len_max) {
33396 s->stack_len_max = stack_len;
33397 if (s->stack_len_max > JS_STACK_SIZE_MAX) {
33398 JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
33399 goto fail;
33400 }
33401 }
33402 switch(op) {
33403 case OP_tail_call:
33404 case OP_tail_call_method:
33405 case OP_return:
33406 case OP_return_undef:
33407 case OP_return_async:
33408 case OP_throw:
33409 case OP_throw_error:
33410 case OP_ret:
33411 goto done_insn;
33412 case OP_goto:
33413 diff = get_u32(bc_buf + pos + 1);
33414 pos_next = pos + 1 + diff;
33415 break;
33416#if SHORT_OPCODES
33417 case OP_goto16:
33418 diff = (int16_t)get_u16(bc_buf + pos + 1);
33419 pos_next = pos + 1 + diff;
33420 break;
33421 case OP_goto8:
33422 diff = (int8_t)bc_buf[pos + 1];
33423 pos_next = pos + 1 + diff;
33424 break;
33425 case OP_if_true8:
33426 case OP_if_false8:
33427 diff = (int8_t)bc_buf[pos + 1];
33428 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
33429 goto fail;
33430 break;
33431#endif
33432 case OP_if_true:
33433 case OP_if_false:
33434 diff = get_u32(bc_buf + pos + 1);
33435 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
33436 goto fail;
33437 break;
33438 case OP_gosub:
33439 diff = get_u32(bc_buf + pos + 1);
33440 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
33441 goto fail;
33442 break;
33443 case OP_with_get_var:
33444 case OP_with_delete_var:
33445 diff = get_u32(bc_buf + pos + 5);
33446 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos))
33447 goto fail;
33448 break;
33449 case OP_with_make_ref:
33450 case OP_with_get_ref:
33451 diff = get_u32(bc_buf + pos + 5);
33452 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos))
33453 goto fail;
33454 break;
33455 case OP_with_put_var:
33456 diff = get_u32(bc_buf + pos + 5);
33457 if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos))
33458 goto fail;
33459 break;
33460 case OP_catch:
33461 diff = get_u32(bc_buf + pos + 1);
33462 if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
33463 goto fail;
33464 catch_pos = pos;
33465 break;
33466 case OP_for_of_start:
33467 case OP_for_await_of_start:
33468 catch_pos = pos;
33469 break;
33470 /* we assume the catch offset entry is only removed with
33471 some op codes */
33472 case OP_drop:
33473 catch_level = stack_len;
33474 goto check_catch;
33475 case OP_nip:
33476 catch_level = stack_len - 1;
33477 goto check_catch;
33478 case OP_nip1:
33479 catch_level = stack_len - 1;
33480 goto check_catch;
33481 case OP_iterator_close:
33482 catch_level = stack_len + 2;
33483 check_catch:
33484 /* Note: for for_of_start/for_await_of_start we consider
33485 the catch offset is on the first stack entry instead of
33486 the thirst */
33487 if (catch_pos >= 0) {
33488 int level;
33489 level = s->stack_level_tab[catch_pos];
33490 if (bc_buf[catch_pos] != OP_catch)
33491 level++; /* for_of_start, for_wait_of_start */
33492 /* catch_level = stack_level before op_catch is executed ? */
33493 if (catch_level == level) {
33494 catch_pos = s->catch_pos_tab[catch_pos];
33495 }
33496 }
33497 break;
33498 case OP_nip_catch:
33499 if (catch_pos < 0) {
33500 JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
33501 goto fail;
33502 }
33503 stack_len = s->stack_level_tab[catch_pos];
33504 if (bc_buf[catch_pos] != OP_catch)
33505 stack_len++; /* for_of_start, for_wait_of_start */
33506 stack_len++; /* no stack overflow is possible by construction */
33507 catch_pos = s->catch_pos_tab[catch_pos];
33508 break;
33509 default:
33510 break;
33511 }
33512 if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos))
33513 goto fail;
33514 done_insn: ;
33515 }
33516 js_free(ctx, s->pc_stack);
33517 js_free(ctx, s->catch_pos_tab);
33518 js_free(ctx, s->stack_level_tab);
33519 *pstack_size = s->stack_len_max;
33520 return 0;
33521 fail:
33522 js_free(ctx, s->pc_stack);
33523 js_free(ctx, s->catch_pos_tab);
33524 js_free(ctx, s->stack_level_tab);
33525 *pstack_size = 0;
33526 return -1;
33527}
33528
33529static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
33530{
33531 int i, idx;
33532 JSModuleDef *m = fd->module;
33533 JSExportEntry *me;
33534 JSGlobalVar *hf;
33535
33536 /* The imported global variables were added as closure variables
33537 in js_parse_import(). We add here the module global
33538 variables. */
33539
33540 for(i = 0; i < fd->global_var_count; i++) {
33541 hf = &fd->global_vars[i];
33542 if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
33543 hf->is_lexical, FALSE) < 0)
33544 return -1;
33545 }
33546
33547 /* resolve the variable names of the local exports */
33548 for(i = 0; i < m->export_entries_count; i++) {
33549 me = &m->export_entries[i];
33550 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
33551 idx = find_closure_var(ctx, fd, me->local_name);
33552 if (idx < 0) {
33553 JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
33554 me->local_name);
33555 return -1;
33556 }
33557 me->u.local.var_idx = idx;
33558 }
33559 }
33560 return 0;
33561}
33562
33563/* create a function object from a function definition. The function
33564 definition is freed. All the child functions are also created. It
33565 must be done this way to resolve all the variables. */
33566static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
33567{
33568 JSValue func_obj;
33569 JSFunctionBytecode *b;
33570 struct list_head *el, *el1;
33571 int stack_size, scope, idx;
33572 int function_size, byte_code_offset, cpool_offset;
33573 int closure_var_offset, vardefs_offset;
33574
33575 /* recompute scope linkage */
33576 for (scope = 0; scope < fd->scope_count; scope++) {
33577 fd->scopes[scope].first = -1;
33578 }
33579 if (fd->has_parameter_expressions) {
33580 /* special end of variable list marker for the argument scope */
33581 fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
33582 }
33583 for (idx = 0; idx < fd->var_count; idx++) {
33584 JSVarDef *vd = &fd->vars[idx];
33585 vd->scope_next = fd->scopes[vd->scope_level].first;
33586 fd->scopes[vd->scope_level].first = idx;
33587 }
33588 for (scope = 2; scope < fd->scope_count; scope++) {
33589 JSVarScope *sd = &fd->scopes[scope];
33590 if (sd->first < 0)
33591 sd->first = fd->scopes[sd->parent].first;
33592 }
33593 for (idx = 0; idx < fd->var_count; idx++) {
33594 JSVarDef *vd = &fd->vars[idx];
33595 if (vd->scope_next < 0 && vd->scope_level > 1) {
33596 scope = fd->scopes[vd->scope_level].parent;
33597 vd->scope_next = fd->scopes[scope].first;
33598 }
33599 }
33600
33601 /* if the function contains an eval call, the closure variables
33602 are used to compile the eval and they must be ordered by scope,
33603 so it is necessary to create the closure variables before any
33604 other variable lookup is done. */
33605 if (fd->has_eval_call)
33606 add_eval_variables(ctx, fd);
33607
33608 /* add the module global variables in the closure */
33609 if (fd->module) {
33610 if (add_module_variables(ctx, fd))
33611 goto fail;
33612 }
33613
33614 /* first create all the child functions */
33615 list_for_each_safe(el, el1, &fd->child_list) {
33616 JSFunctionDef *fd1;
33617 int cpool_idx;
33618
33619 fd1 = list_entry(el, JSFunctionDef, link);
33620 cpool_idx = fd1->parent_cpool_idx;
33621 func_obj = js_create_function(ctx, fd1);
33622 if (JS_IsException(func_obj))
33623 goto fail;
33624 /* save it in the constant pool */
33625 assert(cpool_idx >= 0);
33626 fd->cpool[cpool_idx] = func_obj;
33627 }
33628
33629#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
33630 if (!fd->strip_debug) {
33631 printf("pass 1\n");
33632 dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
33633 fd->args, fd->arg_count, fd->vars, fd->var_count,
33634 fd->closure_var, fd->closure_var_count,
33635 fd->cpool, fd->cpool_count, fd->source,
33636 fd->label_slots, NULL);
33637 printf("\n");
33638 }
33639#endif
33640
33641 if (resolve_variables(ctx, fd))
33642 goto fail;
33643
33644#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
33645 if (!fd->strip_debug) {
33646 printf("pass 2\n");
33647 dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
33648 fd->args, fd->arg_count, fd->vars, fd->var_count,
33649 fd->closure_var, fd->closure_var_count,
33650 fd->cpool, fd->cpool_count, fd->source,
33651 fd->label_slots, NULL);
33652 printf("\n");
33653 }
33654#endif
33655
33656 if (resolve_labels(ctx, fd))
33657 goto fail;
33658
33659 if (compute_stack_size(ctx, fd, &stack_size) < 0)
33660 goto fail;
33661
33662 if (fd->strip_debug) {
33663 function_size = offsetof(JSFunctionBytecode, debug);
33664 } else {
33665 function_size = sizeof(*b);
33666 }
33667 cpool_offset = function_size;
33668 function_size += fd->cpool_count * sizeof(*fd->cpool);
33669 vardefs_offset = function_size;
33670 if (!fd->strip_debug || fd->has_eval_call) {
33671 function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
33672 }
33673 closure_var_offset = function_size;
33674 function_size += fd->closure_var_count * sizeof(*fd->closure_var);
33675 byte_code_offset = function_size;
33676 function_size += fd->byte_code.size;
33677
33678 b = js_mallocz(ctx, function_size);
33679 if (!b)
33680 goto fail;
33681 b->header.ref_count = 1;
33682
33683 b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
33684 b->byte_code_len = fd->byte_code.size;
33685 memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
33686 js_free(ctx, fd->byte_code.buf);
33687 fd->byte_code.buf = NULL;
33688
33689 b->func_name = fd->func_name;
33690 if (fd->arg_count + fd->var_count > 0) {
33691 if (fd->strip_debug && !fd->has_eval_call) {
33692 /* Strip variable definitions not needed at runtime */
33693 int i;
33694 for(i = 0; i < fd->var_count; i++) {
33695 JS_FreeAtom(ctx, fd->vars[i].var_name);
33696 }
33697 for(i = 0; i < fd->arg_count; i++) {
33698 JS_FreeAtom(ctx, fd->args[i].var_name);
33699 }
33700 for(i = 0; i < fd->closure_var_count; i++) {
33701 JS_FreeAtom(ctx, fd->closure_var[i].var_name);
33702 fd->closure_var[i].var_name = JS_ATOM_NULL;
33703 }
33704 } else {
33705 b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
33706 memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
33707 memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
33708 }
33709 b->var_count = fd->var_count;
33710 b->arg_count = fd->arg_count;
33711 b->defined_arg_count = fd->defined_arg_count;
33712 js_free(ctx, fd->args);
33713 js_free(ctx, fd->vars);
33714 }
33715 b->cpool_count = fd->cpool_count;
33716 if (b->cpool_count) {
33717 b->cpool = (void *)((uint8_t*)b + cpool_offset);
33718 memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
33719 }
33720 js_free(ctx, fd->cpool);
33721 fd->cpool = NULL;
33722
33723 b->stack_size = stack_size;
33724
33725 if (fd->strip_debug) {
33726 JS_FreeAtom(ctx, fd->filename);
33727 dbuf_free(&fd->pc2line); // probably useless
33728 } else {
33729 /* XXX: source and pc2line info should be packed at the end of the
33730 JSFunctionBytecode structure, avoiding allocation overhead
33731 */
33732 b->has_debug = 1;
33733 b->debug.filename = fd->filename;
33734
33735 //DynBuf pc2line;
33736 //compute_pc2line_info(fd, &pc2line);
33737 //js_free(ctx, fd->line_number_slots)
33738 b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
33739 if (!b->debug.pc2line_buf)
33740 b->debug.pc2line_buf = fd->pc2line.buf;
33741 b->debug.pc2line_len = fd->pc2line.size;
33742 b->debug.source = fd->source;
33743 b->debug.source_len = fd->source_len;
33744 }
33745 if (fd->scopes != fd->def_scope_array)
33746 js_free(ctx, fd->scopes);
33747
33748 b->closure_var_count = fd->closure_var_count;
33749 if (b->closure_var_count) {
33750 b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
33751 memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
33752 }
33753 js_free(ctx, fd->closure_var);
33754 fd->closure_var = NULL;
33755
33756 b->has_prototype = fd->has_prototype;
33757 b->has_simple_parameter_list = fd->has_simple_parameter_list;
33758 b->js_mode = fd->js_mode;
33759 b->is_derived_class_constructor = fd->is_derived_class_constructor;
33760 b->func_kind = fd->func_kind;
33761 b->need_home_object = (fd->home_object_var_idx >= 0 ||
33762 fd->need_home_object);
33763 b->new_target_allowed = fd->new_target_allowed;
33764 b->super_call_allowed = fd->super_call_allowed;
33765 b->super_allowed = fd->super_allowed;
33766 b->arguments_allowed = fd->arguments_allowed;
33767 b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT ||
33768 fd->eval_type == JS_EVAL_TYPE_INDIRECT);
33769 b->realm = JS_DupContext(ctx);
33770
33771 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
33772
33773#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
33774 if (!fd->strip_debug) {
33775 js_dump_function_bytecode(ctx, b);
33776 }
33777#endif
33778
33779 if (fd->parent) {
33780 /* remove from parent list */
33781 list_del(&fd->link);
33782 }
33783
33784 js_free(ctx, fd);
33785 return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
33786 fail:
33787 js_free_function_def(ctx, fd);
33788 return JS_EXCEPTION;
33789}
33790
33791static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
33792{
33793 int i;
33794
33795#if 0
33796 {
33797 char buf[ATOM_GET_STR_BUF_SIZE];
33798 printf("freeing %s\n",
33799 JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
33800 }
33801#endif
33802 free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
33803
33804 if (b->vardefs) {
33805 for(i = 0; i < b->arg_count + b->var_count; i++) {
33806 JS_FreeAtomRT(rt, b->vardefs[i].var_name);
33807 }
33808 }
33809 for(i = 0; i < b->cpool_count; i++)
33810 JS_FreeValueRT(rt, b->cpool[i]);
33811
33812 for(i = 0; i < b->closure_var_count; i++) {
33813 JSClosureVar *cv = &b->closure_var[i];
33814 JS_FreeAtomRT(rt, cv->var_name);
33815 }
33816 if (b->realm)
33817 JS_FreeContext(b->realm);
33818
33819 JS_FreeAtomRT(rt, b->func_name);
33820 if (b->has_debug) {
33821 JS_FreeAtomRT(rt, b->debug.filename);
33822 js_free_rt(rt, b->debug.pc2line_buf);
33823 js_free_rt(rt, b->debug.source);
33824 }
33825
33826 remove_gc_object(&b->header);
33827 if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
33828 list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
33829 } else {
33830 js_free_rt(rt, b);
33831 }
33832}
33833
33834static __exception int js_parse_directives(JSParseState *s)
33835{
33836 char str[20];
33837 JSParsePos pos;
33838 BOOL has_semi;
33839
33840 if (s->token.val != TOK_STRING)
33841 return 0;
33842
33843 js_parse_get_pos(s, &pos);
33844
33845 while(s->token.val == TOK_STRING) {
33846 /* Copy actual source string representation */
33847 snprintf(str, sizeof str, "%.*s",
33848 (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
33849
33850 if (next_token(s))
33851 return -1;
33852
33853 has_semi = FALSE;
33854 switch (s->token.val) {
33855 case ';':
33856 if (next_token(s))
33857 return -1;
33858 has_semi = TRUE;
33859 break;
33860 case '}':
33861 case TOK_EOF:
33862 has_semi = TRUE;
33863 break;
33864 case TOK_NUMBER:
33865 case TOK_STRING:
33866 case TOK_TEMPLATE:
33867 case TOK_IDENT:
33868 case TOK_REGEXP:
33869 case TOK_DEC:
33870 case TOK_INC:
33871 case TOK_NULL:
33872 case TOK_FALSE:
33873 case TOK_TRUE:
33874 case TOK_IF:
33875 case TOK_RETURN:
33876 case TOK_VAR:
33877 case TOK_THIS:
33878 case TOK_DELETE:
33879 case TOK_TYPEOF:
33880 case TOK_NEW:
33881 case TOK_DO:
33882 case TOK_WHILE:
33883 case TOK_FOR:
33884 case TOK_SWITCH:
33885 case TOK_THROW:
33886 case TOK_TRY:
33887 case TOK_FUNCTION:
33888 case TOK_DEBUGGER:
33889 case TOK_WITH:
33890 case TOK_CLASS:
33891 case TOK_CONST:
33892 case TOK_ENUM:
33893 case TOK_EXPORT:
33894 case TOK_IMPORT:
33895 case TOK_SUPER:
33896 case TOK_INTERFACE:
33897 case TOK_LET:
33898 case TOK_PACKAGE:
33899 case TOK_PRIVATE:
33900 case TOK_PROTECTED:
33901 case TOK_PUBLIC:
33902 case TOK_STATIC:
33903 /* automatic insertion of ';' */
33904 if (s->got_lf)
33905 has_semi = TRUE;
33906 break;
33907 default:
33908 break;
33909 }
33910 if (!has_semi)
33911 break;
33912 if (!strcmp(str, "use strict")) {
33913 s->cur_func->has_use_strict = TRUE;
33914 s->cur_func->js_mode |= JS_MODE_STRICT;
33915 }
33916 }
33917 return js_parse_seek_token(s, &pos);
33918}
33919
33920/* return TRUE if the keyword is forbidden only in strict mode */
33921static BOOL is_strict_future_keyword(JSAtom atom)
33922{
33923 return (atom >= JS_ATOM_LAST_KEYWORD + 1 && atom <= JS_ATOM_LAST_STRICT_KEYWORD);
33924}
33925
33926static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
33927 JSAtom func_name)
33928{
33929 JSAtom name;
33930 int i, idx;
33931
33932 if (fd->js_mode & JS_MODE_STRICT) {
33933 if (!fd->has_simple_parameter_list && fd->has_use_strict) {
33934 return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
33935 }
33936 if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments ||
33937 is_strict_future_keyword(func_name)) {
33938 return js_parse_error(s, "invalid function name in strict code");
33939 }
33940 for (idx = 0; idx < fd->arg_count; idx++) {
33941 name = fd->args[idx].var_name;
33942
33943 if (name == JS_ATOM_eval || name == JS_ATOM_arguments ||
33944 is_strict_future_keyword(name)) {
33945 return js_parse_error(s, "invalid argument name in strict code");
33946 }
33947 }
33948 }
33949 /* check async_generator case */
33950 if ((fd->js_mode & JS_MODE_STRICT)
33951 || !fd->has_simple_parameter_list
33952 || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
33953 || fd->func_type == JS_PARSE_FUNC_ARROW
33954 || fd->func_type == JS_PARSE_FUNC_METHOD) {
33955 for (idx = 0; idx < fd->arg_count; idx++) {
33956 name = fd->args[idx].var_name;
33957 if (name != JS_ATOM_NULL) {
33958 for (i = 0; i < idx; i++) {
33959 if (fd->args[i].var_name == name)
33960 goto duplicate;
33961 }
33962 /* Check if argument name duplicates a destructuring parameter */
33963 /* XXX: should have a flag for such variables */
33964 for (i = 0; i < fd->var_count; i++) {
33965 if (fd->vars[i].var_name == name &&
33966 fd->vars[i].scope_level == 0)
33967 goto duplicate;
33968 }
33969 }
33970 }
33971 }
33972 return 0;
33973
33974duplicate:
33975 return js_parse_error(s, "duplicate argument names not allowed in this context");
33976}
33977
33978/* create a function to initialize class fields */
33979static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
33980{
33981 JSFunctionDef *fd;
33982
33983 fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
33984 s->filename, s->buf_start,
33985 &s->get_line_col_cache);
33986 if (!fd)
33987 return NULL;
33988 fd->func_name = JS_ATOM_NULL;
33989 fd->has_prototype = FALSE;
33990 fd->has_home_object = TRUE;
33991
33992 fd->has_arguments_binding = FALSE;
33993 fd->has_this_binding = TRUE;
33994 fd->is_derived_class_constructor = FALSE;
33995 fd->new_target_allowed = TRUE;
33996 fd->super_call_allowed = FALSE;
33997 fd->super_allowed = fd->has_home_object;
33998 fd->arguments_allowed = FALSE;
33999
34000 fd->func_kind = JS_FUNC_NORMAL;
34001 fd->func_type = JS_PARSE_FUNC_METHOD;
34002 return fd;
34003}
34004
34005/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
34006 JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
34007static __exception int js_parse_function_decl2(JSParseState *s,
34008 JSParseFunctionEnum func_type,
34009 JSFunctionKindEnum func_kind,
34010 JSAtom func_name,
34011 const uint8_t *ptr,
34012 JSParseExportEnum export_flag,
34013 JSFunctionDef **pfd)
34014{
34015 JSContext *ctx = s->ctx;
34016 JSFunctionDef *fd = s->cur_func;
34017 BOOL is_expr;
34018 int func_idx, lexical_func_idx = -1;
34019 BOOL has_opt_arg;
34020 BOOL create_func_var = FALSE;
34021
34022 is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
34023 func_type != JS_PARSE_FUNC_VAR);
34024
34025 if (func_type == JS_PARSE_FUNC_STATEMENT ||
34026 func_type == JS_PARSE_FUNC_VAR ||
34027 func_type == JS_PARSE_FUNC_EXPR) {
34028 if (func_kind == JS_FUNC_NORMAL &&
34029 token_is_pseudo_keyword(s, JS_ATOM_async) &&
34030 peek_token(s, TRUE) != '\n') {
34031 if (next_token(s))
34032 return -1;
34033 func_kind = JS_FUNC_ASYNC;
34034 }
34035 if (next_token(s))
34036 return -1;
34037 if (s->token.val == '*') {
34038 if (next_token(s))
34039 return -1;
34040 func_kind |= JS_FUNC_GENERATOR;
34041 }
34042
34043 if (s->token.val == TOK_IDENT) {
34044 if (s->token.u.ident.is_reserved ||
34045 (s->token.u.ident.atom == JS_ATOM_yield &&
34046 func_type == JS_PARSE_FUNC_EXPR &&
34047 (func_kind & JS_FUNC_GENERATOR)) ||
34048 (s->token.u.ident.atom == JS_ATOM_await &&
34049 ((func_type == JS_PARSE_FUNC_EXPR &&
34050 (func_kind & JS_FUNC_ASYNC)) ||
34051 func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) {
34052 return js_parse_error_reserved_identifier(s);
34053 }
34054 }
34055 if (s->token.val == TOK_IDENT ||
34056 (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
34057 (s->token.val == TOK_AWAIT && !s->is_module)) &&
34058 func_type == JS_PARSE_FUNC_EXPR)) {
34059 func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
34060 if (next_token(s)) {
34061 JS_FreeAtom(ctx, func_name);
34062 return -1;
34063 }
34064 } else {
34065 if (func_type != JS_PARSE_FUNC_EXPR &&
34066 export_flag != JS_PARSE_EXPORT_DEFAULT) {
34067 return js_parse_error(s, "function name expected");
34068 }
34069 }
34070 } else if (func_type != JS_PARSE_FUNC_ARROW) {
34071 func_name = JS_DupAtom(ctx, func_name);
34072 }
34073
34074 if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
34075 (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
34076 JSGlobalVar *hf;
34077 hf = find_global_var(fd, func_name);
34078 /* XXX: should check scope chain */
34079 if (hf && hf->scope_level == fd->scope_level) {
34080 js_parse_error(s, "invalid redefinition of global identifier in module code");
34081 JS_FreeAtom(ctx, func_name);
34082 return -1;
34083 }
34084 }
34085
34086 if (func_type == JS_PARSE_FUNC_VAR) {
34087 if (!(fd->js_mode & JS_MODE_STRICT)
34088 && func_kind == JS_FUNC_NORMAL
34089 && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
34090 && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
34091 && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
34092 create_func_var = TRUE;
34093 }
34094 /* Create the lexical name here so that the function closure
34095 contains it */
34096 if (fd->is_eval &&
34097 (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
34098 fd->eval_type == JS_EVAL_TYPE_MODULE) &&
34099 fd->scope_level == fd->body_scope) {
34100 /* avoid creating a lexical variable in the global
34101 scope. XXX: check annex B */
34102 JSGlobalVar *hf;
34103 hf = find_global_var(fd, func_name);
34104 /* XXX: should check scope chain */
34105 if (hf && hf->scope_level == fd->scope_level) {
34106 js_parse_error(s, "invalid redefinition of global identifier");
34107 JS_FreeAtom(ctx, func_name);
34108 return -1;
34109 }
34110 } else {
34111 /* Always create a lexical name, fail if at the same scope as
34112 existing name */
34113 /* Lexical variable will be initialized upon entering scope */
34114 lexical_func_idx = define_var(s, fd, func_name,
34115 func_kind != JS_FUNC_NORMAL ?
34116 JS_VAR_DEF_NEW_FUNCTION_DECL :
34117 JS_VAR_DEF_FUNCTION_DECL);
34118 if (lexical_func_idx < 0) {
34119 JS_FreeAtom(ctx, func_name);
34120 return -1;
34121 }
34122 }
34123 }
34124
34125 fd = js_new_function_def(ctx, fd, FALSE, is_expr,
34126 s->filename, ptr,
34127 &s->get_line_col_cache);
34128 if (!fd) {
34129 JS_FreeAtom(ctx, func_name);
34130 return -1;
34131 }
34132 if (pfd)
34133 *pfd = fd;
34134 s->cur_func = fd;
34135 fd->func_name = func_name;
34136 /* XXX: test !fd->is_generator is always false */
34137 fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
34138 func_type == JS_PARSE_FUNC_VAR ||
34139 func_type == JS_PARSE_FUNC_EXPR) &&
34140 func_kind == JS_FUNC_NORMAL;
34141 fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
34142 func_type == JS_PARSE_FUNC_GETTER ||
34143 func_type == JS_PARSE_FUNC_SETTER ||
34144 func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
34145 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
34146 fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW &&
34147 func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT);
34148 fd->has_this_binding = fd->has_arguments_binding;
34149 fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
34150 if (func_type == JS_PARSE_FUNC_ARROW) {
34151 fd->new_target_allowed = fd->parent->new_target_allowed;
34152 fd->super_call_allowed = fd->parent->super_call_allowed;
34153 fd->super_allowed = fd->parent->super_allowed;
34154 fd->arguments_allowed = fd->parent->arguments_allowed;
34155 } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) {
34156 fd->new_target_allowed = TRUE; // although new.target === undefined
34157 fd->super_call_allowed = FALSE;
34158 fd->super_allowed = TRUE;
34159 fd->arguments_allowed = FALSE;
34160 } else {
34161 fd->new_target_allowed = TRUE;
34162 fd->super_call_allowed = fd->is_derived_class_constructor;
34163 fd->super_allowed = fd->has_home_object;
34164 fd->arguments_allowed = TRUE;
34165 }
34166
34167 /* fd->in_function_body == FALSE prevents yield/await during the parsing
34168 of the arguments in generator/async functions. They are parsed as
34169 regular identifiers for other function kinds. */
34170 fd->func_kind = func_kind;
34171 fd->func_type = func_type;
34172
34173 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
34174 func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
34175 /* error if not invoked as a constructor */
34176 emit_op(s, OP_check_ctor);
34177 }
34178
34179 if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
34180 emit_class_field_init(s);
34181 }
34182
34183 /* parse arguments */
34184 fd->has_simple_parameter_list = TRUE;
34185 fd->has_parameter_expressions = FALSE;
34186 has_opt_arg = FALSE;
34187 if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
34188 JSAtom name;
34189 if (s->token.u.ident.is_reserved) {
34190 js_parse_error_reserved_identifier(s);
34191 goto fail;
34192 }
34193 name = s->token.u.ident.atom;
34194 if (add_arg(ctx, fd, name) < 0)
34195 goto fail;
34196 fd->defined_arg_count = 1;
34197 } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
34198 if (s->token.val == '(') {
34199 int skip_bits;
34200 /* if there is an '=' inside the parameter list, we
34201 consider there is a parameter expression inside */
34202 js_parse_skip_parens_token(s, &skip_bits, FALSE);
34203 if (skip_bits & SKIP_HAS_ASSIGNMENT)
34204 fd->has_parameter_expressions = TRUE;
34205 if (next_token(s))
34206 goto fail;
34207 } else {
34208 if (js_parse_expect(s, '('))
34209 goto fail;
34210 }
34211
34212 if (fd->has_parameter_expressions) {
34213 fd->scope_level = -1; /* force no parent scope */
34214 if (push_scope(s) < 0)
34215 return -1;
34216 }
34217
34218 while (s->token.val != ')') {
34219 JSAtom name;
34220 BOOL rest = FALSE;
34221 int idx, has_initializer;
34222
34223 if (s->token.val == TOK_ELLIPSIS) {
34224 if (func_type == JS_PARSE_FUNC_SETTER)
34225 goto fail_accessor;
34226 fd->has_simple_parameter_list = FALSE;
34227 rest = TRUE;
34228 if (next_token(s))
34229 goto fail;
34230 }
34231 if (s->token.val == '[' || s->token.val == '{') {
34232 fd->has_simple_parameter_list = FALSE;
34233 if (rest) {
34234 emit_op(s, OP_rest);
34235 emit_u16(s, fd->arg_count);
34236 } else {
34237 /* unnamed arg for destructuring */
34238 idx = add_arg(ctx, fd, JS_ATOM_NULL);
34239 emit_op(s, OP_get_arg);
34240 emit_u16(s, idx);
34241 }
34242 has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE, FALSE);
34243 if (has_initializer < 0)
34244 goto fail;
34245 if (has_initializer)
34246 has_opt_arg = TRUE;
34247 if (!has_opt_arg)
34248 fd->defined_arg_count++;
34249 } else if (s->token.val == TOK_IDENT) {
34250 if (s->token.u.ident.is_reserved) {
34251 js_parse_error_reserved_identifier(s);
34252 goto fail;
34253 }
34254 name = s->token.u.ident.atom;
34255 if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
34256 js_parse_error_reserved_identifier(s);
34257 goto fail;
34258 }
34259 if (fd->has_parameter_expressions) {
34260 if (js_parse_check_duplicate_parameter(s, name))
34261 goto fail;
34262 if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
34263 goto fail;
34264 }
34265 /* XXX: could avoid allocating an argument if rest is true */
34266 idx = add_arg(ctx, fd, name);
34267 if (idx < 0)
34268 goto fail;
34269 if (next_token(s))
34270 goto fail;
34271 if (rest) {
34272 emit_op(s, OP_rest);
34273 emit_u16(s, idx);
34274 if (fd->has_parameter_expressions) {
34275 emit_op(s, OP_dup);
34276 emit_op(s, OP_scope_put_var_init);
34277 emit_atom(s, name);
34278 emit_u16(s, fd->scope_level);
34279 }
34280 emit_op(s, OP_put_arg);
34281 emit_u16(s, idx);
34282 fd->has_simple_parameter_list = FALSE;
34283 has_opt_arg = TRUE;
34284 } else if (s->token.val == '=') {
34285 int label;
34286
34287 fd->has_simple_parameter_list = FALSE;
34288 has_opt_arg = TRUE;
34289
34290 if (next_token(s))
34291 goto fail;
34292
34293 label = new_label(s);
34294 emit_op(s, OP_get_arg);
34295 emit_u16(s, idx);
34296 emit_op(s, OP_dup);
34297 emit_op(s, OP_undefined);
34298 emit_op(s, OP_strict_eq);
34299 emit_goto(s, OP_if_false, label);
34300 emit_op(s, OP_drop);
34301 if (js_parse_assign_expr(s))
34302 goto fail;
34303 set_object_name(s, name);
34304 emit_op(s, OP_dup);
34305 emit_op(s, OP_put_arg);
34306 emit_u16(s, idx);
34307 emit_label(s, label);
34308 emit_op(s, OP_scope_put_var_init);
34309 emit_atom(s, name);
34310 emit_u16(s, fd->scope_level);
34311 } else {
34312 if (!has_opt_arg) {
34313 fd->defined_arg_count++;
34314 }
34315 if (fd->has_parameter_expressions) {
34316 /* copy the argument to the argument scope */
34317 emit_op(s, OP_get_arg);
34318 emit_u16(s, idx);
34319 emit_op(s, OP_scope_put_var_init);
34320 emit_atom(s, name);
34321 emit_u16(s, fd->scope_level);
34322 }
34323 }
34324 } else {
34325 js_parse_error(s, "missing formal parameter");
34326 goto fail;
34327 }
34328 if (rest && s->token.val != ')') {
34329 js_parse_expect(s, ')');
34330 goto fail;
34331 }
34332 if (s->token.val == ')')
34333 break;
34334 if (js_parse_expect(s, ','))
34335 goto fail;
34336 }
34337 if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
34338 (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
34339 fail_accessor:
34340 js_parse_error(s, "invalid number of arguments for getter or setter");
34341 goto fail;
34342 }
34343 }
34344
34345 if (fd->has_parameter_expressions) {
34346 int idx;
34347
34348 /* Copy the variables in the argument scope to the variable
34349 scope (see FunctionDeclarationInstantiation() in spec). The
34350 normal arguments are already present, so no need to copy
34351 them. */
34352 idx = fd->scopes[fd->scope_level].first;
34353 while (idx >= 0) {
34354 JSVarDef *vd = &fd->vars[idx];
34355 if (vd->scope_level != fd->scope_level)
34356 break;
34357 if (find_var(ctx, fd, vd->var_name) < 0) {
34358 if (add_var(ctx, fd, vd->var_name) < 0)
34359 goto fail;
34360 vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
34361 emit_op(s, OP_scope_get_var);
34362 emit_atom(s, vd->var_name);
34363 emit_u16(s, fd->scope_level);
34364 emit_op(s, OP_scope_put_var);
34365 emit_atom(s, vd->var_name);
34366 emit_u16(s, 0);
34367 }
34368 idx = vd->scope_next;
34369 }
34370
34371 /* the argument scope has no parent, hence we don't use pop_scope(s) */
34372 emit_op(s, OP_leave_scope);
34373 emit_u16(s, fd->scope_level);
34374
34375 /* set the variable scope as the current scope */
34376 fd->scope_level = 0;
34377 fd->scope_first = fd->scopes[fd->scope_level].first;
34378 }
34379
34380 if (next_token(s))
34381 goto fail;
34382
34383 /* generator function: yield after the parameters are evaluated */
34384 if (func_kind == JS_FUNC_GENERATOR ||
34385 func_kind == JS_FUNC_ASYNC_GENERATOR)
34386 emit_op(s, OP_initial_yield);
34387
34388 /* in generators, yield expression is forbidden during the parsing
34389 of the arguments */
34390 fd->in_function_body = TRUE;
34391 push_scope(s); /* enter body scope */
34392 fd->body_scope = fd->scope_level;
34393
34394 if (s->token.val == TOK_ARROW) {
34395 if (next_token(s))
34396 goto fail;
34397
34398 if (s->token.val != '{') {
34399 if (js_parse_function_check_names(s, fd, func_name))
34400 goto fail;
34401
34402 if (js_parse_assign_expr(s))
34403 goto fail;
34404
34405 if (func_kind != JS_FUNC_NORMAL)
34406 emit_op(s, OP_return_async);
34407 else
34408 emit_op(s, OP_return);
34409
34410 if (!fd->strip_source) {
34411 /* save the function source code */
34412 /* the end of the function source code is after the last
34413 token of the function source stored into s->last_ptr */
34414 fd->source_len = s->last_ptr - ptr;
34415 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
34416 if (!fd->source)
34417 goto fail;
34418 }
34419 goto done;
34420 }
34421 }
34422
34423 if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) {
34424 if (js_parse_expect(s, '{'))
34425 goto fail;
34426 }
34427
34428 if (js_parse_directives(s))
34429 goto fail;
34430
34431 /* in strict_mode, check function and argument names */
34432 if (js_parse_function_check_names(s, fd, func_name))
34433 goto fail;
34434
34435 while (s->token.val != '}') {
34436 if (js_parse_source_element(s))
34437 goto fail;
34438 }
34439 if (!fd->strip_source) {
34440 /* save the function source code */
34441 fd->source_len = s->buf_ptr - ptr;
34442 fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
34443 if (!fd->source)
34444 goto fail;
34445 }
34446
34447 if (next_token(s)) {
34448 /* consume the '}' */
34449 goto fail;
34450 }
34451
34452 /* in case there is no return, add one */
34453 if (js_is_live_code(s)) {
34454 emit_return(s, FALSE);
34455 }
34456 done:
34457 s->cur_func = fd->parent;
34458
34459 /* Reparse identifiers after the function is terminated so that
34460 the token is parsed in the englobing function. It could be done
34461 by just using next_token() here for normal functions, but it is
34462 necessary for arrow functions with an expression body. */
34463 reparse_ident_token(s);
34464
34465 /* create the function object */
34466 {
34467 int idx;
34468 JSAtom func_name = fd->func_name;
34469
34470 /* the real object will be set at the end of the compilation */
34471 idx = cpool_add(s, JS_NULL);
34472 fd->parent_cpool_idx = idx;
34473
34474 if (is_expr) {
34475 /* for constructors, no code needs to be generated here */
34476 if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
34477 func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
34478 /* OP_fclosure creates the function object from the bytecode
34479 and adds the scope information */
34480 emit_op(s, OP_fclosure);
34481 emit_u32(s, idx);
34482 if (func_name == JS_ATOM_NULL) {
34483 emit_op(s, OP_set_name);
34484 emit_u32(s, JS_ATOM_NULL);
34485 }
34486 }
34487 } else if (func_type == JS_PARSE_FUNC_VAR) {
34488 emit_op(s, OP_fclosure);
34489 emit_u32(s, idx);
34490 if (create_func_var) {
34491 if (s->cur_func->is_global_var) {
34492 JSGlobalVar *hf;
34493 /* the global variable must be defined at the start of the
34494 function */
34495 hf = add_global_var(ctx, s->cur_func, func_name);
34496 if (!hf)
34497 goto fail;
34498 /* it is considered as defined at the top level
34499 (needed for annex B.3.3.4 and B.3.3.5
34500 checks) */
34501 hf->scope_level = 0;
34502 hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
34503 /* store directly into global var, bypass lexical scope */
34504 emit_op(s, OP_dup);
34505 emit_op(s, OP_scope_put_var);
34506 emit_atom(s, func_name);
34507 emit_u16(s, 0);
34508 } else {
34509 /* do not call define_var to bypass lexical scope check */
34510 func_idx = find_var(ctx, s->cur_func, func_name);
34511 if (func_idx < 0) {
34512 func_idx = add_var(ctx, s->cur_func, func_name);
34513 if (func_idx < 0)
34514 goto fail;
34515 }
34516 /* store directly into local var, bypass lexical catch scope */
34517 emit_op(s, OP_dup);
34518 emit_op(s, OP_scope_put_var);
34519 emit_atom(s, func_name);
34520 emit_u16(s, 0);
34521 }
34522 }
34523 if (lexical_func_idx >= 0) {
34524 /* lexical variable will be initialized upon entering scope */
34525 s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
34526 emit_op(s, OP_drop);
34527 } else {
34528 /* store function object into its lexical name */
34529 /* XXX: could use OP_put_loc directly */
34530 emit_op(s, OP_scope_put_var_init);
34531 emit_atom(s, func_name);
34532 emit_u16(s, s->cur_func->scope_level);
34533 }
34534 } else {
34535 if (!s->cur_func->is_global_var) {
34536 int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
34537
34538 if (var_idx < 0)
34539 goto fail;
34540 /* the variable will be assigned at the top of the function */
34541 if (var_idx & ARGUMENT_VAR_OFFSET) {
34542 s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
34543 } else {
34544 s->cur_func->vars[var_idx].func_pool_idx = idx;
34545 }
34546 } else {
34547 JSAtom func_var_name;
34548 JSGlobalVar *hf;
34549 if (func_name == JS_ATOM_NULL)
34550 func_var_name = JS_ATOM__default_; /* export default */
34551 else
34552 func_var_name = func_name;
34553 /* the variable will be assigned at the top of the function */
34554 hf = add_global_var(ctx, s->cur_func, func_var_name);
34555 if (!hf)
34556 goto fail;
34557 hf->cpool_idx = idx;
34558 if (export_flag != JS_PARSE_EXPORT_NONE) {
34559 if (!add_export_entry(s, s->cur_func->module, func_var_name,
34560 export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
34561 goto fail;
34562 }
34563 }
34564 }
34565 }
34566 return 0;
34567 fail:
34568 s->cur_func = fd->parent;
34569 js_free_function_def(ctx, fd);
34570 if (pfd)
34571 *pfd = NULL;
34572 return -1;
34573}
34574
34575static __exception int js_parse_function_decl(JSParseState *s,
34576 JSParseFunctionEnum func_type,
34577 JSFunctionKindEnum func_kind,
34578 JSAtom func_name,
34579 const uint8_t *ptr)
34580{
34581 return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
34582 JS_PARSE_EXPORT_NONE, NULL);
34583}
34584
34585static __exception int js_parse_program(JSParseState *s)
34586{
34587 JSFunctionDef *fd = s->cur_func;
34588 int idx;
34589
34590 if (next_token(s))
34591 return -1;
34592
34593 if (js_parse_directives(s))
34594 return -1;
34595
34596 fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
34597 (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
34598 !(fd->js_mode & JS_MODE_STRICT);
34599
34600 if (!s->is_module) {
34601 /* hidden variable for the return value */
34602 fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
34603 if (idx < 0)
34604 return -1;
34605 }
34606
34607 while (s->token.val != TOK_EOF) {
34608 if (js_parse_source_element(s))
34609 return -1;
34610 }
34611
34612 if (!s->is_module) {
34613 /* return the value of the hidden variable eval_ret_idx */
34614 if (fd->func_kind == JS_FUNC_ASYNC) {
34615 /* wrap the return value in an object so that promises can
34616 be safely returned */
34617 emit_op(s, OP_object);
34618 emit_op(s, OP_dup);
34619
34620 emit_op(s, OP_get_loc);
34621 emit_u16(s, fd->eval_ret_idx);
34622
34623 emit_op(s, OP_put_field);
34624 emit_atom(s, JS_ATOM_value);
34625 } else {
34626 emit_op(s, OP_get_loc);
34627 emit_u16(s, fd->eval_ret_idx);
34628 }
34629 emit_return(s, TRUE);
34630 } else {
34631 emit_return(s, FALSE);
34632 }
34633
34634 return 0;
34635}
34636
34637static void js_parse_init(JSContext *ctx, JSParseState *s,
34638 const char *input, size_t input_len,
34639 const char *filename)
34640{
34641 memset(s, 0, sizeof(*s));
34642 s->ctx = ctx;
34643 s->filename = filename;
34644 s->buf_start = s->buf_ptr = (const uint8_t *)input;
34645 s->buf_end = s->buf_ptr + input_len;
34646 s->token.val = ' ';
34647 s->token.ptr = s->buf_ptr;
34648
34649 s->get_line_col_cache.ptr = s->buf_start;
34650 s->get_line_col_cache.buf_start = s->buf_start;
34651 s->get_line_col_cache.line_num = 0;
34652 s->get_line_col_cache.col_num = 0;
34653}
34654
34655static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
34656 JSValueConst this_obj,
34657 JSVarRef **var_refs, JSStackFrame *sf)
34658{
34659 JSValue ret_val;
34660 uint32_t tag;
34661
34662 tag = JS_VALUE_GET_TAG(fun_obj);
34663 if (tag == JS_TAG_FUNCTION_BYTECODE) {
34664 fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
34665 ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
34666 } else if (tag == JS_TAG_MODULE) {
34667 JSModuleDef *m;
34668 m = JS_VALUE_GET_PTR(fun_obj);
34669 /* the module refcount should be >= 2 */
34670 JS_FreeValue(ctx, fun_obj);
34671 if (js_create_module_function(ctx, m) < 0)
34672 goto fail;
34673 if (js_link_module(ctx, m) < 0)
34674 goto fail;
34675 ret_val = js_evaluate_module(ctx, m);
34676 if (JS_IsException(ret_val)) {
34677 fail:
34678 return JS_EXCEPTION;
34679 }
34680 } else {
34681 JS_FreeValue(ctx, fun_obj);
34682 ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
34683 }
34684 return ret_val;
34685}
34686
34687JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
34688{
34689 return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
34690}
34691
34692/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
34693static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
34694 const char *input, size_t input_len,
34695 const char *filename, int flags, int scope_idx)
34696{
34697 JSParseState s1, *s = &s1;
34698 int err, js_mode, eval_type;
34699 JSValue fun_obj, ret_val;
34700 JSStackFrame *sf;
34701 JSVarRef **var_refs;
34702 JSFunctionBytecode *b;
34703 JSFunctionDef *fd;
34704 JSModuleDef *m;
34705
34706 js_parse_init(ctx, s, input, input_len, filename);
34707 skip_shebang(&s->buf_ptr, s->buf_end);
34708
34709 eval_type = flags & JS_EVAL_TYPE_MASK;
34710 m = NULL;
34711 if (eval_type == JS_EVAL_TYPE_DIRECT) {
34712 JSObject *p;
34713 sf = ctx->rt->current_stack_frame;
34714 assert(sf != NULL);
34715 assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
34716 p = JS_VALUE_GET_OBJ(sf->cur_func);
34717 assert(js_class_has_bytecode(p->class_id));
34718 b = p->u.func.function_bytecode;
34719 var_refs = p->u.func.var_refs;
34720 js_mode = b->js_mode;
34721 } else {
34722 sf = NULL;
34723 b = NULL;
34724 var_refs = NULL;
34725 js_mode = 0;
34726 if (flags & JS_EVAL_FLAG_STRICT)
34727 js_mode |= JS_MODE_STRICT;
34728 if (eval_type == JS_EVAL_TYPE_MODULE) {
34729 JSAtom module_name = JS_NewAtom(ctx, filename);
34730 if (module_name == JS_ATOM_NULL)
34731 return JS_EXCEPTION;
34732 m = js_new_module_def(ctx, module_name);
34733 if (!m)
34734 return JS_EXCEPTION;
34735 js_mode |= JS_MODE_STRICT;
34736 }
34737 }
34738 fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename,
34739 s->buf_start, &s->get_line_col_cache);
34740 if (!fd)
34741 goto fail1;
34742 s->cur_func = fd;
34743 fd->eval_type = eval_type;
34744 fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
34745 if (eval_type == JS_EVAL_TYPE_DIRECT) {
34746 fd->new_target_allowed = b->new_target_allowed;
34747 fd->super_call_allowed = b->super_call_allowed;
34748 fd->super_allowed = b->super_allowed;
34749 fd->arguments_allowed = b->arguments_allowed;
34750 } else {
34751 fd->new_target_allowed = FALSE;
34752 fd->super_call_allowed = FALSE;
34753 fd->super_allowed = FALSE;
34754 fd->arguments_allowed = TRUE;
34755 }
34756 fd->js_mode = js_mode;
34757 fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
34758 if (b) {
34759 if (add_closure_variables(ctx, fd, b, scope_idx))
34760 goto fail;
34761 }
34762 fd->module = m;
34763 if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) {
34764 fd->in_function_body = TRUE;
34765 fd->func_kind = JS_FUNC_ASYNC;
34766 }
34767 s->is_module = (m != NULL);
34768 s->allow_html_comments = !s->is_module;
34769
34770 push_scope(s); /* body scope */
34771 fd->body_scope = fd->scope_level;
34772
34773 err = js_parse_program(s);
34774 if (err) {
34775 fail:
34776 free_token(s, &s->token);
34777 js_free_function_def(ctx, fd);
34778 goto fail1;
34779 }
34780
34781 if (m != NULL)
34782 m->has_tla = fd->has_await;
34783
34784 /* create the function object and all the enclosed functions */
34785 fun_obj = js_create_function(ctx, fd);
34786 if (JS_IsException(fun_obj))
34787 goto fail1;
34788 /* Could add a flag to avoid resolution if necessary */
34789 if (m) {
34790 m->func_obj = fun_obj;
34791 if (js_resolve_module(ctx, m) < 0)
34792 goto fail1;
34793 fun_obj = JS_NewModuleValue(ctx, m);
34794 }
34795 if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
34796 ret_val = fun_obj;
34797 } else {
34798 ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
34799 }
34800 return ret_val;
34801 fail1:
34802 /* XXX: should free all the unresolved dependencies */
34803 if (m)
34804 js_free_module_def(ctx, m);
34805 return JS_EXCEPTION;
34806}
34807
34808/* the indirection is needed to make 'eval' optional */
34809static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
34810 const char *input, size_t input_len,
34811 const char *filename, int flags, int scope_idx)
34812{
34813 BOOL backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
34814 int saved_js_mode = 0;
34815 JSValue ret;
34816
34817 if (unlikely(!ctx->eval_internal)) {
34818 return JS_ThrowTypeError(ctx, "eval is not supported");
34819 }
34820 if (backtrace_barrier && ctx->rt->current_stack_frame) {
34821 saved_js_mode = ctx->rt->current_stack_frame->js_mode;
34822 ctx->rt->current_stack_frame->js_mode |= JS_MODE_BACKTRACE_BARRIER;
34823 }
34824 ret = ctx->eval_internal(ctx, this_obj, input, input_len, filename,
34825 flags, scope_idx);
34826 if (backtrace_barrier && ctx->rt->current_stack_frame)
34827 ctx->rt->current_stack_frame->js_mode = saved_js_mode;
34828 return ret;
34829}
34830
34831static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
34832 JSValueConst val, int flags, int scope_idx)
34833{
34834 JSValue ret;
34835 const char *str;
34836 size_t len;
34837
34838 if (!JS_IsString(val))
34839 return JS_DupValue(ctx, val);
34840 str = JS_ToCStringLen(ctx, &len, val);
34841 if (!str)
34842 return JS_EXCEPTION;
34843 ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
34844 JS_FreeCString(ctx, str);
34845 return ret;
34846
34847}
34848
34849JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
34850 const char *input, size_t input_len,
34851 const char *filename, int eval_flags)
34852{
34853 int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
34854 JSValue ret;
34855
34856 assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
34857 eval_type == JS_EVAL_TYPE_MODULE);
34858 ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
34859 eval_flags, -1);
34860 return ret;
34861}
34862
34863JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
34864 const char *filename, int eval_flags)
34865{
34866 return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
34867 eval_flags);
34868}
34869
34870int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
34871{
34872 if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
34873 JSModuleDef *m = JS_VALUE_GET_PTR(obj);
34874 if (js_resolve_module(ctx, m) < 0) {
34875 js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
34876 return -1;
34877 }
34878 }
34879 return 0;
34880}
34881
34882/*******************************************************************/
34883/* object list */
34884
34885typedef struct {
34886 JSObject *obj;
34887 uint32_t hash_next; /* -1 if no next entry */
34888} JSObjectListEntry;
34889
34890/* XXX: reuse it to optimize weak references */
34891typedef struct {
34892 JSObjectListEntry *object_tab;
34893 int object_count;
34894 int object_size;
34895 uint32_t *hash_table;
34896 uint32_t hash_size;
34897} JSObjectList;
34898
34899static void js_object_list_init(JSObjectList *s)
34900{
34901 memset(s, 0, sizeof(*s));
34902}
34903
34904static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
34905{
34906 return ((uintptr_t)p * 3163) & (hash_size - 1);
34907}
34908
34909static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
34910 uint32_t new_hash_size)
34911{
34912 JSObjectListEntry *e;
34913 uint32_t i, h, *new_hash_table;
34914
34915 new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
34916 if (!new_hash_table)
34917 return -1;
34918 js_free(ctx, s->hash_table);
34919 s->hash_table = new_hash_table;
34920 s->hash_size = new_hash_size;
34921
34922 for(i = 0; i < s->hash_size; i++) {
34923 s->hash_table[i] = -1;
34924 }
34925 for(i = 0; i < s->object_count; i++) {
34926 e = &s->object_tab[i];
34927 h = js_object_list_get_hash(e->obj, s->hash_size);
34928 e->hash_next = s->hash_table[h];
34929 s->hash_table[h] = i;
34930 }
34931 return 0;
34932}
34933
34934/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
34935 memory error */
34936static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
34937{
34938 JSObjectListEntry *e;
34939 uint32_t h, new_hash_size;
34940
34941 if (js_resize_array(ctx, (void *)&s->object_tab,
34942 sizeof(s->object_tab[0]),
34943 &s->object_size, s->object_count + 1))
34944 return -1;
34945 if (unlikely((s->object_count + 1) >= s->hash_size)) {
34946 new_hash_size = max_uint32(s->hash_size, 4);
34947 while (new_hash_size <= s->object_count)
34948 new_hash_size *= 2;
34949 if (js_object_list_resize_hash(ctx, s, new_hash_size))
34950 return -1;
34951 }
34952 e = &s->object_tab[s->object_count++];
34953 h = js_object_list_get_hash(obj, s->hash_size);
34954 e->obj = obj;
34955 e->hash_next = s->hash_table[h];
34956 s->hash_table[h] = s->object_count - 1;
34957 return 0;
34958}
34959
34960/* return -1 if not present or the object index */
34961static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
34962{
34963 JSObjectListEntry *e;
34964 uint32_t h, p;
34965
34966 /* must test empty size because there is no hash table */
34967 if (s->object_count == 0)
34968 return -1;
34969 h = js_object_list_get_hash(obj, s->hash_size);
34970 p = s->hash_table[h];
34971 while (p != -1) {
34972 e = &s->object_tab[p];
34973 if (e->obj == obj)
34974 return p;
34975 p = e->hash_next;
34976 }
34977 return -1;
34978}
34979
34980static void js_object_list_end(JSContext *ctx, JSObjectList *s)
34981{
34982 js_free(ctx, s->object_tab);
34983 js_free(ctx, s->hash_table);
34984}
34985
34986/*******************************************************************/
34987/* binary object writer & reader */
34988
34989typedef enum BCTagEnum {
34990 BC_TAG_NULL = 1,
34991 BC_TAG_UNDEFINED,
34992 BC_TAG_BOOL_FALSE,
34993 BC_TAG_BOOL_TRUE,
34994 BC_TAG_INT32,
34995 BC_TAG_FLOAT64,
34996 BC_TAG_STRING,
34997 BC_TAG_OBJECT,
34998 BC_TAG_ARRAY,
34999 BC_TAG_BIG_INT,
35000 BC_TAG_TEMPLATE_OBJECT,
35001 BC_TAG_FUNCTION_BYTECODE,
35002 BC_TAG_MODULE,
35003 BC_TAG_TYPED_ARRAY,
35004 BC_TAG_ARRAY_BUFFER,
35005 BC_TAG_SHARED_ARRAY_BUFFER,
35006 BC_TAG_DATE,
35007 BC_TAG_OBJECT_VALUE,
35008 BC_TAG_OBJECT_REFERENCE,
35009} BCTagEnum;
35010
35011#define BC_VERSION 4
35012
35013typedef struct BCWriterState {
35014 JSContext *ctx;
35015 DynBuf dbuf;
35016 BOOL allow_bytecode : 8;
35017 BOOL allow_sab : 8;
35018 BOOL allow_reference : 8;
35019 uint32_t first_atom;
35020 uint32_t *atom_to_idx;
35021 int atom_to_idx_size;
35022 JSAtom *idx_to_atom;
35023 int idx_to_atom_count;
35024 int idx_to_atom_size;
35025 uint8_t **sab_tab;
35026 int sab_tab_len;
35027 int sab_tab_size;
35028 /* list of referenced objects (used if allow_reference = TRUE) */
35029 JSObjectList object_list;
35030} BCWriterState;
35031
35032#ifdef DUMP_READ_OBJECT
35033static const char * const bc_tag_str[] = {
35034 "invalid",
35035 "null",
35036 "undefined",
35037 "false",
35038 "true",
35039 "int32",
35040 "float64",
35041 "string",
35042 "object",
35043 "array",
35044 "bigint",
35045 "template",
35046 "function",
35047 "module",
35048 "TypedArray",
35049 "ArrayBuffer",
35050 "SharedArrayBuffer",
35051 "Date",
35052 "ObjectValue",
35053 "ObjectReference",
35054};
35055#endif
35056
35057static inline BOOL is_be(void)
35058{
35059 union {
35060 uint16_t a;
35061 uint8_t b;
35062 } u = {0x100};
35063 return u.b;
35064}
35065
35066static void bc_put_u8(BCWriterState *s, uint8_t v)
35067{
35068 dbuf_putc(&s->dbuf, v);
35069}
35070
35071static void bc_put_u16(BCWriterState *s, uint16_t v)
35072{
35073 if (is_be())
35074 v = bswap16(v);
35075 dbuf_put_u16(&s->dbuf, v);
35076}
35077
35078static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
35079{
35080 if (is_be())
35081 v = bswap32(v);
35082 dbuf_put_u32(&s->dbuf, v);
35083}
35084
35085static void bc_put_u64(BCWriterState *s, uint64_t v)
35086{
35087 if (is_be())
35088 v = bswap64(v);
35089 dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
35090}
35091
35092static void bc_put_leb128(BCWriterState *s, uint32_t v)
35093{
35094 dbuf_put_leb128(&s->dbuf, v);
35095}
35096
35097static void bc_put_sleb128(BCWriterState *s, int32_t v)
35098{
35099 dbuf_put_sleb128(&s->dbuf, v);
35100}
35101
35102static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
35103{
35104 *pflags = *pflags | (val << *pidx);
35105 *pidx += n;
35106}
35107
35108static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
35109{
35110 uint32_t v;
35111
35112 if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
35113 *pres = atom;
35114 return 0;
35115 }
35116 atom -= s->first_atom;
35117 if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
35118 *pres = s->atom_to_idx[atom];
35119 return 0;
35120 }
35121 if (atom >= s->atom_to_idx_size) {
35122 int old_size, i;
35123 old_size = s->atom_to_idx_size;
35124 if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
35125 sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
35126 atom + 1))
35127 return -1;
35128 /* XXX: could add a specific js_resize_array() function to do it */
35129 for(i = old_size; i < s->atom_to_idx_size; i++)
35130 s->atom_to_idx[i] = 0;
35131 }
35132 if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
35133 sizeof(s->idx_to_atom[0]),
35134 &s->idx_to_atom_size, s->idx_to_atom_count + 1))
35135 goto fail;
35136
35137 v = s->idx_to_atom_count++;
35138 s->idx_to_atom[v] = atom + s->first_atom;
35139 v += s->first_atom;
35140 s->atom_to_idx[atom] = v;
35141 *pres = v;
35142 return 0;
35143 fail:
35144 *pres = 0;
35145 return -1;
35146}
35147
35148static int bc_put_atom(BCWriterState *s, JSAtom atom)
35149{
35150 uint32_t v;
35151
35152 if (__JS_AtomIsTaggedInt(atom)) {
35153 v = (__JS_AtomToUInt32(atom) << 1) | 1;
35154 } else {
35155 if (bc_atom_to_idx(s, &v, atom))
35156 return -1;
35157 v <<= 1;
35158 }
35159 bc_put_leb128(s, v);
35160 return 0;
35161}
35162
35163static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
35164{
35165 int pos, len, op, fmt;
35166
35167 pos = 0;
35168 while (pos < bc_len) {
35169 op = bc_buf[pos];
35170 len = short_opcode_info(op).size;
35171 fmt = short_opcode_info(op).fmt;
35172 switch(fmt) {
35173 case OP_FMT_u16:
35174 case OP_FMT_i16:
35175 case OP_FMT_label16:
35176 case OP_FMT_npop:
35177 case OP_FMT_loc:
35178 case OP_FMT_arg:
35179 case OP_FMT_var_ref:
35180 put_u16(bc_buf + pos + 1,
35181 bswap16(get_u16(bc_buf + pos + 1)));
35182 break;
35183 case OP_FMT_i32:
35184 case OP_FMT_u32:
35185 case OP_FMT_const:
35186 case OP_FMT_label:
35187 case OP_FMT_atom:
35188 case OP_FMT_atom_u8:
35189 put_u32(bc_buf + pos + 1,
35190 bswap32(get_u32(bc_buf + pos + 1)));
35191 break;
35192 case OP_FMT_atom_u16:
35193 case OP_FMT_label_u16:
35194 put_u32(bc_buf + pos + 1,
35195 bswap32(get_u32(bc_buf + pos + 1)));
35196 put_u16(bc_buf + pos + 1 + 4,
35197 bswap16(get_u16(bc_buf + pos + 1 + 4)));
35198 break;
35199 case OP_FMT_atom_label_u8:
35200 case OP_FMT_atom_label_u16:
35201 put_u32(bc_buf + pos + 1,
35202 bswap32(get_u32(bc_buf + pos + 1)));
35203 put_u32(bc_buf + pos + 1 + 4,
35204 bswap32(get_u32(bc_buf + pos + 1 + 4)));
35205 if (fmt == OP_FMT_atom_label_u16) {
35206 put_u16(bc_buf + pos + 1 + 4 + 4,
35207 bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
35208 }
35209 break;
35210 case OP_FMT_npop_u16:
35211 put_u16(bc_buf + pos + 1,
35212 bswap16(get_u16(bc_buf + pos + 1)));
35213 put_u16(bc_buf + pos + 1 + 2,
35214 bswap16(get_u16(bc_buf + pos + 1 + 2)));
35215 break;
35216 default:
35217 break;
35218 }
35219 pos += len;
35220 }
35221}
35222
35223static int JS_WriteFunctionBytecode(BCWriterState *s,
35224 const uint8_t *bc_buf1, int bc_len)
35225{
35226 int pos, len, op;
35227 JSAtom atom;
35228 uint8_t *bc_buf;
35229 uint32_t val;
35230
35231 bc_buf = js_malloc(s->ctx, bc_len);
35232 if (!bc_buf)
35233 return -1;
35234 memcpy(bc_buf, bc_buf1, bc_len);
35235
35236 pos = 0;
35237 while (pos < bc_len) {
35238 op = bc_buf[pos];
35239 len = short_opcode_info(op).size;
35240 switch(short_opcode_info(op).fmt) {
35241 case OP_FMT_atom:
35242 case OP_FMT_atom_u8:
35243 case OP_FMT_atom_u16:
35244 case OP_FMT_atom_label_u8:
35245 case OP_FMT_atom_label_u16:
35246 atom = get_u32(bc_buf + pos + 1);
35247 if (bc_atom_to_idx(s, &val, atom))
35248 goto fail;
35249 put_u32(bc_buf + pos + 1, val);
35250 break;
35251 default:
35252 break;
35253 }
35254 pos += len;
35255 }
35256
35257 if (is_be())
35258 bc_byte_swap(bc_buf, bc_len);
35259
35260 dbuf_put(&s->dbuf, bc_buf, bc_len);
35261
35262 js_free(s->ctx, bc_buf);
35263 return 0;
35264 fail:
35265 js_free(s->ctx, bc_buf);
35266 return -1;
35267}
35268
35269static void JS_WriteString(BCWriterState *s, JSString *p)
35270{
35271 int i;
35272 bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
35273 if (p->is_wide_char) {
35274 for(i = 0; i < p->len; i++)
35275 bc_put_u16(s, p->u.str16[i]);
35276 } else {
35277 dbuf_put(&s->dbuf, p->u.str8, p->len);
35278 }
35279}
35280
35281static int JS_WriteBigInt(BCWriterState *s, JSValueConst obj)
35282{
35283 JSBigIntBuf buf;
35284 JSBigInt *p;
35285 uint32_t len, i;
35286 js_limb_t v, b;
35287 int shift;
35288
35289 bc_put_u8(s, BC_TAG_BIG_INT);
35290
35291 if (JS_VALUE_GET_TAG(obj) == JS_TAG_SHORT_BIG_INT)
35292 p = js_bigint_set_short(&buf, obj);
35293 else
35294 p = JS_VALUE_GET_PTR(obj);
35295 if (p->len == 1 && p->tab[0] == 0) {
35296 /* zero case */
35297 len = 0;
35298 } else {
35299 /* compute the length of the two's complement representation
35300 in bytes */
35301 len = p->len * (JS_LIMB_BITS / 8);
35302 v = p->tab[p->len - 1];
35303 shift = JS_LIMB_BITS - 8;
35304 while (shift > 0) {
35305 b = (v >> shift) & 0xff;
35306 if (b != 0x00 && b != 0xff)
35307 break;
35308 if ((b & 1) != ((v >> (shift - 1)) & 1))
35309 break;
35310 shift -= 8;
35311 len--;
35312 }
35313 }
35314 bc_put_leb128(s, len);
35315 if (len > 0) {
35316 for(i = 0; i < (len / (JS_LIMB_BITS / 8)); i++) {
35317#if JS_LIMB_BITS == 32
35318 bc_put_u32(s, p->tab[i]);
35319#else
35320 bc_put_u64(s, p->tab[i]);
35321#endif
35322 }
35323 for(i = 0; i < len % (JS_LIMB_BITS / 8); i++) {
35324 bc_put_u8(s, (p->tab[p->len - 1] >> (i * 8)) & 0xff);
35325 }
35326 }
35327 return 0;
35328}
35329
35330static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
35331
35332static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
35333{
35334 JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
35335 uint32_t flags;
35336 int idx, i;
35337
35338 bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
35339 flags = idx = 0;
35340 bc_set_flags(&flags, &idx, b->has_prototype, 1);
35341 bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
35342 bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
35343 bc_set_flags(&flags, &idx, b->need_home_object, 1);
35344 bc_set_flags(&flags, &idx, b->func_kind, 2);
35345 bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
35346 bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
35347 bc_set_flags(&flags, &idx, b->super_allowed, 1);
35348 bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
35349 bc_set_flags(&flags, &idx, b->has_debug, 1);
35350 bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1);
35351 assert(idx <= 16);
35352 bc_put_u16(s, flags);
35353 bc_put_u8(s, b->js_mode);
35354 bc_put_atom(s, b->func_name);
35355
35356 bc_put_leb128(s, b->arg_count);
35357 bc_put_leb128(s, b->var_count);
35358 bc_put_leb128(s, b->defined_arg_count);
35359 bc_put_leb128(s, b->stack_size);
35360 bc_put_leb128(s, b->closure_var_count);
35361 bc_put_leb128(s, b->cpool_count);
35362 bc_put_leb128(s, b->byte_code_len);
35363 if (b->vardefs) {
35364 /* XXX: this field is redundant */
35365 bc_put_leb128(s, b->arg_count + b->var_count);
35366 for(i = 0; i < b->arg_count + b->var_count; i++) {
35367 JSVarDef *vd = &b->vardefs[i];
35368 bc_put_atom(s, vd->var_name);
35369 bc_put_leb128(s, vd->scope_level);
35370 bc_put_leb128(s, vd->scope_next + 1);
35371 flags = idx = 0;
35372 bc_set_flags(&flags, &idx, vd->var_kind, 4);
35373 bc_set_flags(&flags, &idx, vd->is_const, 1);
35374 bc_set_flags(&flags, &idx, vd->is_lexical, 1);
35375 bc_set_flags(&flags, &idx, vd->is_captured, 1);
35376 assert(idx <= 8);
35377 bc_put_u8(s, flags);
35378 }
35379 } else {
35380 bc_put_leb128(s, 0);
35381 }
35382
35383 for(i = 0; i < b->closure_var_count; i++) {
35384 JSClosureVar *cv = &b->closure_var[i];
35385 bc_put_atom(s, cv->var_name);
35386 bc_put_leb128(s, cv->var_idx);
35387 flags = idx = 0;
35388 bc_set_flags(&flags, &idx, cv->is_local, 1);
35389 bc_set_flags(&flags, &idx, cv->is_arg, 1);
35390 bc_set_flags(&flags, &idx, cv->is_const, 1);
35391 bc_set_flags(&flags, &idx, cv->is_lexical, 1);
35392 bc_set_flags(&flags, &idx, cv->var_kind, 4);
35393 assert(idx <= 8);
35394 bc_put_u8(s, flags);
35395 }
35396
35397 if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
35398 goto fail;
35399
35400 if (b->has_debug) {
35401 bc_put_atom(s, b->debug.filename);
35402 bc_put_leb128(s, b->debug.pc2line_len);
35403 dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
35404 if (b->debug.source) {
35405 bc_put_leb128(s, b->debug.source_len);
35406 dbuf_put(&s->dbuf, (uint8_t *)b->debug.source, b->debug.source_len);
35407 } else {
35408 bc_put_leb128(s, 0);
35409 }
35410 }
35411
35412 for(i = 0; i < b->cpool_count; i++) {
35413 if (JS_WriteObjectRec(s, b->cpool[i]))
35414 goto fail;
35415 }
35416 return 0;
35417 fail:
35418 return -1;
35419}
35420
35421static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
35422{
35423 JSModuleDef *m = JS_VALUE_GET_PTR(obj);
35424 int i;
35425
35426 bc_put_u8(s, BC_TAG_MODULE);
35427 bc_put_atom(s, m->module_name);
35428
35429 bc_put_leb128(s, m->req_module_entries_count);
35430 for(i = 0; i < m->req_module_entries_count; i++) {
35431 JSReqModuleEntry *rme = &m->req_module_entries[i];
35432 bc_put_atom(s, rme->module_name);
35433 }
35434
35435 bc_put_leb128(s, m->export_entries_count);
35436 for(i = 0; i < m->export_entries_count; i++) {
35437 JSExportEntry *me = &m->export_entries[i];
35438 bc_put_u8(s, me->export_type);
35439 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
35440 bc_put_leb128(s, me->u.local.var_idx);
35441 } else {
35442 bc_put_leb128(s, me->u.req_module_idx);
35443 bc_put_atom(s, me->local_name);
35444 }
35445 bc_put_atom(s, me->export_name);
35446 }
35447
35448 bc_put_leb128(s, m->star_export_entries_count);
35449 for(i = 0; i < m->star_export_entries_count; i++) {
35450 JSStarExportEntry *se = &m->star_export_entries[i];
35451 bc_put_leb128(s, se->req_module_idx);
35452 }
35453
35454 bc_put_leb128(s, m->import_entries_count);
35455 for(i = 0; i < m->import_entries_count; i++) {
35456 JSImportEntry *mi = &m->import_entries[i];
35457 bc_put_leb128(s, mi->var_idx);
35458 bc_put_atom(s, mi->import_name);
35459 bc_put_leb128(s, mi->req_module_idx);
35460 }
35461
35462 bc_put_u8(s, m->has_tla);
35463
35464 if (JS_WriteObjectRec(s, m->func_obj))
35465 goto fail;
35466 return 0;
35467 fail:
35468 return -1;
35469}
35470
35471static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
35472{
35473 JSObject *p = JS_VALUE_GET_OBJ(obj);
35474 uint32_t i, len;
35475 JSValue val;
35476 int ret;
35477 BOOL is_template;
35478
35479 if (s->allow_bytecode && !p->extensible) {
35480 /* not extensible array: we consider it is a
35481 template when we are saving bytecode */
35482 bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
35483 is_template = TRUE;
35484 } else {
35485 bc_put_u8(s, BC_TAG_ARRAY);
35486 is_template = FALSE;
35487 }
35488 if (js_get_length32(s->ctx, &len, obj))
35489 goto fail1;
35490 bc_put_leb128(s, len);
35491 for(i = 0; i < len; i++) {
35492 val = JS_GetPropertyUint32(s->ctx, obj, i);
35493 if (JS_IsException(val))
35494 goto fail1;
35495 ret = JS_WriteObjectRec(s, val);
35496 JS_FreeValue(s->ctx, val);
35497 if (ret)
35498 goto fail1;
35499 }
35500 if (is_template) {
35501 val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
35502 if (JS_IsException(val))
35503 goto fail1;
35504 ret = JS_WriteObjectRec(s, val);
35505 JS_FreeValue(s->ctx, val);
35506 if (ret)
35507 goto fail1;
35508 }
35509 return 0;
35510 fail1:
35511 return -1;
35512}
35513
35514static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
35515{
35516 JSObject *p = JS_VALUE_GET_OBJ(obj);
35517 uint32_t i, prop_count;
35518 JSShape *sh;
35519 JSShapeProperty *pr;
35520 int pass;
35521 JSAtom atom;
35522
35523 bc_put_u8(s, BC_TAG_OBJECT);
35524 prop_count = 0;
35525 sh = p->shape;
35526 for(pass = 0; pass < 2; pass++) {
35527 if (pass == 1)
35528 bc_put_leb128(s, prop_count);
35529 for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
35530 atom = pr->atom;
35531 if (atom != JS_ATOM_NULL &&
35532 JS_AtomIsString(s->ctx, atom) &&
35533 (pr->flags & JS_PROP_ENUMERABLE)) {
35534 if (pr->flags & JS_PROP_TMASK) {
35535 JS_ThrowTypeError(s->ctx, "only value properties are supported");
35536 goto fail;
35537 }
35538 if (pass == 0) {
35539 prop_count++;
35540 } else {
35541 bc_put_atom(s, atom);
35542 if (JS_WriteObjectRec(s, p->prop[i].u.value))
35543 goto fail;
35544 }
35545 }
35546 }
35547 }
35548 return 0;
35549 fail:
35550 return -1;
35551}
35552
35553static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
35554{
35555 JSObject *p = JS_VALUE_GET_OBJ(obj);
35556 JSTypedArray *ta = p->u.typed_array;
35557
35558 bc_put_u8(s, BC_TAG_TYPED_ARRAY);
35559 bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
35560 bc_put_leb128(s, p->u.array.count);
35561 bc_put_leb128(s, ta->offset);
35562 if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
35563 return -1;
35564 return 0;
35565}
35566
35567static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
35568{
35569 JSObject *p = JS_VALUE_GET_OBJ(obj);
35570 JSArrayBuffer *abuf = p->u.array_buffer;
35571 if (abuf->detached) {
35572 JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
35573 return -1;
35574 }
35575 bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
35576 bc_put_leb128(s, abuf->byte_length);
35577 dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
35578 return 0;
35579}
35580
35581static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
35582{
35583 JSObject *p = JS_VALUE_GET_OBJ(obj);
35584 JSArrayBuffer *abuf = p->u.array_buffer;
35585 assert(!abuf->detached); /* SharedArrayBuffer are never detached */
35586 bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
35587 bc_put_leb128(s, abuf->byte_length);
35588 bc_put_u64(s, (uintptr_t)abuf->data);
35589 if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
35590 &s->sab_tab_size, s->sab_tab_len + 1))
35591 return -1;
35592 /* keep the SAB pointer so that the user can clone it or free it */
35593 s->sab_tab[s->sab_tab_len++] = abuf->data;
35594 return 0;
35595}
35596
35597static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
35598{
35599 uint32_t tag;
35600
35601 if (js_check_stack_overflow(s->ctx->rt, 0)) {
35602 JS_ThrowStackOverflow(s->ctx);
35603 return -1;
35604 }
35605
35606 tag = JS_VALUE_GET_NORM_TAG(obj);
35607 switch(tag) {
35608 case JS_TAG_NULL:
35609 bc_put_u8(s, BC_TAG_NULL);
35610 break;
35611 case JS_TAG_UNDEFINED:
35612 bc_put_u8(s, BC_TAG_UNDEFINED);
35613 break;
35614 case JS_TAG_BOOL:
35615 bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
35616 break;
35617 case JS_TAG_INT:
35618 bc_put_u8(s, BC_TAG_INT32);
35619 bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
35620 break;
35621 case JS_TAG_FLOAT64:
35622 {
35623 JSFloat64Union u;
35624 bc_put_u8(s, BC_TAG_FLOAT64);
35625 u.d = JS_VALUE_GET_FLOAT64(obj);
35626 bc_put_u64(s, u.u64);
35627 }
35628 break;
35629 case JS_TAG_STRING:
35630 {
35631 JSString *p = JS_VALUE_GET_STRING(obj);
35632 bc_put_u8(s, BC_TAG_STRING);
35633 JS_WriteString(s, p);
35634 }
35635 break;
35636 case JS_TAG_STRING_ROPE:
35637 {
35638 JSValue str;
35639 str = JS_ToString(s->ctx, obj);
35640 if (JS_IsException(str))
35641 goto fail;
35642 JS_WriteObjectRec(s, str);
35643 JS_FreeValue(s->ctx, str);
35644 }
35645 break;
35646 case JS_TAG_FUNCTION_BYTECODE:
35647 if (!s->allow_bytecode)
35648 goto invalid_tag;
35649 if (JS_WriteFunctionTag(s, obj))
35650 goto fail;
35651 break;
35652 case JS_TAG_MODULE:
35653 if (!s->allow_bytecode)
35654 goto invalid_tag;
35655 if (JS_WriteModule(s, obj))
35656 goto fail;
35657 break;
35658 case JS_TAG_OBJECT:
35659 {
35660 JSObject *p = JS_VALUE_GET_OBJ(obj);
35661 int ret, idx;
35662
35663 if (s->allow_reference) {
35664 idx = js_object_list_find(s->ctx, &s->object_list, p);
35665 if (idx >= 0) {
35666 bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
35667 bc_put_leb128(s, idx);
35668 break;
35669 } else {
35670 if (js_object_list_add(s->ctx, &s->object_list, p))
35671 goto fail;
35672 }
35673 } else {
35674 if (p->tmp_mark) {
35675 JS_ThrowTypeError(s->ctx, "circular reference");
35676 goto fail;
35677 }
35678 p->tmp_mark = 1;
35679 }
35680 switch(p->class_id) {
35681 case JS_CLASS_ARRAY:
35682 ret = JS_WriteArray(s, obj);
35683 break;
35684 case JS_CLASS_OBJECT:
35685 ret = JS_WriteObjectTag(s, obj);
35686 break;
35687 case JS_CLASS_ARRAY_BUFFER:
35688 ret = JS_WriteArrayBuffer(s, obj);
35689 break;
35690 case JS_CLASS_SHARED_ARRAY_BUFFER:
35691 if (!s->allow_sab)
35692 goto invalid_tag;
35693 ret = JS_WriteSharedArrayBuffer(s, obj);
35694 break;
35695 case JS_CLASS_DATE:
35696 bc_put_u8(s, BC_TAG_DATE);
35697 ret = JS_WriteObjectRec(s, p->u.object_data);
35698 break;
35699 case JS_CLASS_NUMBER:
35700 case JS_CLASS_STRING:
35701 case JS_CLASS_BOOLEAN:
35702 case JS_CLASS_BIG_INT:
35703 bc_put_u8(s, BC_TAG_OBJECT_VALUE);
35704 ret = JS_WriteObjectRec(s, p->u.object_data);
35705 break;
35706 default:
35707 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
35708 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
35709 ret = JS_WriteTypedArray(s, obj);
35710 } else {
35711 JS_ThrowTypeError(s->ctx, "unsupported object class");
35712 ret = -1;
35713 }
35714 break;
35715 }
35716 p->tmp_mark = 0;
35717 if (ret)
35718 goto fail;
35719 }
35720 break;
35721 case JS_TAG_SHORT_BIG_INT:
35722 case JS_TAG_BIG_INT:
35723 if (JS_WriteBigInt(s, obj))
35724 goto fail;
35725 break;
35726 default:
35727 invalid_tag:
35728 JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
35729 goto fail;
35730 }
35731 return 0;
35732
35733 fail:
35734 return -1;
35735}
35736
35737/* create the atom table */
35738static int JS_WriteObjectAtoms(BCWriterState *s)
35739{
35740 JSRuntime *rt = s->ctx->rt;
35741 DynBuf dbuf1;
35742 int i, atoms_size;
35743
35744 dbuf1 = s->dbuf;
35745 js_dbuf_init(s->ctx, &s->dbuf);
35746 bc_put_u8(s, BC_VERSION);
35747
35748 bc_put_leb128(s, s->idx_to_atom_count);
35749 for(i = 0; i < s->idx_to_atom_count; i++) {
35750 JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
35751 JS_WriteString(s, p);
35752 }
35753 /* XXX: should check for OOM in above phase */
35754
35755 /* move the atoms at the start */
35756 /* XXX: could just append dbuf1 data, but it uses more memory if
35757 dbuf1 is larger than dbuf */
35758 atoms_size = s->dbuf.size;
35759 if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
35760 goto fail;
35761 memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
35762 memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
35763 dbuf1.size += atoms_size;
35764 dbuf_free(&s->dbuf);
35765 s->dbuf = dbuf1;
35766 return 0;
35767 fail:
35768 dbuf_free(&dbuf1);
35769 return -1;
35770}
35771
35772uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
35773 int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
35774{
35775 BCWriterState ss, *s = &ss;
35776
35777 memset(s, 0, sizeof(*s));
35778 s->ctx = ctx;
35779 s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
35780 s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
35781 s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
35782 /* XXX: could use a different version when bytecode is included */
35783 if (s->allow_bytecode)
35784 s->first_atom = JS_ATOM_END;
35785 else
35786 s->first_atom = 1;
35787 js_dbuf_init(ctx, &s->dbuf);
35788 js_object_list_init(&s->object_list);
35789
35790 if (JS_WriteObjectRec(s, obj))
35791 goto fail;
35792 if (JS_WriteObjectAtoms(s))
35793 goto fail;
35794 js_object_list_end(ctx, &s->object_list);
35795 js_free(ctx, s->atom_to_idx);
35796 js_free(ctx, s->idx_to_atom);
35797 *psize = s->dbuf.size;
35798 if (psab_tab)
35799 *psab_tab = s->sab_tab;
35800 if (psab_tab_len)
35801 *psab_tab_len = s->sab_tab_len;
35802 return s->dbuf.buf;
35803 fail:
35804 js_object_list_end(ctx, &s->object_list);
35805 js_free(ctx, s->atom_to_idx);
35806 js_free(ctx, s->idx_to_atom);
35807 dbuf_free(&s->dbuf);
35808 *psize = 0;
35809 if (psab_tab)
35810 *psab_tab = NULL;
35811 if (psab_tab_len)
35812 *psab_tab_len = 0;
35813 return NULL;
35814}
35815
35816uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
35817 int flags)
35818{
35819 return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
35820}
35821
35822typedef struct BCReaderState {
35823 JSContext *ctx;
35824 const uint8_t *buf_start, *ptr, *buf_end;
35825 uint32_t first_atom;
35826 uint32_t idx_to_atom_count;
35827 JSAtom *idx_to_atom;
35828 int error_state;
35829 BOOL allow_sab : 8;
35830 BOOL allow_bytecode : 8;
35831 BOOL is_rom_data : 8;
35832 BOOL allow_reference : 8;
35833 /* object references */
35834 JSObject **objects;
35835 int objects_count;
35836 int objects_size;
35837
35838#ifdef DUMP_READ_OBJECT
35839 const uint8_t *ptr_last;
35840 int level;
35841#endif
35842} BCReaderState;
35843
35844#ifdef DUMP_READ_OBJECT
35845static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
35846 va_list ap;
35847 int i, n, n0;
35848
35849 if (!s->ptr_last)
35850 s->ptr_last = s->buf_start;
35851
35852 n = n0 = 0;
35853 if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
35854 n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
35855 n += n0;
35856 }
35857 for (i = 0; s->ptr_last < s->ptr; i++) {
35858 if ((i & 7) == 0 && i > 0) {
35859 printf("\n%*s", n0, "");
35860 n = n0;
35861 }
35862 n += printf(" %02x", *s->ptr_last++);
35863 }
35864 if (*fmt == '}')
35865 s->level--;
35866 if (n < 32 + s->level * 2) {
35867 printf("%*s", 32 + s->level * 2 - n, "");
35868 }
35869 va_start(ap, fmt);
35870 vfprintf(stdout, fmt, ap);
35871 va_end(ap);
35872 if (strchr(fmt, '{'))
35873 s->level++;
35874}
35875#else
35876#define bc_read_trace(...)
35877#endif
35878
35879static int bc_read_error_end(BCReaderState *s)
35880{
35881 if (!s->error_state) {
35882 JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
35883 }
35884 return s->error_state = -1;
35885}
35886
35887static int bc_get_u8(BCReaderState *s, uint8_t *pval)
35888{
35889 if (unlikely(s->buf_end - s->ptr < 1)) {
35890 *pval = 0; /* avoid warning */
35891 return bc_read_error_end(s);
35892 }
35893 *pval = *s->ptr++;
35894 return 0;
35895}
35896
35897static int bc_get_u16(BCReaderState *s, uint16_t *pval)
35898{
35899 uint16_t v;
35900 if (unlikely(s->buf_end - s->ptr < 2)) {
35901 *pval = 0; /* avoid warning */
35902 return bc_read_error_end(s);
35903 }
35904 v = get_u16(s->ptr);
35905 if (is_be())
35906 v = bswap16(v);
35907 *pval = v;
35908 s->ptr += 2;
35909 return 0;
35910}
35911
35912static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
35913{
35914 uint32_t v;
35915 if (unlikely(s->buf_end - s->ptr < 4)) {
35916 *pval = 0; /* avoid warning */
35917 return bc_read_error_end(s);
35918 }
35919 v = get_u32(s->ptr);
35920 if (is_be())
35921 v = bswap32(v);
35922 *pval = v;
35923 s->ptr += 4;
35924 return 0;
35925}
35926
35927static int bc_get_u64(BCReaderState *s, uint64_t *pval)
35928{
35929 uint64_t v;
35930 if (unlikely(s->buf_end - s->ptr < 8)) {
35931 *pval = 0; /* avoid warning */
35932 return bc_read_error_end(s);
35933 }
35934 v = get_u64(s->ptr);
35935 if (is_be())
35936 v = bswap64(v);
35937 *pval = v;
35938 s->ptr += 8;
35939 return 0;
35940}
35941
35942static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
35943{
35944 int ret;
35945 ret = get_leb128(pval, s->ptr, s->buf_end);
35946 if (unlikely(ret < 0))
35947 return bc_read_error_end(s);
35948 s->ptr += ret;
35949 return 0;
35950}
35951
35952static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
35953{
35954 int ret;
35955 ret = get_sleb128(pval, s->ptr, s->buf_end);
35956 if (unlikely(ret < 0))
35957 return bc_read_error_end(s);
35958 s->ptr += ret;
35959 return 0;
35960}
35961
35962/* XXX: used to read an `int` with a positive value */
35963static int bc_get_leb128_int(BCReaderState *s, int *pval)
35964{
35965 return bc_get_leb128(s, (uint32_t *)pval);
35966}
35967
35968static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
35969{
35970 uint32_t val;
35971 if (bc_get_leb128(s, &val)) {
35972 *pval = 0;
35973 return -1;
35974 }
35975 *pval = val;
35976 return 0;
35977}
35978
35979static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
35980{
35981 if (buf_len != 0) {
35982 if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
35983 return bc_read_error_end(s);
35984 memcpy(buf, s->ptr, buf_len);
35985 s->ptr += buf_len;
35986 }
35987 return 0;
35988}
35989
35990static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
35991{
35992 JSAtom atom;
35993
35994 if (__JS_AtomIsTaggedInt(idx)) {
35995 atom = idx;
35996 } else if (idx < s->first_atom) {
35997 atom = JS_DupAtom(s->ctx, idx);
35998 } else {
35999 idx -= s->first_atom;
36000 if (idx >= s->idx_to_atom_count) {
36001 JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
36002 (unsigned int)(s->ptr - s->buf_start));
36003 *patom = JS_ATOM_NULL;
36004 return s->error_state = -1;
36005 }
36006 atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
36007 }
36008 *patom = atom;
36009 return 0;
36010}
36011
36012static int bc_get_atom(BCReaderState *s, JSAtom *patom)
36013{
36014 uint32_t v;
36015 if (bc_get_leb128(s, &v))
36016 return -1;
36017 if (v & 1) {
36018 *patom = __JS_AtomFromUInt32(v >> 1);
36019 return 0;
36020 } else {
36021 return bc_idx_to_atom(s, patom, v >> 1);
36022 }
36023}
36024
36025static JSString *JS_ReadString(BCReaderState *s)
36026{
36027 uint32_t len;
36028 size_t size;
36029 BOOL is_wide_char;
36030 JSString *p;
36031
36032 if (bc_get_leb128(s, &len))
36033 return NULL;
36034 is_wide_char = len & 1;
36035 len >>= 1;
36036 if (len > JS_STRING_LEN_MAX) {
36037 JS_ThrowInternalError(s->ctx, "string too long");
36038 return NULL;
36039 }
36040 p = js_alloc_string(s->ctx, len, is_wide_char);
36041 if (!p) {
36042 s->error_state = -1;
36043 return NULL;
36044 }
36045 size = (size_t)len << is_wide_char;
36046 if ((s->buf_end - s->ptr) < size) {
36047 bc_read_error_end(s);
36048 js_free_string(s->ctx->rt, p);
36049 return NULL;
36050 }
36051 memcpy(p->u.str8, s->ptr, size);
36052 s->ptr += size;
36053 if (is_wide_char) {
36054 if (is_be()) {
36055 uint32_t i;
36056 for (i = 0; i < len; i++)
36057 p->u.str16[i] = bswap16(p->u.str16[i]);
36058 }
36059 } else {
36060 p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
36061 }
36062#ifdef DUMP_READ_OBJECT
36063 JS_DumpString(s->ctx->rt, p); printf("\n");
36064#endif
36065 return p;
36066}
36067
36068static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
36069{
36070 uint32_t val;
36071 /* XXX: this does not work for n == 32 */
36072 val = (flags >> *pidx) & ((1U << n) - 1);
36073 *pidx += n;
36074 return val;
36075}
36076
36077static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
36078 int byte_code_offset, uint32_t bc_len)
36079{
36080 uint8_t *bc_buf;
36081 int pos, len, op;
36082 JSAtom atom;
36083 uint32_t idx;
36084
36085 if (s->is_rom_data) {
36086 /* directly use the input buffer */
36087 if (unlikely(s->buf_end - s->ptr < bc_len))
36088 return bc_read_error_end(s);
36089 bc_buf = (uint8_t *)s->ptr;
36090 s->ptr += bc_len;
36091 } else {
36092 bc_buf = (void *)((uint8_t*)b + byte_code_offset);
36093 if (bc_get_buf(s, bc_buf, bc_len))
36094 return -1;
36095 }
36096 b->byte_code_buf = bc_buf;
36097
36098 if (is_be())
36099 bc_byte_swap(bc_buf, bc_len);
36100
36101 pos = 0;
36102 while (pos < bc_len) {
36103 op = bc_buf[pos];
36104 len = short_opcode_info(op).size;
36105 switch(short_opcode_info(op).fmt) {
36106 case OP_FMT_atom:
36107 case OP_FMT_atom_u8:
36108 case OP_FMT_atom_u16:
36109 case OP_FMT_atom_label_u8:
36110 case OP_FMT_atom_label_u16:
36111 idx = get_u32(bc_buf + pos + 1);
36112 if (s->is_rom_data) {
36113 /* just increment the reference count of the atom */
36114 JS_DupAtom(s->ctx, (JSAtom)idx);
36115 } else {
36116 if (bc_idx_to_atom(s, &atom, idx)) {
36117 /* Note: the atoms will be freed up to this position */
36118 b->byte_code_len = pos;
36119 return -1;
36120 }
36121 put_u32(bc_buf + pos + 1, atom);
36122#ifdef DUMP_READ_OBJECT
36123 bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
36124#endif
36125 }
36126 break;
36127 default:
36128 break;
36129 }
36130 pos += len;
36131 }
36132 return 0;
36133}
36134
36135static JSValue JS_ReadBigInt(BCReaderState *s)
36136{
36137 JSValue obj = JS_UNDEFINED;
36138 uint32_t len, i, n;
36139 JSBigInt *p;
36140 js_limb_t v;
36141 uint8_t v8;
36142
36143 if (bc_get_leb128(s, &len))
36144 goto fail;
36145 bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
36146 if (len == 0) {
36147 /* zero case */
36148 bc_read_trace(s, "}\n");
36149 return __JS_NewShortBigInt(s->ctx, 0);
36150 }
36151 p = js_bigint_new(s->ctx, (len - 1) / (JS_LIMB_BITS / 8) + 1);
36152 if (!p)
36153 goto fail;
36154 for(i = 0; i < len / (JS_LIMB_BITS / 8); i++) {
36155#if JS_LIMB_BITS == 32
36156 if (bc_get_u32(s, &v))
36157 goto fail;
36158#else
36159 if (bc_get_u64(s, &v))
36160 goto fail;
36161#endif
36162 p->tab[i] = v;
36163 }
36164 n = len % (JS_LIMB_BITS / 8);
36165 if (n != 0) {
36166 int shift;
36167 v = 0;
36168 for(i = 0; i < n; i++) {
36169 if (bc_get_u8(s, &v8))
36170 goto fail;
36171 v |= (js_limb_t)v8 << (i * 8);
36172 }
36173 shift = JS_LIMB_BITS - n * 8;
36174 /* extend the sign */
36175 if (shift != 0) {
36176 v = (js_slimb_t)(v << shift) >> shift;
36177 }
36178 p->tab[p->len - 1] = v;
36179 }
36180 bc_read_trace(s, "}\n");
36181 return JS_CompactBigInt(s->ctx, p);
36182 fail:
36183 JS_FreeValue(s->ctx, obj);
36184 return JS_EXCEPTION;
36185}
36186
36187static JSValue JS_ReadObjectRec(BCReaderState *s);
36188
36189static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
36190{
36191 if (s->allow_reference) {
36192 if (js_resize_array(s->ctx, (void *)&s->objects,
36193 sizeof(s->objects[0]),
36194 &s->objects_size, s->objects_count + 1))
36195 return -1;
36196 s->objects[s->objects_count++] = p;
36197 }
36198 return 0;
36199}
36200
36201static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
36202{
36203 return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
36204}
36205
36206static JSValue JS_ReadFunctionTag(BCReaderState *s)
36207{
36208 JSContext *ctx = s->ctx;
36209 JSFunctionBytecode bc, *b;
36210 JSValue obj = JS_UNDEFINED;
36211 uint16_t v16;
36212 uint8_t v8;
36213 int idx, i, local_count;
36214 int function_size, cpool_offset, byte_code_offset;
36215 int closure_var_offset, vardefs_offset;
36216
36217 memset(&bc, 0, sizeof(bc));
36218 bc.header.ref_count = 1;
36219 //bc.gc_header.mark = 0;
36220
36221 if (bc_get_u16(s, &v16))
36222 goto fail;
36223 idx = 0;
36224 bc.has_prototype = bc_get_flags(v16, &idx, 1);
36225 bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
36226 bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
36227 bc.need_home_object = bc_get_flags(v16, &idx, 1);
36228 bc.func_kind = bc_get_flags(v16, &idx, 2);
36229 bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
36230 bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
36231 bc.super_allowed = bc_get_flags(v16, &idx, 1);
36232 bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
36233 bc.has_debug = bc_get_flags(v16, &idx, 1);
36234 bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1);
36235 bc.read_only_bytecode = s->is_rom_data;
36236 if (bc_get_u8(s, &v8))
36237 goto fail;
36238 bc.js_mode = v8;
36239 if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
36240 goto fail;
36241 if (bc_get_leb128_u16(s, &bc.arg_count))
36242 goto fail;
36243 if (bc_get_leb128_u16(s, &bc.var_count))
36244 goto fail;
36245 if (bc_get_leb128_u16(s, &bc.defined_arg_count))
36246 goto fail;
36247 if (bc_get_leb128_u16(s, &bc.stack_size))
36248 goto fail;
36249 if (bc_get_leb128_int(s, &bc.closure_var_count))
36250 goto fail;
36251 if (bc_get_leb128_int(s, &bc.cpool_count))
36252 goto fail;
36253 if (bc_get_leb128_int(s, &bc.byte_code_len))
36254 goto fail;
36255 if (bc_get_leb128_int(s, &local_count))
36256 goto fail;
36257
36258 if (bc.has_debug) {
36259 function_size = sizeof(*b);
36260 } else {
36261 function_size = offsetof(JSFunctionBytecode, debug);
36262 }
36263 cpool_offset = function_size;
36264 function_size += bc.cpool_count * sizeof(*bc.cpool);
36265 vardefs_offset = function_size;
36266 function_size += local_count * sizeof(*bc.vardefs);
36267 closure_var_offset = function_size;
36268 function_size += bc.closure_var_count * sizeof(*bc.closure_var);
36269 byte_code_offset = function_size;
36270 if (!bc.read_only_bytecode) {
36271 function_size += bc.byte_code_len;
36272 }
36273
36274 b = js_mallocz(ctx, function_size);
36275 if (!b)
36276 return JS_EXCEPTION;
36277
36278 memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
36279 b->header.ref_count = 1;
36280 if (local_count != 0) {
36281 b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
36282 }
36283 if (b->closure_var_count != 0) {
36284 b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
36285 }
36286 if (b->cpool_count != 0) {
36287 b->cpool = (void *)((uint8_t*)b + cpool_offset);
36288 }
36289
36290 add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
36291
36292 obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
36293
36294#ifdef DUMP_READ_OBJECT
36295 bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
36296#endif
36297 bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
36298 b->arg_count, b->var_count, b->defined_arg_count,
36299 b->closure_var_count, b->cpool_count);
36300 bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
36301 b->stack_size, b->byte_code_len, local_count);
36302
36303 if (local_count != 0) {
36304 bc_read_trace(s, "vars {\n");
36305 for(i = 0; i < local_count; i++) {
36306 JSVarDef *vd = &b->vardefs[i];
36307 if (bc_get_atom(s, &vd->var_name))
36308 goto fail;
36309 if (bc_get_leb128_int(s, &vd->scope_level))
36310 goto fail;
36311 if (bc_get_leb128_int(s, &vd->scope_next))
36312 goto fail;
36313 vd->scope_next--;
36314 if (bc_get_u8(s, &v8))
36315 goto fail;
36316 idx = 0;
36317 vd->var_kind = bc_get_flags(v8, &idx, 4);
36318 vd->is_const = bc_get_flags(v8, &idx, 1);
36319 vd->is_lexical = bc_get_flags(v8, &idx, 1);
36320 vd->is_captured = bc_get_flags(v8, &idx, 1);
36321#ifdef DUMP_READ_OBJECT
36322 bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
36323#endif
36324 }
36325 bc_read_trace(s, "}\n");
36326 }
36327 if (b->closure_var_count != 0) {
36328 bc_read_trace(s, "closure vars {\n");
36329 for(i = 0; i < b->closure_var_count; i++) {
36330 JSClosureVar *cv = &b->closure_var[i];
36331 int var_idx;
36332 if (bc_get_atom(s, &cv->var_name))
36333 goto fail;
36334 if (bc_get_leb128_int(s, &var_idx))
36335 goto fail;
36336 cv->var_idx = var_idx;
36337 if (bc_get_u8(s, &v8))
36338 goto fail;
36339 idx = 0;
36340 cv->is_local = bc_get_flags(v8, &idx, 1);
36341 cv->is_arg = bc_get_flags(v8, &idx, 1);
36342 cv->is_const = bc_get_flags(v8, &idx, 1);
36343 cv->is_lexical = bc_get_flags(v8, &idx, 1);
36344 cv->var_kind = bc_get_flags(v8, &idx, 4);
36345#ifdef DUMP_READ_OBJECT
36346 bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
36347#endif
36348 }
36349 bc_read_trace(s, "}\n");
36350 }
36351 {
36352 bc_read_trace(s, "bytecode {\n");
36353 if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
36354 goto fail;
36355 bc_read_trace(s, "}\n");
36356 }
36357 if (b->has_debug) {
36358 /* read optional debug information */
36359 bc_read_trace(s, "debug {\n");
36360 if (bc_get_atom(s, &b->debug.filename))
36361 goto fail;
36362#ifdef DUMP_READ_OBJECT
36363 bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
36364#endif
36365 if (bc_get_leb128_int(s, &b->debug.pc2line_len))
36366 goto fail;
36367 if (b->debug.pc2line_len) {
36368 b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
36369 if (!b->debug.pc2line_buf)
36370 goto fail;
36371 if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
36372 goto fail;
36373 }
36374 if (bc_get_leb128_int(s, &b->debug.source_len))
36375 goto fail;
36376 if (b->debug.source_len) {
36377 bc_read_trace(s, "source: %d bytes\n", b->source_len);
36378 b->debug.source = js_mallocz(ctx, b->debug.source_len);
36379 if (!b->debug.source)
36380 goto fail;
36381 if (bc_get_buf(s, (uint8_t *)b->debug.source, b->debug.source_len))
36382 goto fail;
36383 }
36384 bc_read_trace(s, "}\n");
36385 }
36386 if (b->cpool_count != 0) {
36387 bc_read_trace(s, "cpool {\n");
36388 for(i = 0; i < b->cpool_count; i++) {
36389 JSValue val;
36390 val = JS_ReadObjectRec(s);
36391 if (JS_IsException(val))
36392 goto fail;
36393 b->cpool[i] = val;
36394 }
36395 bc_read_trace(s, "}\n");
36396 }
36397 b->realm = JS_DupContext(ctx);
36398 return obj;
36399 fail:
36400 JS_FreeValue(ctx, obj);
36401 return JS_EXCEPTION;
36402}
36403
36404static JSValue JS_ReadModule(BCReaderState *s)
36405{
36406 JSContext *ctx = s->ctx;
36407 JSValue obj;
36408 JSModuleDef *m = NULL;
36409 JSAtom module_name;
36410 int i;
36411 uint8_t v8;
36412
36413 if (bc_get_atom(s, &module_name))
36414 goto fail;
36415#ifdef DUMP_READ_OBJECT
36416 bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
36417#endif
36418 m = js_new_module_def(ctx, module_name);
36419 if (!m)
36420 goto fail;
36421 obj = JS_NewModuleValue(ctx, m);
36422 if (bc_get_leb128_int(s, &m->req_module_entries_count))
36423 goto fail;
36424 if (m->req_module_entries_count != 0) {
36425 m->req_module_entries_size = m->req_module_entries_count;
36426 m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
36427 if (!m->req_module_entries)
36428 goto fail;
36429 for(i = 0; i < m->req_module_entries_count; i++) {
36430 JSReqModuleEntry *rme = &m->req_module_entries[i];
36431 if (bc_get_atom(s, &rme->module_name))
36432 goto fail;
36433 }
36434 }
36435
36436 if (bc_get_leb128_int(s, &m->export_entries_count))
36437 goto fail;
36438 if (m->export_entries_count != 0) {
36439 m->export_entries_size = m->export_entries_count;
36440 m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
36441 if (!m->export_entries)
36442 goto fail;
36443 for(i = 0; i < m->export_entries_count; i++) {
36444 JSExportEntry *me = &m->export_entries[i];
36445 if (bc_get_u8(s, &v8))
36446 goto fail;
36447 me->export_type = v8;
36448 if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
36449 if (bc_get_leb128_int(s, &me->u.local.var_idx))
36450 goto fail;
36451 } else {
36452 if (bc_get_leb128_int(s, &me->u.req_module_idx))
36453 goto fail;
36454 if (bc_get_atom(s, &me->local_name))
36455 goto fail;
36456 }
36457 if (bc_get_atom(s, &me->export_name))
36458 goto fail;
36459 }
36460 }
36461
36462 if (bc_get_leb128_int(s, &m->star_export_entries_count))
36463 goto fail;
36464 if (m->star_export_entries_count != 0) {
36465 m->star_export_entries_size = m->star_export_entries_count;
36466 m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
36467 if (!m->star_export_entries)
36468 goto fail;
36469 for(i = 0; i < m->star_export_entries_count; i++) {
36470 JSStarExportEntry *se = &m->star_export_entries[i];
36471 if (bc_get_leb128_int(s, &se->req_module_idx))
36472 goto fail;
36473 }
36474 }
36475
36476 if (bc_get_leb128_int(s, &m->import_entries_count))
36477 goto fail;
36478 if (m->import_entries_count != 0) {
36479 m->import_entries_size = m->import_entries_count;
36480 m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
36481 if (!m->import_entries)
36482 goto fail;
36483 for(i = 0; i < m->import_entries_count; i++) {
36484 JSImportEntry *mi = &m->import_entries[i];
36485 if (bc_get_leb128_int(s, &mi->var_idx))
36486 goto fail;
36487 if (bc_get_atom(s, &mi->import_name))
36488 goto fail;
36489 if (bc_get_leb128_int(s, &mi->req_module_idx))
36490 goto fail;
36491 }
36492 }
36493
36494 if (bc_get_u8(s, &v8))
36495 goto fail;
36496 m->has_tla = (v8 != 0);
36497
36498 m->func_obj = JS_ReadObjectRec(s);
36499 if (JS_IsException(m->func_obj))
36500 goto fail;
36501 return obj;
36502 fail:
36503 if (m) {
36504 js_free_module_def(ctx, m);
36505 }
36506 return JS_EXCEPTION;
36507}
36508
36509static JSValue JS_ReadObjectTag(BCReaderState *s)
36510{
36511 JSContext *ctx = s->ctx;
36512 JSValue obj;
36513 uint32_t prop_count, i;
36514 JSAtom atom;
36515 JSValue val;
36516 int ret;
36517
36518 obj = JS_NewObject(ctx);
36519 if (BC_add_object_ref(s, obj))
36520 goto fail;
36521 if (bc_get_leb128(s, &prop_count))
36522 goto fail;
36523 for(i = 0; i < prop_count; i++) {
36524 if (bc_get_atom(s, &atom))
36525 goto fail;
36526#ifdef DUMP_READ_OBJECT
36527 bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
36528#endif
36529 val = JS_ReadObjectRec(s);
36530 if (JS_IsException(val)) {
36531 JS_FreeAtom(ctx, atom);
36532 goto fail;
36533 }
36534 ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
36535 JS_FreeAtom(ctx, atom);
36536 if (ret < 0)
36537 goto fail;
36538 }
36539 return obj;
36540 fail:
36541 JS_FreeValue(ctx, obj);
36542 return JS_EXCEPTION;
36543}
36544
36545static JSValue JS_ReadArray(BCReaderState *s, int tag)
36546{
36547 JSContext *ctx = s->ctx;
36548 JSValue obj;
36549 uint32_t len, i;
36550 JSValue val;
36551 int ret, prop_flags;
36552 BOOL is_template;
36553
36554 obj = JS_NewArray(ctx);
36555 if (BC_add_object_ref(s, obj))
36556 goto fail;
36557 is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
36558 if (bc_get_leb128(s, &len))
36559 goto fail;
36560 for(i = 0; i < len; i++) {
36561 val = JS_ReadObjectRec(s);
36562 if (JS_IsException(val))
36563 goto fail;
36564 if (is_template)
36565 prop_flags = JS_PROP_ENUMERABLE;
36566 else
36567 prop_flags = JS_PROP_C_W_E;
36568 ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
36569 prop_flags);
36570 if (ret < 0)
36571 goto fail;
36572 }
36573 if (is_template) {
36574 val = JS_ReadObjectRec(s);
36575 if (JS_IsException(val))
36576 goto fail;
36577 if (!JS_IsUndefined(val)) {
36578 ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
36579 if (ret < 0)
36580 goto fail;
36581 }
36582 JS_PreventExtensions(ctx, obj);
36583 }
36584 return obj;
36585 fail:
36586 JS_FreeValue(ctx, obj);
36587 return JS_EXCEPTION;
36588}
36589
36590static JSValue JS_ReadTypedArray(BCReaderState *s)
36591{
36592 JSContext *ctx = s->ctx;
36593 JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
36594 uint8_t array_tag;
36595 JSValueConst args[3];
36596 uint32_t offset, len, idx;
36597
36598 if (bc_get_u8(s, &array_tag))
36599 return JS_EXCEPTION;
36600 if (array_tag >= JS_TYPED_ARRAY_COUNT)
36601 return JS_ThrowTypeError(ctx, "invalid typed array");
36602 if (bc_get_leb128(s, &len))
36603 return JS_EXCEPTION;
36604 if (bc_get_leb128(s, &offset))
36605 return JS_EXCEPTION;
36606 /* XXX: this hack could be avoided if the typed array could be
36607 created before the array buffer */
36608 idx = s->objects_count;
36609 if (BC_add_object_ref1(s, NULL))
36610 goto fail;
36611 array_buffer = JS_ReadObjectRec(s);
36612 if (JS_IsException(array_buffer))
36613 return JS_EXCEPTION;
36614 if (!js_get_array_buffer(ctx, array_buffer)) {
36615 JS_FreeValue(ctx, array_buffer);
36616 return JS_EXCEPTION;
36617 }
36618 args[0] = array_buffer;
36619 args[1] = JS_NewInt64(ctx, offset);
36620 args[2] = JS_NewInt64(ctx, len);
36621 obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
36622 3, args,
36623 JS_CLASS_UINT8C_ARRAY + array_tag);
36624 if (JS_IsException(obj))
36625 goto fail;
36626 if (s->allow_reference) {
36627 s->objects[idx] = JS_VALUE_GET_OBJ(obj);
36628 }
36629 JS_FreeValue(ctx, array_buffer);
36630 return obj;
36631 fail:
36632 JS_FreeValue(ctx, array_buffer);
36633 JS_FreeValue(ctx, obj);
36634 return JS_EXCEPTION;
36635}
36636
36637static JSValue JS_ReadArrayBuffer(BCReaderState *s)
36638{
36639 JSContext *ctx = s->ctx;
36640 uint32_t byte_length;
36641 JSValue obj;
36642
36643 if (bc_get_leb128(s, &byte_length))
36644 return JS_EXCEPTION;
36645 if (unlikely(s->buf_end - s->ptr < byte_length)) {
36646 bc_read_error_end(s);
36647 return JS_EXCEPTION;
36648 }
36649 obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
36650 if (JS_IsException(obj))
36651 goto fail;
36652 if (BC_add_object_ref(s, obj))
36653 goto fail;
36654 s->ptr += byte_length;
36655 return obj;
36656 fail:
36657 JS_FreeValue(ctx, obj);
36658 return JS_EXCEPTION;
36659}
36660
36661static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
36662{
36663 JSContext *ctx = s->ctx;
36664 uint32_t byte_length;
36665 uint8_t *data_ptr;
36666 JSValue obj;
36667 uint64_t u64;
36668
36669 if (bc_get_leb128(s, &byte_length))
36670 return JS_EXCEPTION;
36671 if (bc_get_u64(s, &u64))
36672 return JS_EXCEPTION;
36673 data_ptr = (uint8_t *)(uintptr_t)u64;
36674 /* the SharedArrayBuffer is cloned */
36675 obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
36676 JS_CLASS_SHARED_ARRAY_BUFFER,
36677 data_ptr,
36678 NULL, NULL, FALSE);
36679 if (JS_IsException(obj))
36680 goto fail;
36681 if (BC_add_object_ref(s, obj))
36682 goto fail;
36683 return obj;
36684 fail:
36685 JS_FreeValue(ctx, obj);
36686 return JS_EXCEPTION;
36687}
36688
36689static JSValue JS_ReadDate(BCReaderState *s)
36690{
36691 JSContext *ctx = s->ctx;
36692 JSValue val, obj = JS_UNDEFINED;
36693
36694 val = JS_ReadObjectRec(s);
36695 if (JS_IsException(val))
36696 goto fail;
36697 if (!JS_IsNumber(val)) {
36698 JS_ThrowTypeError(ctx, "Number tag expected for date");
36699 goto fail;
36700 }
36701 obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
36702 JS_CLASS_DATE);
36703 if (JS_IsException(obj))
36704 goto fail;
36705 if (BC_add_object_ref(s, obj))
36706 goto fail;
36707 JS_SetObjectData(ctx, obj, val);
36708 return obj;
36709 fail:
36710 JS_FreeValue(ctx, val);
36711 JS_FreeValue(ctx, obj);
36712 return JS_EXCEPTION;
36713}
36714
36715static JSValue JS_ReadObjectValue(BCReaderState *s)
36716{
36717 JSContext *ctx = s->ctx;
36718 JSValue val, obj = JS_UNDEFINED;
36719
36720 val = JS_ReadObjectRec(s);
36721 if (JS_IsException(val))
36722 goto fail;
36723 obj = JS_ToObject(ctx, val);
36724 if (JS_IsException(obj))
36725 goto fail;
36726 if (BC_add_object_ref(s, obj))
36727 goto fail;
36728 JS_FreeValue(ctx, val);
36729 return obj;
36730 fail:
36731 JS_FreeValue(ctx, val);
36732 JS_FreeValue(ctx, obj);
36733 return JS_EXCEPTION;
36734}
36735
36736static JSValue JS_ReadObjectRec(BCReaderState *s)
36737{
36738 JSContext *ctx = s->ctx;
36739 uint8_t tag;
36740 JSValue obj = JS_UNDEFINED;
36741
36742 if (js_check_stack_overflow(ctx->rt, 0))
36743 return JS_ThrowStackOverflow(ctx);
36744
36745 if (bc_get_u8(s, &tag))
36746 return JS_EXCEPTION;
36747
36748 bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
36749
36750 switch(tag) {
36751 case BC_TAG_NULL:
36752 obj = JS_NULL;
36753 break;
36754 case BC_TAG_UNDEFINED:
36755 obj = JS_UNDEFINED;
36756 break;
36757 case BC_TAG_BOOL_FALSE:
36758 case BC_TAG_BOOL_TRUE:
36759 obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
36760 break;
36761 case BC_TAG_INT32:
36762 {
36763 int32_t val;
36764 if (bc_get_sleb128(s, &val))
36765 return JS_EXCEPTION;
36766 bc_read_trace(s, "%d\n", val);
36767 obj = JS_NewInt32(ctx, val);
36768 }
36769 break;
36770 case BC_TAG_FLOAT64:
36771 {
36772 JSFloat64Union u;
36773 if (bc_get_u64(s, &u.u64))
36774 return JS_EXCEPTION;
36775 bc_read_trace(s, "%g\n", u.d);
36776 obj = __JS_NewFloat64(ctx, u.d);
36777 }
36778 break;
36779 case BC_TAG_STRING:
36780 {
36781 JSString *p;
36782 p = JS_ReadString(s);
36783 if (!p)
36784 return JS_EXCEPTION;
36785 obj = JS_MKPTR(JS_TAG_STRING, p);
36786 }
36787 break;
36788 case BC_TAG_FUNCTION_BYTECODE:
36789 if (!s->allow_bytecode)
36790 goto invalid_tag;
36791 obj = JS_ReadFunctionTag(s);
36792 break;
36793 case BC_TAG_MODULE:
36794 if (!s->allow_bytecode)
36795 goto invalid_tag;
36796 obj = JS_ReadModule(s);
36797 break;
36798 case BC_TAG_OBJECT:
36799 obj = JS_ReadObjectTag(s);
36800 break;
36801 case BC_TAG_ARRAY:
36802 case BC_TAG_TEMPLATE_OBJECT:
36803 obj = JS_ReadArray(s, tag);
36804 break;
36805 case BC_TAG_TYPED_ARRAY:
36806 obj = JS_ReadTypedArray(s);
36807 break;
36808 case BC_TAG_ARRAY_BUFFER:
36809 obj = JS_ReadArrayBuffer(s);
36810 break;
36811 case BC_TAG_SHARED_ARRAY_BUFFER:
36812 if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
36813 goto invalid_tag;
36814 obj = JS_ReadSharedArrayBuffer(s);
36815 break;
36816 case BC_TAG_DATE:
36817 obj = JS_ReadDate(s);
36818 break;
36819 case BC_TAG_OBJECT_VALUE:
36820 obj = JS_ReadObjectValue(s);
36821 break;
36822 case BC_TAG_BIG_INT:
36823 obj = JS_ReadBigInt(s);
36824 break;
36825 case BC_TAG_OBJECT_REFERENCE:
36826 {
36827 uint32_t val;
36828 if (!s->allow_reference)
36829 return JS_ThrowSyntaxError(ctx, "object references are not allowed");
36830 if (bc_get_leb128(s, &val))
36831 return JS_EXCEPTION;
36832 bc_read_trace(s, "%u\n", val);
36833 if (val >= s->objects_count) {
36834 return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
36835 val, s->objects_count);
36836 }
36837 obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
36838 }
36839 break;
36840 default:
36841 invalid_tag:
36842 return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
36843 tag, (unsigned int)(s->ptr - s->buf_start));
36844 }
36845 bc_read_trace(s, "}\n");
36846 return obj;
36847}
36848
36849static int JS_ReadObjectAtoms(BCReaderState *s)
36850{
36851 uint8_t v8;
36852 JSString *p;
36853 int i;
36854 JSAtom atom;
36855
36856 if (bc_get_u8(s, &v8))
36857 return -1;
36858 if (v8 != BC_VERSION) {
36859 JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
36860 v8, BC_VERSION);
36861 return -1;
36862 }
36863 if (bc_get_leb128(s, &s->idx_to_atom_count))
36864 return -1;
36865
36866 bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
36867
36868 if (s->idx_to_atom_count != 0) {
36869 s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
36870 sizeof(s->idx_to_atom[0]));
36871 if (!s->idx_to_atom)
36872 return s->error_state = -1;
36873 }
36874 for(i = 0; i < s->idx_to_atom_count; i++) {
36875 p = JS_ReadString(s);
36876 if (!p)
36877 return -1;
36878 atom = JS_NewAtomStr(s->ctx, p);
36879 if (atom == JS_ATOM_NULL)
36880 return s->error_state = -1;
36881 s->idx_to_atom[i] = atom;
36882 if (s->is_rom_data && (atom != (i + s->first_atom)))
36883 s->is_rom_data = FALSE; /* atoms must be relocated */
36884 }
36885 bc_read_trace(s, "}\n");
36886 return 0;
36887}
36888
36889static void bc_reader_free(BCReaderState *s)
36890{
36891 int i;
36892 if (s->idx_to_atom) {
36893 for(i = 0; i < s->idx_to_atom_count; i++) {
36894 JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
36895 }
36896 js_free(s->ctx, s->idx_to_atom);
36897 }
36898 js_free(s->ctx, s->objects);
36899}
36900
36901JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
36902 int flags)
36903{
36904 BCReaderState ss, *s = &ss;
36905 JSValue obj;
36906
36907 ctx->binary_object_count += 1;
36908 ctx->binary_object_size += buf_len;
36909
36910 memset(s, 0, sizeof(*s));
36911 s->ctx = ctx;
36912 s->buf_start = buf;
36913 s->buf_end = buf + buf_len;
36914 s->ptr = buf;
36915 s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
36916 s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
36917 s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
36918 s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
36919 if (s->allow_bytecode)
36920 s->first_atom = JS_ATOM_END;
36921 else
36922 s->first_atom = 1;
36923 if (JS_ReadObjectAtoms(s)) {
36924 obj = JS_EXCEPTION;
36925 } else {
36926 obj = JS_ReadObjectRec(s);
36927 }
36928 bc_reader_free(s);
36929 return obj;
36930}
36931
36932/*******************************************************************/
36933/* runtime functions & objects */
36934
36935static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
36936 int argc, JSValueConst *argv);
36937static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
36938 int argc, JSValueConst *argv);
36939static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
36940 int argc, JSValueConst *argv);
36941
36942static int check_function(JSContext *ctx, JSValueConst obj)
36943{
36944 if (likely(JS_IsFunction(ctx, obj)))
36945 return 0;
36946 JS_ThrowTypeError(ctx, "not a function");
36947 return -1;
36948}
36949
36950static int check_exception_free(JSContext *ctx, JSValue obj)
36951{
36952 JS_FreeValue(ctx, obj);
36953 return JS_IsException(obj);
36954}
36955
36956static JSAtom find_atom(JSContext *ctx, const char *name)
36957{
36958 JSAtom atom;
36959 int len;
36960
36961 if (*name == '[') {
36962 name++;
36963 len = strlen(name) - 1;
36964 /* We assume 8 bit non null strings, which is the case for these
36965 symbols */
36966 for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
36967 JSAtomStruct *p = ctx->rt->atom_array[atom];
36968 JSString *str = p;
36969 if (str->len == len && !memcmp(str->u.str8, name, len))
36970 return JS_DupAtom(ctx, atom);
36971 }
36972 abort();
36973 } else {
36974 atom = JS_NewAtom(ctx, name);
36975 }
36976 return atom;
36977}
36978
36979static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
36980 JSAtom atom, void *opaque)
36981{
36982 const JSCFunctionListEntry *e = opaque;
36983 JSValue val;
36984
36985 switch(e->def_type) {
36986 case JS_DEF_CFUNC:
36987 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
36988 e->name, e->u.func.length, e->u.func.cproto, e->magic);
36989 break;
36990 case JS_DEF_PROP_STRING:
36991 val = JS_NewAtomString(ctx, e->u.str);
36992 break;
36993 case JS_DEF_OBJECT:
36994 val = JS_NewObject(ctx);
36995 JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
36996 break;
36997 default:
36998 abort();
36999 }
37000 return val;
37001}
37002
37003static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
37004 JSAtom atom,
37005 const JSCFunctionListEntry *e)
37006{
37007 JSValue val;
37008 int prop_flags = e->prop_flags;
37009
37010 switch(e->def_type) {
37011 case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
37012 {
37013 JSAtom atom1 = find_atom(ctx, e->u.alias.name);
37014 switch (e->u.alias.base) {
37015 case -1:
37016 val = JS_GetProperty(ctx, obj, atom1);
37017 break;
37018 case 0:
37019 val = JS_GetProperty(ctx, ctx->global_obj, atom1);
37020 break;
37021 case 1:
37022 val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
37023 break;
37024 default:
37025 abort();
37026 }
37027 JS_FreeAtom(ctx, atom1);
37028 if (atom == JS_ATOM_Symbol_toPrimitive) {
37029 /* Symbol.toPrimitive functions are not writable */
37030 prop_flags = JS_PROP_CONFIGURABLE;
37031 } else if (atom == JS_ATOM_Symbol_hasInstance) {
37032 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
37033 prop_flags = 0;
37034 }
37035 }
37036 break;
37037 case JS_DEF_CFUNC:
37038 if (atom == JS_ATOM_Symbol_toPrimitive) {
37039 /* Symbol.toPrimitive functions are not writable */
37040 prop_flags = JS_PROP_CONFIGURABLE;
37041 } else if (atom == JS_ATOM_Symbol_hasInstance) {
37042 /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
37043 prop_flags = 0;
37044 }
37045 JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
37046 (void *)e, prop_flags);
37047 return 0;
37048 case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
37049 case JS_DEF_CGETSET_MAGIC:
37050 {
37051 JSValue getter, setter;
37052 char buf[64];
37053
37054 getter = JS_UNDEFINED;
37055 if (e->u.getset.get.generic) {
37056 snprintf(buf, sizeof(buf), "get %s", e->name);
37057 getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
37058 buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
37059 e->magic);
37060 }
37061 setter = JS_UNDEFINED;
37062 if (e->u.getset.set.generic) {
37063 snprintf(buf, sizeof(buf), "set %s", e->name);
37064 setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
37065 buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
37066 e->magic);
37067 }
37068 JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
37069 return 0;
37070 }
37071 break;
37072 case JS_DEF_PROP_INT32:
37073 val = JS_NewInt32(ctx, e->u.i32);
37074 break;
37075 case JS_DEF_PROP_INT64:
37076 val = JS_NewInt64(ctx, e->u.i64);
37077 break;
37078 case JS_DEF_PROP_DOUBLE:
37079 val = __JS_NewFloat64(ctx, e->u.f64);
37080 break;
37081 case JS_DEF_PROP_UNDEFINED:
37082 val = JS_UNDEFINED;
37083 break;
37084 case JS_DEF_PROP_STRING:
37085 case JS_DEF_OBJECT:
37086 JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
37087 (void *)e, prop_flags);
37088 return 0;
37089 default:
37090 abort();
37091 }
37092 JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
37093 return 0;
37094}
37095
37096void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
37097 const JSCFunctionListEntry *tab, int len)
37098{
37099 int i;
37100
37101 for (i = 0; i < len; i++) {
37102 const JSCFunctionListEntry *e = &tab[i];
37103 JSAtom atom = find_atom(ctx, e->name);
37104 JS_InstantiateFunctionListItem(ctx, obj, atom, e);
37105 JS_FreeAtom(ctx, atom);
37106 }
37107}
37108
37109int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
37110 const JSCFunctionListEntry *tab, int len)
37111{
37112 int i;
37113 for(i = 0; i < len; i++) {
37114 if (JS_AddModuleExport(ctx, m, tab[i].name))
37115 return -1;
37116 }
37117 return 0;
37118}
37119
37120int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
37121 const JSCFunctionListEntry *tab, int len)
37122{
37123 int i;
37124 JSValue val;
37125
37126 for(i = 0; i < len; i++) {
37127 const JSCFunctionListEntry *e = &tab[i];
37128 switch(e->def_type) {
37129 case JS_DEF_CFUNC:
37130 val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
37131 e->name, e->u.func.length, e->u.func.cproto, e->magic);
37132 break;
37133 case JS_DEF_PROP_STRING:
37134 val = JS_NewString(ctx, e->u.str);
37135 break;
37136 case JS_DEF_PROP_INT32:
37137 val = JS_NewInt32(ctx, e->u.i32);
37138 break;
37139 case JS_DEF_PROP_INT64:
37140 val = JS_NewInt64(ctx, e->u.i64);
37141 break;
37142 case JS_DEF_PROP_DOUBLE:
37143 val = __JS_NewFloat64(ctx, e->u.f64);
37144 break;
37145 case JS_DEF_OBJECT:
37146 val = JS_NewObject(ctx);
37147 JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
37148 break;
37149 default:
37150 abort();
37151 }
37152 if (JS_SetModuleExport(ctx, m, e->name, val))
37153 return -1;
37154 }
37155 return 0;
37156}
37157
37158/* Note: 'func_obj' is not necessarily a constructor */
37159static void JS_SetConstructor2(JSContext *ctx,
37160 JSValueConst func_obj,
37161 JSValueConst proto,
37162 int proto_flags, int ctor_flags)
37163{
37164 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
37165 JS_DupValue(ctx, proto), proto_flags);
37166 JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
37167 JS_DupValue(ctx, func_obj),
37168 ctor_flags);
37169 set_cycle_flag(ctx, func_obj);
37170 set_cycle_flag(ctx, proto);
37171}
37172
37173void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
37174 JSValueConst proto)
37175{
37176 JS_SetConstructor2(ctx, func_obj, proto,
37177 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37178}
37179
37180static void JS_NewGlobalCConstructor2(JSContext *ctx,
37181 JSValue func_obj,
37182 const char *name,
37183 JSValueConst proto)
37184{
37185 JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
37186 JS_DupValue(ctx, func_obj),
37187 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
37188 JS_SetConstructor(ctx, func_obj, proto);
37189 JS_FreeValue(ctx, func_obj);
37190}
37191
37192static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
37193 JSCFunction *func, int length,
37194 JSValueConst proto)
37195{
37196 JSValue func_obj;
37197 func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
37198 JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
37199 return func_obj;
37200}
37201
37202static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
37203 JSCFunction *func, int length,
37204 JSValueConst proto)
37205{
37206 JSValue func_obj;
37207 func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
37208 JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
37209 return func_obj;
37210}
37211
37212static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
37213 int argc, JSValueConst *argv)
37214{
37215 return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
37216}
37217
37218static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
37219 int argc, JSValueConst *argv)
37220{
37221 double d;
37222
37223 if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
37224 return JS_EXCEPTION;
37225 return JS_NewBool(ctx, isnan(d));
37226}
37227
37228static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
37229 int argc, JSValueConst *argv)
37230{
37231 double d;
37232 if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
37233 return JS_EXCEPTION;
37234 return JS_NewBool(ctx, isfinite(d));
37235}
37236
37237/* Object class */
37238
37239static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
37240{
37241 int tag = JS_VALUE_GET_NORM_TAG(val);
37242 JSValue obj;
37243
37244 switch(tag) {
37245 default:
37246 case JS_TAG_NULL:
37247 case JS_TAG_UNDEFINED:
37248 return JS_ThrowTypeError(ctx, "cannot convert to object");
37249 case JS_TAG_OBJECT:
37250 case JS_TAG_EXCEPTION:
37251 return JS_DupValue(ctx, val);
37252 case JS_TAG_SHORT_BIG_INT:
37253 case JS_TAG_BIG_INT:
37254 obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
37255 goto set_value;
37256 case JS_TAG_INT:
37257 case JS_TAG_FLOAT64:
37258 obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
37259 goto set_value;
37260 case JS_TAG_STRING:
37261 case JS_TAG_STRING_ROPE:
37262 /* XXX: should call the string constructor */
37263 {
37264 JSValue str;
37265 str = JS_ToString(ctx, val); /* ensure that we never store a rope */
37266 if (JS_IsException(str))
37267 return JS_EXCEPTION;
37268 obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
37269 if (!JS_IsException(obj)) {
37270 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length,
37271 JS_NewInt32(ctx, JS_VALUE_GET_STRING(str)->len), 0);
37272 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, str));
37273 }
37274 JS_FreeValue(ctx, str);
37275 return obj;
37276 }
37277 case JS_TAG_BOOL:
37278 obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
37279 goto set_value;
37280 case JS_TAG_SYMBOL:
37281 obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
37282 set_value:
37283 if (!JS_IsException(obj))
37284 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
37285 return obj;
37286 }
37287}
37288
37289static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
37290{
37291 JSValue obj = JS_ToObject(ctx, val);
37292 JS_FreeValue(ctx, val);
37293 return obj;
37294}
37295
37296static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
37297 JSValueConst desc)
37298{
37299 JSValue val, getter, setter;
37300 int flags;
37301
37302 if (!JS_IsObject(desc)) {
37303 JS_ThrowTypeErrorNotAnObject(ctx);
37304 return -1;
37305 }
37306 flags = 0;
37307 val = JS_UNDEFINED;
37308 getter = JS_UNDEFINED;
37309 setter = JS_UNDEFINED;
37310 if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
37311 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
37312 if (JS_IsException(prop))
37313 goto fail;
37314 flags |= JS_PROP_HAS_CONFIGURABLE;
37315 if (JS_ToBoolFree(ctx, prop))
37316 flags |= JS_PROP_CONFIGURABLE;
37317 }
37318 if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
37319 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
37320 if (JS_IsException(prop))
37321 goto fail;
37322 flags |= JS_PROP_HAS_WRITABLE;
37323 if (JS_ToBoolFree(ctx, prop))
37324 flags |= JS_PROP_WRITABLE;
37325 }
37326 if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
37327 JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
37328 if (JS_IsException(prop))
37329 goto fail;
37330 flags |= JS_PROP_HAS_ENUMERABLE;
37331 if (JS_ToBoolFree(ctx, prop))
37332 flags |= JS_PROP_ENUMERABLE;
37333 }
37334 if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
37335 flags |= JS_PROP_HAS_VALUE;
37336 val = JS_GetProperty(ctx, desc, JS_ATOM_value);
37337 if (JS_IsException(val))
37338 goto fail;
37339 }
37340 if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
37341 flags |= JS_PROP_HAS_GET;
37342 getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
37343 if (JS_IsException(getter) ||
37344 !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
37345 JS_ThrowTypeError(ctx, "invalid getter");
37346 goto fail;
37347 }
37348 }
37349 if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
37350 flags |= JS_PROP_HAS_SET;
37351 setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
37352 if (JS_IsException(setter) ||
37353 !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
37354 JS_ThrowTypeError(ctx, "invalid setter");
37355 goto fail;
37356 }
37357 }
37358 if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
37359 (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
37360 JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
37361 goto fail;
37362 }
37363 d->flags = flags;
37364 d->value = val;
37365 d->getter = getter;
37366 d->setter = setter;
37367 return 0;
37368 fail:
37369 JS_FreeValue(ctx, val);
37370 JS_FreeValue(ctx, getter);
37371 JS_FreeValue(ctx, setter);
37372 return -1;
37373}
37374
37375static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
37376 JSAtom prop, JSValueConst desc,
37377 int flags)
37378{
37379 JSPropertyDescriptor d;
37380 int ret;
37381
37382 if (js_obj_to_desc(ctx, &d, desc) < 0)
37383 return -1;
37384
37385 ret = JS_DefineProperty(ctx, obj, prop,
37386 d.value, d.getter, d.setter, d.flags | flags);
37387 js_free_desc(ctx, &d);
37388 return ret;
37389}
37390
37391static __exception int JS_ObjectDefineProperties(JSContext *ctx,
37392 JSValueConst obj,
37393 JSValueConst properties)
37394{
37395 JSValue props, desc;
37396 JSObject *p;
37397 JSPropertyEnum *atoms;
37398 uint32_t len, i;
37399 int ret = -1;
37400
37401 if (!JS_IsObject(obj)) {
37402 JS_ThrowTypeErrorNotAnObject(ctx);
37403 return -1;
37404 }
37405 desc = JS_UNDEFINED;
37406 props = JS_ToObject(ctx, properties);
37407 if (JS_IsException(props))
37408 return -1;
37409 p = JS_VALUE_GET_OBJ(props);
37410 /* XXX: not done in the same order as the spec */
37411 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
37412 goto exception;
37413 for(i = 0; i < len; i++) {
37414 JS_FreeValue(ctx, desc);
37415 desc = JS_GetProperty(ctx, props, atoms[i].atom);
37416 if (JS_IsException(desc))
37417 goto exception;
37418 if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
37419 goto exception;
37420 }
37421 ret = 0;
37422
37423exception:
37424 js_free_prop_enum(ctx, atoms, len);
37425 JS_FreeValue(ctx, props);
37426 JS_FreeValue(ctx, desc);
37427 return ret;
37428}
37429
37430static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
37431 int argc, JSValueConst *argv)
37432{
37433 JSValue ret;
37434 if (!JS_IsUndefined(new_target) &&
37435 JS_VALUE_GET_OBJ(new_target) !=
37436 JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
37437 ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
37438 } else {
37439 int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
37440 switch(tag) {
37441 case JS_TAG_NULL:
37442 case JS_TAG_UNDEFINED:
37443 ret = JS_NewObject(ctx);
37444 break;
37445 default:
37446 ret = JS_ToObject(ctx, argv[0]);
37447 break;
37448 }
37449 }
37450 return ret;
37451}
37452
37453static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
37454 int argc, JSValueConst *argv)
37455{
37456 JSValueConst proto, props;
37457 JSValue obj;
37458
37459 proto = argv[0];
37460 if (!JS_IsObject(proto) && !JS_IsNull(proto))
37461 return JS_ThrowTypeError(ctx, "not a prototype");
37462 obj = JS_NewObjectProto(ctx, proto);
37463 if (JS_IsException(obj))
37464 return JS_EXCEPTION;
37465 props = argv[1];
37466 if (!JS_IsUndefined(props)) {
37467 if (JS_ObjectDefineProperties(ctx, obj, props)) {
37468 JS_FreeValue(ctx, obj);
37469 return JS_EXCEPTION;
37470 }
37471 }
37472 return obj;
37473}
37474
37475static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
37476 int argc, JSValueConst *argv, int magic)
37477{
37478 JSValueConst val;
37479
37480 val = argv[0];
37481 if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
37482 /* ES6 feature non compatible with ES5.1: primitive types are
37483 accepted */
37484 if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
37485 JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
37486 return JS_ThrowTypeErrorNotAnObject(ctx);
37487 }
37488 return JS_GetPrototype(ctx, val);
37489}
37490
37491static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
37492 int argc, JSValueConst *argv)
37493{
37494 JSValueConst obj;
37495 obj = argv[0];
37496 if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
37497 return JS_EXCEPTION;
37498 return JS_DupValue(ctx, obj);
37499}
37500
37501/* magic = 1 if called as Reflect.defineProperty */
37502static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
37503 int argc, JSValueConst *argv, int magic)
37504{
37505 JSValueConst obj, prop, desc;
37506 int ret, flags;
37507 JSAtom atom;
37508
37509 obj = argv[0];
37510 prop = argv[1];
37511 desc = argv[2];
37512
37513 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
37514 return JS_ThrowTypeErrorNotAnObject(ctx);
37515 atom = JS_ValueToAtom(ctx, prop);
37516 if (unlikely(atom == JS_ATOM_NULL))
37517 return JS_EXCEPTION;
37518 flags = 0;
37519 if (!magic)
37520 flags |= JS_PROP_THROW;
37521 ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
37522 JS_FreeAtom(ctx, atom);
37523 if (ret < 0) {
37524 return JS_EXCEPTION;
37525 } else if (magic) {
37526 return JS_NewBool(ctx, ret);
37527 } else {
37528 return JS_DupValue(ctx, obj);
37529 }
37530}
37531
37532static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
37533 int argc, JSValueConst *argv)
37534{
37535 // defineProperties(obj, properties)
37536 JSValueConst obj = argv[0];
37537
37538 if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
37539 return JS_EXCEPTION;
37540 else
37541 return JS_DupValue(ctx, obj);
37542}
37543
37544/* magic = 1 if called as __defineSetter__ */
37545static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
37546 int argc, JSValueConst *argv, int magic)
37547{
37548 JSValue obj;
37549 JSValueConst prop, value, get, set;
37550 int ret, flags;
37551 JSAtom atom;
37552
37553 prop = argv[0];
37554 value = argv[1];
37555
37556 obj = JS_ToObject(ctx, this_val);
37557 if (JS_IsException(obj))
37558 return JS_EXCEPTION;
37559
37560 if (check_function(ctx, value)) {
37561 JS_FreeValue(ctx, obj);
37562 return JS_EXCEPTION;
37563 }
37564 atom = JS_ValueToAtom(ctx, prop);
37565 if (unlikely(atom == JS_ATOM_NULL)) {
37566 JS_FreeValue(ctx, obj);
37567 return JS_EXCEPTION;
37568 }
37569 flags = JS_PROP_THROW |
37570 JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
37571 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
37572 if (magic) {
37573 get = JS_UNDEFINED;
37574 set = value;
37575 flags |= JS_PROP_HAS_SET;
37576 } else {
37577 get = value;
37578 set = JS_UNDEFINED;
37579 flags |= JS_PROP_HAS_GET;
37580 }
37581 ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
37582 JS_FreeValue(ctx, obj);
37583 JS_FreeAtom(ctx, atom);
37584 if (ret < 0) {
37585 return JS_EXCEPTION;
37586 } else {
37587 return JS_UNDEFINED;
37588 }
37589}
37590
37591static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
37592 int argc, JSValueConst *argv, int magic)
37593{
37594 JSValueConst prop;
37595 JSAtom atom;
37596 JSValue ret, obj;
37597 JSPropertyDescriptor desc;
37598 int res, flags;
37599
37600 if (magic) {
37601 /* Reflect.getOwnPropertyDescriptor case */
37602 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
37603 return JS_ThrowTypeErrorNotAnObject(ctx);
37604 obj = JS_DupValue(ctx, argv[0]);
37605 } else {
37606 obj = JS_ToObject(ctx, argv[0]);
37607 if (JS_IsException(obj))
37608 return obj;
37609 }
37610 prop = argv[1];
37611 atom = JS_ValueToAtom(ctx, prop);
37612 if (unlikely(atom == JS_ATOM_NULL))
37613 goto exception;
37614 ret = JS_UNDEFINED;
37615 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
37616 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
37617 if (res < 0)
37618 goto exception;
37619 if (res) {
37620 ret = JS_NewObject(ctx);
37621 if (JS_IsException(ret))
37622 goto exception1;
37623 flags = JS_PROP_C_W_E | JS_PROP_THROW;
37624 if (desc.flags & JS_PROP_GETSET) {
37625 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
37626 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
37627 goto exception1;
37628 } else {
37629 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
37630 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
37631 JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0)
37632 goto exception1;
37633 }
37634 if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
37635 JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0
37636 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
37637 JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0)
37638 goto exception1;
37639 js_free_desc(ctx, &desc);
37640 }
37641 }
37642 JS_FreeAtom(ctx, atom);
37643 JS_FreeValue(ctx, obj);
37644 return ret;
37645
37646exception1:
37647 js_free_desc(ctx, &desc);
37648 JS_FreeValue(ctx, ret);
37649exception:
37650 JS_FreeAtom(ctx, atom);
37651 JS_FreeValue(ctx, obj);
37652 return JS_EXCEPTION;
37653}
37654
37655static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
37656 int argc, JSValueConst *argv)
37657{
37658 //getOwnPropertyDescriptors(obj)
37659 JSValue obj, r;
37660 JSObject *p;
37661 JSPropertyEnum *props;
37662 uint32_t len, i;
37663
37664 r = JS_UNDEFINED;
37665 obj = JS_ToObject(ctx, argv[0]);
37666 if (JS_IsException(obj))
37667 return JS_EXCEPTION;
37668
37669 p = JS_VALUE_GET_OBJ(obj);
37670 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
37671 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
37672 goto exception;
37673 r = JS_NewObject(ctx);
37674 if (JS_IsException(r))
37675 goto exception;
37676 for(i = 0; i < len; i++) {
37677 JSValue atomValue, desc;
37678 JSValueConst args[2];
37679
37680 atomValue = JS_AtomToValue(ctx, props[i].atom);
37681 if (JS_IsException(atomValue))
37682 goto exception;
37683 args[0] = obj;
37684 args[1] = atomValue;
37685 desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
37686 JS_FreeValue(ctx, atomValue);
37687 if (JS_IsException(desc))
37688 goto exception;
37689 if (!JS_IsUndefined(desc)) {
37690 if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
37691 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
37692 goto exception;
37693 }
37694 }
37695 js_free_prop_enum(ctx, props, len);
37696 JS_FreeValue(ctx, obj);
37697 return r;
37698
37699exception:
37700 js_free_prop_enum(ctx, props, len);
37701 JS_FreeValue(ctx, obj);
37702 JS_FreeValue(ctx, r);
37703 return JS_EXCEPTION;
37704}
37705
37706static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
37707 int flags, int kind)
37708{
37709 JSValue obj, r, val, key, value;
37710 JSObject *p;
37711 JSPropertyEnum *atoms;
37712 uint32_t len, i, j;
37713
37714 r = JS_UNDEFINED;
37715 val = JS_UNDEFINED;
37716 obj = JS_ToObject(ctx, obj1);
37717 if (JS_IsException(obj))
37718 return JS_EXCEPTION;
37719 p = JS_VALUE_GET_OBJ(obj);
37720 if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
37721 goto exception;
37722 r = JS_NewArray(ctx);
37723 if (JS_IsException(r))
37724 goto exception;
37725 for(j = i = 0; i < len; i++) {
37726 JSAtom atom = atoms[i].atom;
37727 if (flags & JS_GPN_ENUM_ONLY) {
37728 JSPropertyDescriptor desc;
37729 int res;
37730
37731 /* Check if property is still enumerable */
37732 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
37733 if (res < 0)
37734 goto exception;
37735 if (!res)
37736 continue;
37737 js_free_desc(ctx, &desc);
37738 if (!(desc.flags & JS_PROP_ENUMERABLE))
37739 continue;
37740 }
37741 switch(kind) {
37742 default:
37743 case JS_ITERATOR_KIND_KEY:
37744 val = JS_AtomToValue(ctx, atom);
37745 if (JS_IsException(val))
37746 goto exception;
37747 break;
37748 case JS_ITERATOR_KIND_VALUE:
37749 val = JS_GetProperty(ctx, obj, atom);
37750 if (JS_IsException(val))
37751 goto exception;
37752 break;
37753 case JS_ITERATOR_KIND_KEY_AND_VALUE:
37754 val = JS_NewArray(ctx);
37755 if (JS_IsException(val))
37756 goto exception;
37757 key = JS_AtomToValue(ctx, atom);
37758 if (JS_IsException(key))
37759 goto exception1;
37760 if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
37761 goto exception1;
37762 value = JS_GetProperty(ctx, obj, atom);
37763 if (JS_IsException(value))
37764 goto exception1;
37765 if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
37766 goto exception1;
37767 break;
37768 }
37769 if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
37770 goto exception;
37771 }
37772 goto done;
37773
37774exception1:
37775 JS_FreeValue(ctx, val);
37776exception:
37777 JS_FreeValue(ctx, r);
37778 r = JS_EXCEPTION;
37779done:
37780 js_free_prop_enum(ctx, atoms, len);
37781 JS_FreeValue(ctx, obj);
37782 return r;
37783}
37784
37785static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
37786 int argc, JSValueConst *argv)
37787{
37788 return JS_GetOwnPropertyNames2(ctx, argv[0],
37789 JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
37790}
37791
37792static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
37793 int argc, JSValueConst *argv)
37794{
37795 return JS_GetOwnPropertyNames2(ctx, argv[0],
37796 JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
37797}
37798
37799static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
37800 int argc, JSValueConst *argv, int kind)
37801{
37802 return JS_GetOwnPropertyNames2(ctx, argv[0],
37803 JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
37804}
37805
37806static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
37807 int argc, JSValueConst *argv, int reflect)
37808{
37809 JSValueConst obj;
37810 int ret;
37811
37812 obj = argv[0];
37813 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
37814 if (reflect)
37815 return JS_ThrowTypeErrorNotAnObject(ctx);
37816 else
37817 return JS_FALSE;
37818 }
37819 ret = JS_IsExtensible(ctx, obj);
37820 if (ret < 0)
37821 return JS_EXCEPTION;
37822 else
37823 return JS_NewBool(ctx, ret);
37824}
37825
37826static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
37827 int argc, JSValueConst *argv, int reflect)
37828{
37829 JSValueConst obj;
37830 int ret;
37831
37832 obj = argv[0];
37833 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
37834 if (reflect)
37835 return JS_ThrowTypeErrorNotAnObject(ctx);
37836 else
37837 return JS_DupValue(ctx, obj);
37838 }
37839 ret = JS_PreventExtensions(ctx, obj);
37840 if (ret < 0)
37841 return JS_EXCEPTION;
37842 if (reflect) {
37843 return JS_NewBool(ctx, ret);
37844 } else {
37845 if (!ret)
37846 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
37847 return JS_DupValue(ctx, obj);
37848 }
37849}
37850
37851static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
37852 int argc, JSValueConst *argv)
37853{
37854 JSValue obj;
37855 JSAtom atom;
37856 JSObject *p;
37857 BOOL ret;
37858
37859 atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
37860 if (unlikely(atom == JS_ATOM_NULL))
37861 return JS_EXCEPTION;
37862 obj = JS_ToObject(ctx, this_val);
37863 if (JS_IsException(obj)) {
37864 JS_FreeAtom(ctx, atom);
37865 return obj;
37866 }
37867 p = JS_VALUE_GET_OBJ(obj);
37868 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
37869 JS_FreeAtom(ctx, atom);
37870 JS_FreeValue(ctx, obj);
37871 if (ret < 0)
37872 return JS_EXCEPTION;
37873 else
37874 return JS_NewBool(ctx, ret);
37875}
37876
37877static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
37878 int argc, JSValueConst *argv)
37879{
37880 JSValue obj;
37881 JSAtom atom;
37882 JSObject *p;
37883 BOOL ret;
37884
37885 obj = JS_ToObject(ctx, argv[0]);
37886 if (JS_IsException(obj))
37887 return obj;
37888 atom = JS_ValueToAtom(ctx, argv[1]);
37889 if (unlikely(atom == JS_ATOM_NULL)) {
37890 JS_FreeValue(ctx, obj);
37891 return JS_EXCEPTION;
37892 }
37893 p = JS_VALUE_GET_OBJ(obj);
37894 ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
37895 JS_FreeAtom(ctx, atom);
37896 JS_FreeValue(ctx, obj);
37897 if (ret < 0)
37898 return JS_EXCEPTION;
37899 else
37900 return JS_NewBool(ctx, ret);
37901}
37902
37903static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
37904 int argc, JSValueConst *argv)
37905{
37906 return JS_ToObject(ctx, this_val);
37907}
37908
37909static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
37910 int argc, JSValueConst *argv)
37911{
37912 JSValue obj, tag;
37913 int is_array;
37914 JSAtom atom;
37915 JSObject *p;
37916
37917 if (JS_IsNull(this_val)) {
37918 tag = js_new_string8(ctx, "Null");
37919 } else if (JS_IsUndefined(this_val)) {
37920 tag = js_new_string8(ctx, "Undefined");
37921 } else {
37922 obj = JS_ToObject(ctx, this_val);
37923 if (JS_IsException(obj))
37924 return obj;
37925 is_array = JS_IsArray(ctx, obj);
37926 if (is_array < 0) {
37927 JS_FreeValue(ctx, obj);
37928 return JS_EXCEPTION;
37929 }
37930 if (is_array) {
37931 atom = JS_ATOM_Array;
37932 } else if (JS_IsFunction(ctx, obj)) {
37933 atom = JS_ATOM_Function;
37934 } else {
37935 p = JS_VALUE_GET_OBJ(obj);
37936 switch(p->class_id) {
37937 case JS_CLASS_STRING:
37938 case JS_CLASS_ARGUMENTS:
37939 case JS_CLASS_MAPPED_ARGUMENTS:
37940 case JS_CLASS_ERROR:
37941 case JS_CLASS_BOOLEAN:
37942 case JS_CLASS_NUMBER:
37943 case JS_CLASS_DATE:
37944 case JS_CLASS_REGEXP:
37945 atom = ctx->rt->class_array[p->class_id].class_name;
37946 break;
37947 default:
37948 atom = JS_ATOM_Object;
37949 break;
37950 }
37951 }
37952 tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
37953 JS_FreeValue(ctx, obj);
37954 if (JS_IsException(tag))
37955 return JS_EXCEPTION;
37956 if (!JS_IsString(tag)) {
37957 JS_FreeValue(ctx, tag);
37958 tag = JS_AtomToString(ctx, atom);
37959 }
37960 }
37961 return JS_ConcatString3(ctx, "[object ", tag, "]");
37962}
37963
37964static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
37965 int argc, JSValueConst *argv)
37966{
37967 return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
37968}
37969
37970static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
37971 int argc, JSValueConst *argv)
37972{
37973 // Object.assign(obj, source1)
37974 JSValue obj, s;
37975 int i;
37976
37977 s = JS_UNDEFINED;
37978 obj = JS_ToObject(ctx, argv[0]);
37979 if (JS_IsException(obj))
37980 goto exception;
37981 for (i = 1; i < argc; i++) {
37982 if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
37983 s = JS_ToObject(ctx, argv[i]);
37984 if (JS_IsException(s))
37985 goto exception;
37986 if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
37987 goto exception;
37988 JS_FreeValue(ctx, s);
37989 }
37990 }
37991 return obj;
37992exception:
37993 JS_FreeValue(ctx, obj);
37994 JS_FreeValue(ctx, s);
37995 return JS_EXCEPTION;
37996}
37997
37998static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
37999 int argc, JSValueConst *argv, int freeze_flag)
38000{
38001 JSValueConst obj = argv[0];
38002 JSObject *p;
38003 JSPropertyEnum *props;
38004 uint32_t len, i;
38005 int flags, desc_flags, res;
38006
38007 if (!JS_IsObject(obj))
38008 return JS_DupValue(ctx, obj);
38009
38010 res = JS_PreventExtensions(ctx, obj);
38011 if (res < 0)
38012 return JS_EXCEPTION;
38013 if (!res) {
38014 return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
38015 }
38016
38017 p = JS_VALUE_GET_OBJ(obj);
38018 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
38019 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
38020 return JS_EXCEPTION;
38021
38022 for(i = 0; i < len; i++) {
38023 JSPropertyDescriptor desc;
38024 JSAtom prop = props[i].atom;
38025
38026 desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
38027 if (freeze_flag) {
38028 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
38029 if (res < 0)
38030 goto exception;
38031 if (res) {
38032 if (desc.flags & JS_PROP_WRITABLE)
38033 desc_flags |= JS_PROP_HAS_WRITABLE;
38034 js_free_desc(ctx, &desc);
38035 }
38036 }
38037 if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
38038 JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
38039 goto exception;
38040 }
38041 js_free_prop_enum(ctx, props, len);
38042 return JS_DupValue(ctx, obj);
38043
38044 exception:
38045 js_free_prop_enum(ctx, props, len);
38046 return JS_EXCEPTION;
38047}
38048
38049static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
38050 int argc, JSValueConst *argv, int is_frozen)
38051{
38052 JSValueConst obj = argv[0];
38053 JSObject *p;
38054 JSPropertyEnum *props;
38055 uint32_t len, i;
38056 int flags, res;
38057
38058 if (!JS_IsObject(obj))
38059 return JS_TRUE;
38060
38061 p = JS_VALUE_GET_OBJ(obj);
38062 flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
38063 if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
38064 return JS_EXCEPTION;
38065
38066 for(i = 0; i < len; i++) {
38067 JSPropertyDescriptor desc;
38068 JSAtom prop = props[i].atom;
38069
38070 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
38071 if (res < 0)
38072 goto exception;
38073 if (res) {
38074 js_free_desc(ctx, &desc);
38075 if ((desc.flags & JS_PROP_CONFIGURABLE)
38076 || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
38077 res = FALSE;
38078 goto done;
38079 }
38080 }
38081 }
38082 res = JS_IsExtensible(ctx, obj);
38083 if (res < 0)
38084 return JS_EXCEPTION;
38085 res ^= 1;
38086done:
38087 js_free_prop_enum(ctx, props, len);
38088 return JS_NewBool(ctx, res);
38089
38090exception:
38091 js_free_prop_enum(ctx, props, len);
38092 return JS_EXCEPTION;
38093}
38094
38095static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
38096 int argc, JSValueConst *argv)
38097{
38098 JSValue obj, iter, next_method = JS_UNDEFINED;
38099 JSValueConst iterable;
38100 BOOL done;
38101
38102 /* RequireObjectCoercible() not necessary because it is tested in
38103 JS_GetIterator() by JS_GetProperty() */
38104 iterable = argv[0];
38105
38106 obj = JS_NewObject(ctx);
38107 if (JS_IsException(obj))
38108 return obj;
38109
38110 iter = JS_GetIterator(ctx, iterable, FALSE);
38111 if (JS_IsException(iter))
38112 goto fail;
38113 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
38114 if (JS_IsException(next_method))
38115 goto fail;
38116
38117 for(;;) {
38118 JSValue key, value, item;
38119 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
38120 if (JS_IsException(item))
38121 goto fail;
38122 if (done)
38123 break;
38124
38125 key = JS_UNDEFINED;
38126 value = JS_UNDEFINED;
38127 if (!JS_IsObject(item)) {
38128 JS_ThrowTypeErrorNotAnObject(ctx);
38129 goto fail1;
38130 }
38131 key = JS_GetPropertyUint32(ctx, item, 0);
38132 if (JS_IsException(key))
38133 goto fail1;
38134 value = JS_GetPropertyUint32(ctx, item, 1);
38135 if (JS_IsException(value)) {
38136 JS_FreeValue(ctx, key);
38137 goto fail1;
38138 }
38139 if (JS_DefinePropertyValueValue(ctx, obj, key, value,
38140 JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
38141 fail1:
38142 JS_FreeValue(ctx, item);
38143 goto fail;
38144 }
38145 JS_FreeValue(ctx, item);
38146 }
38147 JS_FreeValue(ctx, next_method);
38148 JS_FreeValue(ctx, iter);
38149 return obj;
38150 fail:
38151 if (JS_IsObject(iter)) {
38152 /* close the iterator object, preserving pending exception */
38153 JS_IteratorClose(ctx, iter, TRUE);
38154 }
38155 JS_FreeValue(ctx, next_method);
38156 JS_FreeValue(ctx, iter);
38157 JS_FreeValue(ctx, obj);
38158 return JS_EXCEPTION;
38159}
38160
38161#if 0
38162/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
38163static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
38164 int argc, JSValueConst *argv)
38165{
38166 int ret;
38167 ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
38168 JS_DupValue(ctx, argv[2]),
38169 JS_PROP_C_W_E | JS_PROP_THROW);
38170 if (ret < 0)
38171 return JS_EXCEPTION;
38172 else
38173 return JS_NewBool(ctx, ret);
38174}
38175
38176static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
38177 int argc, JSValueConst *argv)
38178{
38179 return JS_ToObject(ctx, argv[0]);
38180}
38181
38182static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
38183 int argc, JSValueConst *argv)
38184{
38185 int hint = HINT_NONE;
38186
38187 if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
38188 hint = JS_VALUE_GET_INT(argv[1]);
38189
38190 return JS_ToPrimitive(ctx, argv[0], hint);
38191}
38192#endif
38193
38194/* return an empty string if not an object */
38195static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
38196 int argc, JSValueConst *argv)
38197{
38198 JSAtom atom;
38199 JSObject *p;
38200 uint32_t tag;
38201 int class_id;
38202
38203 tag = JS_VALUE_GET_NORM_TAG(argv[0]);
38204 if (tag == JS_TAG_OBJECT) {
38205 p = JS_VALUE_GET_OBJ(argv[0]);
38206 class_id = p->class_id;
38207 if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
38208 class_id = JS_CLASS_BYTECODE_FUNCTION;
38209 atom = ctx->rt->class_array[class_id].class_name;
38210 } else {
38211 atom = JS_ATOM_empty_string;
38212 }
38213 return JS_AtomToString(ctx, atom);
38214}
38215
38216static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
38217 int argc, JSValueConst *argv)
38218{
38219 return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
38220}
38221
38222#if 0
38223static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
38224 int argc, JSValueConst *argv)
38225{
38226 return JS_GetObjectData(ctx, argv[0]);
38227}
38228
38229static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
38230 int argc, JSValueConst *argv)
38231{
38232 if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
38233 return JS_EXCEPTION;
38234 return JS_DupValue(ctx, argv[1]);
38235}
38236
38237static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
38238 int argc, JSValueConst *argv)
38239{
38240 return JS_ToPropertyKey(ctx, argv[0]);
38241}
38242
38243static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
38244 int argc, JSValueConst *argv)
38245{
38246 return JS_NewBool(ctx, JS_IsObject(argv[0]));
38247}
38248
38249static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
38250 int argc, JSValueConst *argv)
38251{
38252 return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
38253}
38254
38255static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
38256 int argc, JSValueConst *argv)
38257{
38258 return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
38259}
38260#endif
38261
38262static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
38263 JSValueConst defaultConstructor)
38264{
38265 JSValue ctor, species;
38266
38267 if (!JS_IsObject(obj))
38268 return JS_ThrowTypeErrorNotAnObject(ctx);
38269 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
38270 if (JS_IsException(ctor))
38271 return ctor;
38272 if (JS_IsUndefined(ctor))
38273 return JS_DupValue(ctx, defaultConstructor);
38274 if (!JS_IsObject(ctor)) {
38275 JS_FreeValue(ctx, ctor);
38276 return JS_ThrowTypeErrorNotAnObject(ctx);
38277 }
38278 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
38279 JS_FreeValue(ctx, ctor);
38280 if (JS_IsException(species))
38281 return species;
38282 if (JS_IsUndefined(species) || JS_IsNull(species))
38283 return JS_DupValue(ctx, defaultConstructor);
38284 if (!JS_IsConstructor(ctx, species)) {
38285 JS_FreeValue(ctx, species);
38286 return JS_ThrowTypeError(ctx, "not a constructor");
38287 }
38288 return species;
38289}
38290
38291#if 0
38292static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
38293 int argc, JSValueConst *argv)
38294{
38295 return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
38296}
38297#endif
38298
38299static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
38300{
38301 JSValue val, ret;
38302
38303 val = JS_ToObject(ctx, this_val);
38304 if (JS_IsException(val))
38305 return val;
38306 ret = JS_GetPrototype(ctx, val);
38307 JS_FreeValue(ctx, val);
38308 return ret;
38309}
38310
38311static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
38312 JSValueConst proto)
38313{
38314 if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
38315 return JS_ThrowTypeErrorNotAnObject(ctx);
38316 if (!JS_IsObject(proto) && !JS_IsNull(proto))
38317 return JS_UNDEFINED;
38318 if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
38319 return JS_EXCEPTION;
38320 else
38321 return JS_UNDEFINED;
38322}
38323
38324static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
38325 int argc, JSValueConst *argv)
38326{
38327 JSValue obj, v1;
38328 JSValueConst v;
38329 int res;
38330
38331 v = argv[0];
38332 if (!JS_IsObject(v))
38333 return JS_FALSE;
38334 obj = JS_ToObject(ctx, this_val);
38335 if (JS_IsException(obj))
38336 return JS_EXCEPTION;
38337 v1 = JS_DupValue(ctx, v);
38338 for(;;) {
38339 v1 = JS_GetPrototypeFree(ctx, v1);
38340 if (JS_IsException(v1))
38341 goto exception;
38342 if (JS_IsNull(v1)) {
38343 res = FALSE;
38344 break;
38345 }
38346 if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
38347 res = TRUE;
38348 break;
38349 }
38350 /* avoid infinite loop (possible with proxies) */
38351 if (js_poll_interrupts(ctx))
38352 goto exception;
38353 }
38354 JS_FreeValue(ctx, v1);
38355 JS_FreeValue(ctx, obj);
38356 return JS_NewBool(ctx, res);
38357
38358exception:
38359 JS_FreeValue(ctx, v1);
38360 JS_FreeValue(ctx, obj);
38361 return JS_EXCEPTION;
38362}
38363
38364static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
38365 int argc, JSValueConst *argv)
38366{
38367 JSValue obj = JS_UNDEFINED, res = JS_EXCEPTION;
38368 JSAtom prop;
38369 JSPropertyDescriptor desc;
38370 int has_prop;
38371
38372 prop = JS_ValueToAtom(ctx, argv[0]);
38373 if (unlikely(prop == JS_ATOM_NULL))
38374 goto exception;
38375 obj = JS_ToObject(ctx, this_val);
38376 if (JS_IsException(obj))
38377 goto exception;
38378
38379 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
38380 if (has_prop < 0)
38381 goto exception;
38382 if (has_prop) {
38383 res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE);
38384 js_free_desc(ctx, &desc);
38385 } else {
38386 res = JS_FALSE;
38387 }
38388
38389exception:
38390 JS_FreeAtom(ctx, prop);
38391 JS_FreeValue(ctx, obj);
38392 return res;
38393}
38394
38395static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
38396 int argc, JSValueConst *argv, int setter)
38397{
38398 JSValue obj, res = JS_EXCEPTION;
38399 JSAtom prop = JS_ATOM_NULL;
38400 JSPropertyDescriptor desc;
38401 int has_prop;
38402
38403 obj = JS_ToObject(ctx, this_val);
38404 if (JS_IsException(obj))
38405 goto exception;
38406 prop = JS_ValueToAtom(ctx, argv[0]);
38407 if (unlikely(prop == JS_ATOM_NULL))
38408 goto exception;
38409
38410 for (;;) {
38411 has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
38412 if (has_prop < 0)
38413 goto exception;
38414 if (has_prop) {
38415 if (desc.flags & JS_PROP_GETSET)
38416 res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
38417 else
38418 res = JS_UNDEFINED;
38419 js_free_desc(ctx, &desc);
38420 break;
38421 }
38422 obj = JS_GetPrototypeFree(ctx, obj);
38423 if (JS_IsException(obj))
38424 goto exception;
38425 if (JS_IsNull(obj)) {
38426 res = JS_UNDEFINED;
38427 break;
38428 }
38429 /* avoid infinite loop (possible with proxies) */
38430 if (js_poll_interrupts(ctx))
38431 goto exception;
38432 }
38433
38434exception:
38435 JS_FreeAtom(ctx, prop);
38436 JS_FreeValue(ctx, obj);
38437 return res;
38438}
38439
38440static const JSCFunctionListEntry js_object_funcs[] = {
38441 JS_CFUNC_DEF("create", 2, js_object_create ),
38442 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
38443 JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
38444 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
38445 JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
38446 JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
38447 JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
38448 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ),
38449 JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
38450 JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
38451 JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
38452 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
38453 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
38454 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
38455 JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
38456 JS_CFUNC_DEF("is", 2, js_object_is ),
38457 JS_CFUNC_DEF("assign", 2, js_object_assign ),
38458 JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
38459 JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
38460 JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
38461 JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
38462 JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
38463 //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
38464 //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
38465 //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
38466 //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
38467 //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
38468 //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
38469 //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
38470 //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
38471 //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
38472 //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
38473 JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
38474 JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ),
38475};
38476
38477static const JSCFunctionListEntry js_object_proto_funcs[] = {
38478 JS_CFUNC_DEF("toString", 0, js_object_toString ),
38479 JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
38480 JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
38481 JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
38482 JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
38483 JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
38484 JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
38485 JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
38486 JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
38487 JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
38488 JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
38489};
38490
38491/* Function class */
38492
38493static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
38494 int argc, JSValueConst *argv)
38495{
38496 return JS_UNDEFINED;
38497}
38498
38499/* XXX: add a specific eval mode so that Function("}), ({") is rejected */
38500static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
38501 int argc, JSValueConst *argv, int magic)
38502{
38503 JSFunctionKindEnum func_kind = magic;
38504 int i, n, ret;
38505 JSValue s, proto, obj = JS_UNDEFINED;
38506 StringBuffer b_s, *b = &b_s;
38507
38508 string_buffer_init(ctx, b, 0);
38509 string_buffer_putc8(b, '(');
38510
38511 if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
38512 string_buffer_puts8(b, "async ");
38513 }
38514 string_buffer_puts8(b, "function");
38515
38516 if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
38517 string_buffer_putc8(b, '*');
38518 }
38519 string_buffer_puts8(b, " anonymous(");
38520
38521 n = argc - 1;
38522 for(i = 0; i < n; i++) {
38523 if (i != 0) {
38524 string_buffer_putc8(b, ',');
38525 }
38526 if (string_buffer_concat_value(b, argv[i]))
38527 goto fail;
38528 }
38529 string_buffer_puts8(b, "\n) {\n");
38530 if (n >= 0) {
38531 if (string_buffer_concat_value(b, argv[n]))
38532 goto fail;
38533 }
38534 string_buffer_puts8(b, "\n})");
38535 s = string_buffer_end(b);
38536 if (JS_IsException(s))
38537 goto fail1;
38538
38539 obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
38540 JS_FreeValue(ctx, s);
38541 if (JS_IsException(obj))
38542 goto fail1;
38543 if (!JS_IsUndefined(new_target)) {
38544 /* set the prototype */
38545 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
38546 if (JS_IsException(proto))
38547 goto fail1;
38548 if (!JS_IsObject(proto)) {
38549 JSContext *realm;
38550 JS_FreeValue(ctx, proto);
38551 realm = JS_GetFunctionRealm(ctx, new_target);
38552 if (!realm)
38553 goto fail1;
38554 proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
38555 }
38556 ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
38557 JS_FreeValue(ctx, proto);
38558 if (ret < 0)
38559 goto fail1;
38560 }
38561 return obj;
38562
38563 fail:
38564 string_buffer_free(b);
38565 fail1:
38566 JS_FreeValue(ctx, obj);
38567 return JS_EXCEPTION;
38568}
38569
38570static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
38571 JSValueConst obj)
38572{
38573 JSValue len_val;
38574 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
38575 if (JS_IsException(len_val)) {
38576 *pres = 0;
38577 return -1;
38578 }
38579 return JS_ToUint32Free(ctx, pres, len_val);
38580}
38581
38582static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
38583 JSValueConst obj)
38584{
38585 JSValue len_val;
38586 len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
38587 if (JS_IsException(len_val)) {
38588 *pres = 0;
38589 return -1;
38590 }
38591 return JS_ToLengthFree(ctx, pres, len_val);
38592}
38593
38594static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
38595{
38596 uint32_t i;
38597 for(i = 0; i < len; i++) {
38598 JS_FreeValue(ctx, tab[i]);
38599 }
38600 js_free(ctx, tab);
38601}
38602
38603/* XXX: should use ValueArray */
38604static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
38605 JSValueConst array_arg)
38606{
38607 uint32_t len, i;
38608 int64_t len64;
38609 JSValue *tab, ret;
38610 JSObject *p;
38611
38612 if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
38613 JS_ThrowTypeError(ctx, "not a object");
38614 return NULL;
38615 }
38616 if (js_get_length64(ctx, &len64, array_arg))
38617 return NULL;
38618 if (len64 > JS_MAX_LOCAL_VARS) {
38619 // XXX: check for stack overflow?
38620 JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)",
38621 JS_MAX_LOCAL_VARS);
38622 return NULL;
38623 }
38624 len = len64;
38625 /* avoid allocating 0 bytes */
38626 tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
38627 if (!tab)
38628 return NULL;
38629 p = JS_VALUE_GET_OBJ(array_arg);
38630 if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
38631 p->fast_array &&
38632 len == p->u.array.count) {
38633 for(i = 0; i < len; i++) {
38634 tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
38635 }
38636 } else {
38637 for(i = 0; i < len; i++) {
38638 ret = JS_GetPropertyUint32(ctx, array_arg, i);
38639 if (JS_IsException(ret)) {
38640 free_arg_list(ctx, tab, i);
38641 return NULL;
38642 }
38643 tab[i] = ret;
38644 }
38645 }
38646 *plen = len;
38647 return tab;
38648}
38649
38650/* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
38651 Reflect.apply */
38652static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
38653 int argc, JSValueConst *argv, int magic)
38654{
38655 JSValueConst this_arg, array_arg;
38656 uint32_t len;
38657 JSValue *tab, ret;
38658
38659 if (check_function(ctx, this_val))
38660 return JS_EXCEPTION;
38661 this_arg = argv[0];
38662 array_arg = argv[1];
38663 if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
38664 JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
38665 return JS_Call(ctx, this_val, this_arg, 0, NULL);
38666 }
38667 tab = build_arg_list(ctx, &len, array_arg);
38668 if (!tab)
38669 return JS_EXCEPTION;
38670 if (magic & 1) {
38671 ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
38672 } else {
38673 ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
38674 }
38675 free_arg_list(ctx, tab, len);
38676 return ret;
38677}
38678
38679static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
38680 int argc, JSValueConst *argv)
38681{
38682 if (argc <= 0) {
38683 return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
38684 } else {
38685 return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
38686 }
38687}
38688
38689static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
38690 int argc, JSValueConst *argv)
38691{
38692 JSBoundFunction *bf;
38693 JSValue func_obj, name1, len_val;
38694 JSObject *p;
38695 int arg_count, i, ret;
38696
38697 if (check_function(ctx, this_val))
38698 return JS_EXCEPTION;
38699
38700 func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
38701 JS_CLASS_BOUND_FUNCTION);
38702 if (JS_IsException(func_obj))
38703 return JS_EXCEPTION;
38704 p = JS_VALUE_GET_OBJ(func_obj);
38705 p->is_constructor = JS_IsConstructor(ctx, this_val);
38706 arg_count = max_int(0, argc - 1);
38707 bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
38708 if (!bf)
38709 goto exception;
38710 bf->func_obj = JS_DupValue(ctx, this_val);
38711 bf->this_val = JS_DupValue(ctx, argv[0]);
38712 bf->argc = arg_count;
38713 for(i = 0; i < arg_count; i++) {
38714 bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
38715 }
38716 p->u.bound_function = bf;
38717
38718 /* XXX: the spec could be simpler by only using GetOwnProperty */
38719 ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
38720 if (ret < 0)
38721 goto exception;
38722 if (!ret) {
38723 len_val = JS_NewInt32(ctx, 0);
38724 } else {
38725 len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
38726 if (JS_IsException(len_val))
38727 goto exception;
38728 if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
38729 /* most common case */
38730 int len1 = JS_VALUE_GET_INT(len_val);
38731 if (len1 <= arg_count)
38732 len1 = 0;
38733 else
38734 len1 -= arg_count;
38735 len_val = JS_NewInt32(ctx, len1);
38736 } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
38737 double d = JS_VALUE_GET_FLOAT64(len_val);
38738 if (isnan(d)) {
38739 d = 0.0;
38740 } else {
38741 d = trunc(d);
38742 if (d <= (double)arg_count)
38743 d = 0.0;
38744 else
38745 d -= (double)arg_count; /* also converts -0 to +0 */
38746 }
38747 len_val = JS_NewFloat64(ctx, d);
38748 } else {
38749 JS_FreeValue(ctx, len_val);
38750 len_val = JS_NewInt32(ctx, 0);
38751 }
38752 }
38753 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
38754 len_val, JS_PROP_CONFIGURABLE);
38755
38756 name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
38757 if (JS_IsException(name1))
38758 goto exception;
38759 if (!JS_IsString(name1)) {
38760 JS_FreeValue(ctx, name1);
38761 name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
38762 }
38763 name1 = JS_ConcatString3(ctx, "bound ", name1, "");
38764 if (JS_IsException(name1))
38765 goto exception;
38766 JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
38767 JS_PROP_CONFIGURABLE);
38768 return func_obj;
38769 exception:
38770 JS_FreeValue(ctx, func_obj);
38771 return JS_EXCEPTION;
38772}
38773
38774static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
38775 int argc, JSValueConst *argv)
38776{
38777 JSObject *p;
38778 JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
38779
38780 if (check_function(ctx, this_val))
38781 return JS_EXCEPTION;
38782
38783 p = JS_VALUE_GET_OBJ(this_val);
38784 if (js_class_has_bytecode(p->class_id)) {
38785 JSFunctionBytecode *b = p->u.func.function_bytecode;
38786 if (b->has_debug && b->debug.source) {
38787 return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
38788 }
38789 func_kind = b->func_kind;
38790 }
38791 {
38792 JSValue name;
38793 const char *pref, *suff;
38794
38795 switch(func_kind) {
38796 default:
38797 case JS_FUNC_NORMAL:
38798 pref = "function ";
38799 break;
38800 case JS_FUNC_GENERATOR:
38801 pref = "function *";
38802 break;
38803 case JS_FUNC_ASYNC:
38804 pref = "async function ";
38805 break;
38806 case JS_FUNC_ASYNC_GENERATOR:
38807 pref = "async function *";
38808 break;
38809 }
38810 suff = "() {\n [native code]\n}";
38811 name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
38812 if (JS_IsUndefined(name))
38813 name = JS_AtomToString(ctx, JS_ATOM_empty_string);
38814 return JS_ConcatString3(ctx, pref, name, suff);
38815 }
38816}
38817
38818static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
38819 int argc, JSValueConst *argv)
38820{
38821 int ret;
38822 ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
38823 if (ret < 0)
38824 return JS_EXCEPTION;
38825 else
38826 return JS_NewBool(ctx, ret);
38827}
38828
38829static const JSCFunctionListEntry js_function_proto_funcs[] = {
38830 JS_CFUNC_DEF("call", 1, js_function_call ),
38831 JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
38832 JS_CFUNC_DEF("bind", 1, js_function_bind ),
38833 JS_CFUNC_DEF("toString", 0, js_function_toString ),
38834 JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
38835 JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
38836 JS_CGETSET_MAGIC_DEF("lineNumber", js_function_proto_lineNumber, NULL, 0 ),
38837 JS_CGETSET_MAGIC_DEF("columnNumber", js_function_proto_lineNumber, NULL, 1 ),
38838};
38839
38840/* Error class */
38841
38842static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
38843{
38844 JSValue iter, next_method = JS_UNDEFINED;
38845 JSValue v, r = JS_UNDEFINED;
38846 int64_t k;
38847 BOOL done;
38848
38849 iter = JS_GetIterator(ctx, items, FALSE);
38850 if (JS_IsException(iter))
38851 goto exception;
38852 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
38853 if (JS_IsException(next_method))
38854 goto exception;
38855 r = JS_NewArray(ctx);
38856 if (JS_IsException(r))
38857 goto exception;
38858 for (k = 0;; k++) {
38859 v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
38860 if (JS_IsException(v))
38861 goto exception_close;
38862 if (done)
38863 break;
38864 if (JS_DefinePropertyValueInt64(ctx, r, k, v,
38865 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
38866 goto exception_close;
38867 }
38868 done:
38869 JS_FreeValue(ctx, next_method);
38870 JS_FreeValue(ctx, iter);
38871 return r;
38872 exception_close:
38873 JS_IteratorClose(ctx, iter, TRUE);
38874 exception:
38875 JS_FreeValue(ctx, r);
38876 r = JS_EXCEPTION;
38877 goto done;
38878}
38879
38880static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
38881 int argc, JSValueConst *argv, int magic)
38882{
38883 JSValue obj, msg, proto;
38884 JSValueConst message, options;
38885 int arg_index;
38886
38887 if (JS_IsUndefined(new_target))
38888 new_target = JS_GetActiveFunction(ctx);
38889 proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
38890 if (JS_IsException(proto))
38891 return proto;
38892 if (!JS_IsObject(proto)) {
38893 JSContext *realm;
38894 JSValueConst proto1;
38895
38896 JS_FreeValue(ctx, proto);
38897 realm = JS_GetFunctionRealm(ctx, new_target);
38898 if (!realm)
38899 return JS_EXCEPTION;
38900 if (magic < 0) {
38901 proto1 = realm->class_proto[JS_CLASS_ERROR];
38902 } else {
38903 proto1 = realm->native_error_proto[magic];
38904 }
38905 proto = JS_DupValue(ctx, proto1);
38906 }
38907 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
38908 JS_FreeValue(ctx, proto);
38909 if (JS_IsException(obj))
38910 return obj;
38911 arg_index = (magic == JS_AGGREGATE_ERROR);
38912
38913 message = argv[arg_index++];
38914 if (!JS_IsUndefined(message)) {
38915 msg = JS_ToString(ctx, message);
38916 if (unlikely(JS_IsException(msg)))
38917 goto exception;
38918 JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
38919 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
38920 }
38921
38922 if (arg_index < argc) {
38923 options = argv[arg_index];
38924 if (JS_IsObject(options)) {
38925 int present = JS_HasProperty(ctx, options, JS_ATOM_cause);
38926 if (present < 0)
38927 goto exception;
38928 if (present) {
38929 JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause);
38930 if (JS_IsException(cause))
38931 goto exception;
38932 JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause,
38933 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
38934 }
38935 }
38936 }
38937
38938 if (magic == JS_AGGREGATE_ERROR) {
38939 JSValue error_list = iterator_to_array(ctx, argv[0]);
38940 if (JS_IsException(error_list))
38941 goto exception;
38942 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
38943 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
38944 }
38945
38946 /* skip the Error() function in the backtrace */
38947 build_backtrace(ctx, obj, NULL, 0, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
38948 return obj;
38949 exception:
38950 JS_FreeValue(ctx, obj);
38951 return JS_EXCEPTION;
38952}
38953
38954static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
38955 int argc, JSValueConst *argv)
38956{
38957 JSValue name, msg;
38958
38959 if (!JS_IsObject(this_val))
38960 return JS_ThrowTypeErrorNotAnObject(ctx);
38961 name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
38962 if (JS_IsUndefined(name))
38963 name = JS_AtomToString(ctx, JS_ATOM_Error);
38964 else
38965 name = JS_ToStringFree(ctx, name);
38966 if (JS_IsException(name))
38967 return JS_EXCEPTION;
38968
38969 msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
38970 if (JS_IsUndefined(msg))
38971 msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
38972 else
38973 msg = JS_ToStringFree(ctx, msg);
38974 if (JS_IsException(msg)) {
38975 JS_FreeValue(ctx, name);
38976 return JS_EXCEPTION;
38977 }
38978 if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
38979 name = JS_ConcatString3(ctx, "", name, ": ");
38980 return JS_ConcatString(ctx, name, msg);
38981}
38982
38983static const JSCFunctionListEntry js_error_proto_funcs[] = {
38984 JS_CFUNC_DEF("toString", 0, js_error_toString ),
38985 JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
38986 JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
38987};
38988
38989/* AggregateError */
38990
38991/* used by C code. */
38992static JSValue js_aggregate_error_constructor(JSContext *ctx,
38993 JSValueConst errors)
38994{
38995 JSValue obj;
38996
38997 obj = JS_NewObjectProtoClass(ctx,
38998 ctx->native_error_proto[JS_AGGREGATE_ERROR],
38999 JS_CLASS_ERROR);
39000 if (JS_IsException(obj))
39001 return obj;
39002 JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
39003 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
39004 return obj;
39005}
39006
39007/* Array */
39008
39009static int JS_CopySubArray(JSContext *ctx,
39010 JSValueConst obj, int64_t to_pos,
39011 int64_t from_pos, int64_t count, int dir)
39012{
39013 JSObject *p;
39014 int64_t i, from, to, len;
39015 JSValue val;
39016 int fromPresent;
39017
39018 p = NULL;
39019 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
39020 p = JS_VALUE_GET_OBJ(obj);
39021 if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) {
39022 p = NULL;
39023 }
39024 }
39025
39026 for (i = 0; i < count; ) {
39027 if (dir < 0) {
39028 from = from_pos + count - i - 1;
39029 to = to_pos + count - i - 1;
39030 } else {
39031 from = from_pos + i;
39032 to = to_pos + i;
39033 }
39034 if (p && p->fast_array &&
39035 from >= 0 && from < (len = p->u.array.count) &&
39036 to >= 0 && to < len) {
39037 int64_t l, j;
39038 /* Fast path for fast arrays. Since we don't look at the
39039 prototype chain, we can optimize only the cases where
39040 all the elements are present in the array. */
39041 l = count - i;
39042 if (dir < 0) {
39043 l = min_int64(l, from + 1);
39044 l = min_int64(l, to + 1);
39045 for(j = 0; j < l; j++) {
39046 set_value(ctx, &p->u.array.u.values[to - j],
39047 JS_DupValue(ctx, p->u.array.u.values[from - j]));
39048 }
39049 } else {
39050 l = min_int64(l, len - from);
39051 l = min_int64(l, len - to);
39052 for(j = 0; j < l; j++) {
39053 set_value(ctx, &p->u.array.u.values[to + j],
39054 JS_DupValue(ctx, p->u.array.u.values[from + j]));
39055 }
39056 }
39057 i += l;
39058 } else {
39059 fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
39060 if (fromPresent < 0)
39061 goto exception;
39062
39063 if (fromPresent) {
39064 if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
39065 goto exception;
39066 } else {
39067 if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
39068 goto exception;
39069 }
39070 i++;
39071 }
39072 }
39073 return 0;
39074
39075 exception:
39076 return -1;
39077}
39078
39079static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
39080 int argc, JSValueConst *argv)
39081{
39082 JSValue obj;
39083 int i;
39084
39085 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
39086 if (JS_IsException(obj))
39087 return obj;
39088 if (argc == 1 && JS_IsNumber(argv[0])) {
39089 uint32_t len;
39090 if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
39091 goto fail;
39092 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
39093 goto fail;
39094 } else {
39095 for(i = 0; i < argc; i++) {
39096 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
39097 goto fail;
39098 }
39099 }
39100 return obj;
39101fail:
39102 JS_FreeValue(ctx, obj);
39103 return JS_EXCEPTION;
39104}
39105
39106static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
39107 int argc, JSValueConst *argv)
39108{
39109 // from(items, mapfn = void 0, this_arg = void 0)
39110 JSValueConst items = argv[0], mapfn, this_arg;
39111 JSValueConst args[2];
39112 JSValue iter, r, v, v2, arrayLike, next_method, enum_obj;
39113 int64_t k, len;
39114 int done, mapping;
39115
39116 mapping = FALSE;
39117 mapfn = JS_UNDEFINED;
39118 this_arg = JS_UNDEFINED;
39119 r = JS_UNDEFINED;
39120 arrayLike = JS_UNDEFINED;
39121 iter = JS_UNDEFINED;
39122 enum_obj = JS_UNDEFINED;
39123 next_method = JS_UNDEFINED;
39124
39125 if (argc > 1) {
39126 mapfn = argv[1];
39127 if (!JS_IsUndefined(mapfn)) {
39128 if (check_function(ctx, mapfn))
39129 goto exception;
39130 mapping = 1;
39131 if (argc > 2)
39132 this_arg = argv[2];
39133 }
39134 }
39135 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
39136 if (JS_IsException(iter))
39137 goto exception;
39138 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
39139 if (!JS_IsFunction(ctx, iter)) {
39140 JS_ThrowTypeError(ctx, "value is not iterable");
39141 goto exception;
39142 }
39143 if (JS_IsConstructor(ctx, this_val))
39144 r = JS_CallConstructor(ctx, this_val, 0, NULL);
39145 else
39146 r = JS_NewArray(ctx);
39147 if (JS_IsException(r))
39148 goto exception;
39149 enum_obj = JS_GetIterator2(ctx, items, iter);
39150 if (JS_IsException(enum_obj))
39151 goto exception;
39152 next_method = JS_GetProperty(ctx, enum_obj, JS_ATOM_next);
39153 if (JS_IsException(next_method))
39154 goto exception;
39155 for (k = 0;; k++) {
39156 v = JS_IteratorNext(ctx, enum_obj, next_method, 0, NULL, &done);
39157 if (JS_IsException(v))
39158 goto exception;
39159 if (done)
39160 break;
39161 if (mapping) {
39162 args[0] = v;
39163 args[1] = JS_NewInt32(ctx, k);
39164 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
39165 JS_FreeValue(ctx, v);
39166 v = v2;
39167 if (JS_IsException(v))
39168 goto exception_close;
39169 }
39170 if (JS_DefinePropertyValueInt64(ctx, r, k, v,
39171 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39172 goto exception_close;
39173 }
39174 } else {
39175 arrayLike = JS_ToObject(ctx, items);
39176 if (JS_IsException(arrayLike))
39177 goto exception;
39178 if (js_get_length64(ctx, &len, arrayLike) < 0)
39179 goto exception;
39180 v = JS_NewInt64(ctx, len);
39181 args[0] = v;
39182 if (JS_IsConstructor(ctx, this_val)) {
39183 r = JS_CallConstructor(ctx, this_val, 1, args);
39184 } else {
39185 r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
39186 }
39187 JS_FreeValue(ctx, v);
39188 if (JS_IsException(r))
39189 goto exception;
39190 for(k = 0; k < len; k++) {
39191 v = JS_GetPropertyInt64(ctx, arrayLike, k);
39192 if (JS_IsException(v))
39193 goto exception;
39194 if (mapping) {
39195 args[0] = v;
39196 args[1] = JS_NewInt32(ctx, k);
39197 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
39198 JS_FreeValue(ctx, v);
39199 v = v2;
39200 if (JS_IsException(v))
39201 goto exception;
39202 }
39203 if (JS_DefinePropertyValueInt64(ctx, r, k, v,
39204 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39205 goto exception;
39206 }
39207 }
39208 if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
39209 goto exception;
39210 goto done;
39211
39212 exception_close:
39213 JS_IteratorClose(ctx, enum_obj, TRUE);
39214 exception:
39215 JS_FreeValue(ctx, r);
39216 r = JS_EXCEPTION;
39217 done:
39218 JS_FreeValue(ctx, arrayLike);
39219 JS_FreeValue(ctx, iter);
39220 JS_FreeValue(ctx, enum_obj);
39221 JS_FreeValue(ctx, next_method);
39222 return r;
39223}
39224
39225static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
39226 int argc, JSValueConst *argv)
39227{
39228 JSValue obj, args[1];
39229 int i;
39230
39231 if (JS_IsConstructor(ctx, this_val)) {
39232 args[0] = JS_NewInt32(ctx, argc);
39233 obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
39234 } else {
39235 obj = JS_NewArray(ctx);
39236 }
39237 if (JS_IsException(obj))
39238 return JS_EXCEPTION;
39239 for(i = 0; i < argc; i++) {
39240 if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
39241 JS_PROP_THROW) < 0) {
39242 goto fail;
39243 }
39244 }
39245 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
39246 fail:
39247 JS_FreeValue(ctx, obj);
39248 return JS_EXCEPTION;
39249 }
39250 return obj;
39251}
39252
39253static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
39254 int argc, JSValueConst *argv)
39255{
39256 int ret;
39257 ret = JS_IsArray(ctx, argv[0]);
39258 if (ret < 0)
39259 return JS_EXCEPTION;
39260 else
39261 return JS_NewBool(ctx, ret);
39262}
39263
39264static JSValue js_get_this(JSContext *ctx,
39265 JSValueConst this_val)
39266{
39267 return JS_DupValue(ctx, this_val);
39268}
39269
39270static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
39271 JSValueConst len_val)
39272{
39273 JSValue ctor, ret, species;
39274 int res;
39275 JSContext *realm;
39276
39277 res = JS_IsArray(ctx, obj);
39278 if (res < 0)
39279 return JS_EXCEPTION;
39280 if (!res)
39281 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
39282 ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
39283 if (JS_IsException(ctor))
39284 return ctor;
39285 if (JS_IsConstructor(ctx, ctor)) {
39286 /* legacy web compatibility */
39287 realm = JS_GetFunctionRealm(ctx, ctor);
39288 if (!realm) {
39289 JS_FreeValue(ctx, ctor);
39290 return JS_EXCEPTION;
39291 }
39292 if (realm != ctx &&
39293 js_same_value(ctx, ctor, realm->array_ctor)) {
39294 JS_FreeValue(ctx, ctor);
39295 ctor = JS_UNDEFINED;
39296 }
39297 }
39298 if (JS_IsObject(ctor)) {
39299 species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
39300 JS_FreeValue(ctx, ctor);
39301 if (JS_IsException(species))
39302 return species;
39303 ctor = species;
39304 if (JS_IsNull(ctor))
39305 ctor = JS_UNDEFINED;
39306 }
39307 if (JS_IsUndefined(ctor)) {
39308 return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
39309 } else {
39310 ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
39311 JS_FreeValue(ctx, ctor);
39312 return ret;
39313 }
39314}
39315
39316static const JSCFunctionListEntry js_array_funcs[] = {
39317 JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
39318 JS_CFUNC_DEF("from", 1, js_array_from ),
39319 JS_CFUNC_DEF("of", 0, js_array_of ),
39320 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
39321};
39322
39323static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
39324{
39325 JSValue val;
39326
39327 if (!JS_IsObject(obj))
39328 return FALSE;
39329 val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
39330 if (JS_IsException(val))
39331 return -1;
39332 if (!JS_IsUndefined(val))
39333 return JS_ToBoolFree(ctx, val);
39334 return JS_IsArray(ctx, obj);
39335}
39336
39337static JSValue js_array_at(JSContext *ctx, JSValueConst this_val,
39338 int argc, JSValueConst *argv)
39339{
39340 JSValue obj, ret;
39341 int64_t len, idx;
39342 JSValue *arrp;
39343 uint32_t count;
39344
39345 obj = JS_ToObject(ctx, this_val);
39346 if (js_get_length64(ctx, &len, obj))
39347 goto exception;
39348
39349 if (JS_ToInt64Sat(ctx, &idx, argv[0]))
39350 goto exception;
39351
39352 if (idx < 0)
39353 idx = len + idx;
39354 if (idx < 0 || idx >= len) {
39355 ret = JS_UNDEFINED;
39356 } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) {
39357 ret = JS_DupValue(ctx, arrp[idx]);
39358 } else {
39359 int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret);
39360 if (present < 0)
39361 goto exception;
39362 if (!present)
39363 ret = JS_UNDEFINED;
39364 }
39365 JS_FreeValue(ctx, obj);
39366 return ret;
39367 exception:
39368 JS_FreeValue(ctx, obj);
39369 return JS_EXCEPTION;
39370}
39371
39372static JSValue js_array_with(JSContext *ctx, JSValueConst this_val,
39373 int argc, JSValueConst *argv)
39374{
39375 JSValue arr, obj, ret, *arrp, *pval;
39376 JSObject *p;
39377 int64_t i, len, idx;
39378 uint32_t count32;
39379
39380 ret = JS_EXCEPTION;
39381 arr = JS_UNDEFINED;
39382 obj = JS_ToObject(ctx, this_val);
39383 if (js_get_length64(ctx, &len, obj))
39384 goto exception;
39385
39386 if (JS_ToInt64Sat(ctx, &idx, argv[0]))
39387 goto exception;
39388
39389 if (idx < 0)
39390 idx = len + idx;
39391
39392 if (idx < 0 || idx >= len) {
39393 JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx);
39394 goto exception;
39395 }
39396
39397 arr = js_allocate_fast_array(ctx, len);
39398 if (JS_IsException(arr))
39399 goto exception;
39400
39401 p = JS_VALUE_GET_OBJ(arr);
39402 i = 0;
39403 pval = p->u.array.u.values;
39404 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
39405 for (; i < idx; i++, pval++)
39406 *pval = JS_DupValue(ctx, arrp[i]);
39407 *pval = JS_DupValue(ctx, argv[1]);
39408 for (i++, pval++; i < len; i++, pval++)
39409 *pval = JS_DupValue(ctx, arrp[i]);
39410 } else {
39411 for (; i < idx; i++, pval++)
39412 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
39413 goto fill_and_fail;
39414 *pval = JS_DupValue(ctx, argv[1]);
39415 for (i++, pval++; i < len; i++, pval++) {
39416 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
39417 fill_and_fail:
39418 for (; i < len; i++, pval++)
39419 *pval = JS_UNDEFINED;
39420 goto exception;
39421 }
39422 }
39423 }
39424
39425 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
39426 goto exception;
39427
39428 ret = arr;
39429 arr = JS_UNDEFINED;
39430
39431exception:
39432 JS_FreeValue(ctx, arr);
39433 JS_FreeValue(ctx, obj);
39434 return ret;
39435}
39436
39437static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
39438 int argc, JSValueConst *argv)
39439{
39440 JSValue obj, arr, val;
39441 JSValueConst e;
39442 int64_t len, k, n;
39443 int i, res;
39444
39445 arr = JS_UNDEFINED;
39446 obj = JS_ToObject(ctx, this_val);
39447 if (JS_IsException(obj))
39448 goto exception;
39449
39450 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
39451 if (JS_IsException(arr))
39452 goto exception;
39453 n = 0;
39454 for (i = -1; i < argc; i++) {
39455 if (i < 0)
39456 e = obj;
39457 else
39458 e = argv[i];
39459
39460 res = JS_isConcatSpreadable(ctx, e);
39461 if (res < 0)
39462 goto exception;
39463 if (res) {
39464 if (js_get_length64(ctx, &len, e))
39465 goto exception;
39466 if (n + len > MAX_SAFE_INTEGER) {
39467 JS_ThrowTypeError(ctx, "Array loo long");
39468 goto exception;
39469 }
39470 for (k = 0; k < len; k++, n++) {
39471 res = JS_TryGetPropertyInt64(ctx, e, k, &val);
39472 if (res < 0)
39473 goto exception;
39474 if (res) {
39475 if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
39476 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39477 goto exception;
39478 }
39479 }
39480 } else {
39481 if (n >= MAX_SAFE_INTEGER) {
39482 JS_ThrowTypeError(ctx, "Array loo long");
39483 goto exception;
39484 }
39485 if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
39486 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39487 goto exception;
39488 n++;
39489 }
39490 }
39491 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
39492 goto exception;
39493
39494 JS_FreeValue(ctx, obj);
39495 return arr;
39496
39497exception:
39498 JS_FreeValue(ctx, arr);
39499 JS_FreeValue(ctx, obj);
39500 return JS_EXCEPTION;
39501}
39502
39503#define special_every 0
39504#define special_some 1
39505#define special_forEach 2
39506#define special_map 3
39507#define special_filter 4
39508#define special_TA 8
39509
39510static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
39511
39512static JSValue js_typed_array___speciesCreate(JSContext *ctx,
39513 JSValueConst this_val,
39514 int argc, JSValueConst *argv);
39515
39516static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
39517 int argc, JSValueConst *argv, int special)
39518{
39519 JSValue obj, val, index_val, res, ret;
39520 JSValueConst args[3];
39521 JSValueConst func, this_arg;
39522 int64_t len, k, n;
39523 int present;
39524
39525 ret = JS_UNDEFINED;
39526 val = JS_UNDEFINED;
39527 if (special & special_TA) {
39528 obj = JS_DupValue(ctx, this_val);
39529 len = js_typed_array_get_length_internal(ctx, obj);
39530 if (len < 0)
39531 goto exception;
39532 } else {
39533 obj = JS_ToObject(ctx, this_val);
39534 if (js_get_length64(ctx, &len, obj))
39535 goto exception;
39536 }
39537 func = argv[0];
39538 this_arg = JS_UNDEFINED;
39539 if (argc > 1)
39540 this_arg = argv[1];
39541
39542 if (check_function(ctx, func))
39543 goto exception;
39544
39545 switch (special) {
39546 case special_every:
39547 case special_every | special_TA:
39548 ret = JS_TRUE;
39549 break;
39550 case special_some:
39551 case special_some | special_TA:
39552 ret = JS_FALSE;
39553 break;
39554 case special_map:
39555 /* XXX: JS_ArraySpeciesCreate should take int64_t */
39556 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
39557 if (JS_IsException(ret))
39558 goto exception;
39559 break;
39560 case special_filter:
39561 ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
39562 if (JS_IsException(ret))
39563 goto exception;
39564 break;
39565 case special_map | special_TA:
39566 args[0] = obj;
39567 args[1] = JS_NewInt32(ctx, len);
39568 ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
39569 if (JS_IsException(ret))
39570 goto exception;
39571 break;
39572 case special_filter | special_TA:
39573 ret = JS_NewArray(ctx);
39574 if (JS_IsException(ret))
39575 goto exception;
39576 break;
39577 }
39578 n = 0;
39579
39580 for(k = 0; k < len; k++) {
39581 if (special & special_TA) {
39582 val = JS_GetPropertyInt64(ctx, obj, k);
39583 if (JS_IsException(val))
39584 goto exception;
39585 present = TRUE;
39586 } else {
39587 present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
39588 if (present < 0)
39589 goto exception;
39590 }
39591 if (present) {
39592 index_val = JS_NewInt64(ctx, k);
39593 if (JS_IsException(index_val))
39594 goto exception;
39595 args[0] = val;
39596 args[1] = index_val;
39597 args[2] = obj;
39598 res = JS_Call(ctx, func, this_arg, 3, args);
39599 JS_FreeValue(ctx, index_val);
39600 if (JS_IsException(res))
39601 goto exception;
39602 switch (special) {
39603 case special_every:
39604 case special_every | special_TA:
39605 if (!JS_ToBoolFree(ctx, res)) {
39606 ret = JS_FALSE;
39607 goto done;
39608 }
39609 break;
39610 case special_some:
39611 case special_some | special_TA:
39612 if (JS_ToBoolFree(ctx, res)) {
39613 ret = JS_TRUE;
39614 goto done;
39615 }
39616 break;
39617 case special_map:
39618 if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
39619 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39620 goto exception;
39621 break;
39622 case special_map | special_TA:
39623 if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
39624 goto exception;
39625 break;
39626 case special_filter:
39627 case special_filter | special_TA:
39628 if (JS_ToBoolFree(ctx, res)) {
39629 if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
39630 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
39631 goto exception;
39632 }
39633 break;
39634 default:
39635 JS_FreeValue(ctx, res);
39636 break;
39637 }
39638 JS_FreeValue(ctx, val);
39639 val = JS_UNDEFINED;
39640 }
39641 }
39642done:
39643 if (special == (special_filter | special_TA)) {
39644 JSValue arr;
39645 args[0] = obj;
39646 args[1] = JS_NewInt32(ctx, n);
39647 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
39648 if (JS_IsException(arr))
39649 goto exception;
39650 args[0] = ret;
39651 res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
39652 if (check_exception_free(ctx, res))
39653 goto exception;
39654 JS_FreeValue(ctx, ret);
39655 ret = arr;
39656 }
39657 JS_FreeValue(ctx, val);
39658 JS_FreeValue(ctx, obj);
39659 return ret;
39660
39661exception:
39662 JS_FreeValue(ctx, ret);
39663 JS_FreeValue(ctx, val);
39664 JS_FreeValue(ctx, obj);
39665 return JS_EXCEPTION;
39666}
39667
39668#define special_reduce 0
39669#define special_reduceRight 1
39670
39671static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
39672 int argc, JSValueConst *argv, int special)
39673{
39674 JSValue obj, val, index_val, acc, acc1;
39675 JSValueConst args[4];
39676 JSValueConst func;
39677 int64_t len, k, k1;
39678 int present;
39679
39680 acc = JS_UNDEFINED;
39681 val = JS_UNDEFINED;
39682 if (special & special_TA) {
39683 obj = JS_DupValue(ctx, this_val);
39684 len = js_typed_array_get_length_internal(ctx, obj);
39685 if (len < 0)
39686 goto exception;
39687 } else {
39688 obj = JS_ToObject(ctx, this_val);
39689 if (js_get_length64(ctx, &len, obj))
39690 goto exception;
39691 }
39692 func = argv[0];
39693
39694 if (check_function(ctx, func))
39695 goto exception;
39696
39697 k = 0;
39698 if (argc > 1) {
39699 acc = JS_DupValue(ctx, argv[1]);
39700 } else {
39701 for(;;) {
39702 if (k >= len) {
39703 JS_ThrowTypeError(ctx, "empty array");
39704 goto exception;
39705 }
39706 k1 = (special & special_reduceRight) ? len - k - 1 : k;
39707 k++;
39708 if (special & special_TA) {
39709 acc = JS_GetPropertyInt64(ctx, obj, k1);
39710 if (JS_IsException(acc))
39711 goto exception;
39712 break;
39713 } else {
39714 present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
39715 if (present < 0)
39716 goto exception;
39717 if (present)
39718 break;
39719 }
39720 }
39721 }
39722 for (; k < len; k++) {
39723 k1 = (special & special_reduceRight) ? len - k - 1 : k;
39724 if (special & special_TA) {
39725 val = JS_GetPropertyInt64(ctx, obj, k1);
39726 if (JS_IsException(val))
39727 goto exception;
39728 present = TRUE;
39729 } else {
39730 present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
39731 if (present < 0)
39732 goto exception;
39733 }
39734 if (present) {
39735 index_val = JS_NewInt64(ctx, k1);
39736 if (JS_IsException(index_val))
39737 goto exception;
39738 args[0] = acc;
39739 args[1] = val;
39740 args[2] = index_val;
39741 args[3] = obj;
39742 acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
39743 JS_FreeValue(ctx, index_val);
39744 JS_FreeValue(ctx, val);
39745 val = JS_UNDEFINED;
39746 if (JS_IsException(acc1))
39747 goto exception;
39748 JS_FreeValue(ctx, acc);
39749 acc = acc1;
39750 }
39751 }
39752 JS_FreeValue(ctx, obj);
39753 return acc;
39754
39755exception:
39756 JS_FreeValue(ctx, acc);
39757 JS_FreeValue(ctx, val);
39758 JS_FreeValue(ctx, obj);
39759 return JS_EXCEPTION;
39760}
39761
39762static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
39763 int argc, JSValueConst *argv)
39764{
39765 JSValue obj;
39766 int64_t len, start, end;
39767
39768 obj = JS_ToObject(ctx, this_val);
39769 if (js_get_length64(ctx, &len, obj))
39770 goto exception;
39771
39772 start = 0;
39773 if (argc > 1 && !JS_IsUndefined(argv[1])) {
39774 if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
39775 goto exception;
39776 }
39777
39778 end = len;
39779 if (argc > 2 && !JS_IsUndefined(argv[2])) {
39780 if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
39781 goto exception;
39782 }
39783
39784 /* XXX: should special case fast arrays */
39785 while (start < end) {
39786 if (JS_SetPropertyInt64(ctx, obj, start,
39787 JS_DupValue(ctx, argv[0])) < 0)
39788 goto exception;
39789 start++;
39790 }
39791 return obj;
39792
39793 exception:
39794 JS_FreeValue(ctx, obj);
39795 return JS_EXCEPTION;
39796}
39797
39798static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
39799 int argc, JSValueConst *argv)
39800{
39801 JSValue obj, val;
39802 int64_t len, n;
39803 JSValue *arrp;
39804 uint32_t count;
39805 int res;
39806
39807 obj = JS_ToObject(ctx, this_val);
39808 if (js_get_length64(ctx, &len, obj))
39809 goto exception;
39810
39811 res = FALSE;
39812 if (len > 0) {
39813 n = 0;
39814 if (argc > 1) {
39815 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
39816 goto exception;
39817 }
39818 if (js_get_fast_array(ctx, obj, &arrp, &count)) {
39819 for (; n < count; n++) {
39820 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
39821 JS_DupValue(ctx, arrp[n]),
39822 JS_EQ_SAME_VALUE_ZERO)) {
39823 res = TRUE;
39824 goto done;
39825 }
39826 }
39827 }
39828 for (; n < len; n++) {
39829 val = JS_GetPropertyInt64(ctx, obj, n);
39830 if (JS_IsException(val))
39831 goto exception;
39832 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
39833 JS_EQ_SAME_VALUE_ZERO)) {
39834 res = TRUE;
39835 break;
39836 }
39837 }
39838 }
39839 done:
39840 JS_FreeValue(ctx, obj);
39841 return JS_NewBool(ctx, res);
39842
39843 exception:
39844 JS_FreeValue(ctx, obj);
39845 return JS_EXCEPTION;
39846}
39847
39848static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
39849 int argc, JSValueConst *argv)
39850{
39851 JSValue obj, val;
39852 int64_t len, n, res;
39853 JSValue *arrp;
39854 uint32_t count;
39855
39856 obj = JS_ToObject(ctx, this_val);
39857 if (js_get_length64(ctx, &len, obj))
39858 goto exception;
39859
39860 res = -1;
39861 if (len > 0) {
39862 n = 0;
39863 if (argc > 1) {
39864 if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
39865 goto exception;
39866 }
39867 if (js_get_fast_array(ctx, obj, &arrp, &count)) {
39868 for (; n < count; n++) {
39869 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
39870 JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
39871 res = n;
39872 goto done;
39873 }
39874 }
39875 }
39876 for (; n < len; n++) {
39877 int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
39878 if (present < 0)
39879 goto exception;
39880 if (present) {
39881 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
39882 res = n;
39883 break;
39884 }
39885 }
39886 }
39887 }
39888 done:
39889 JS_FreeValue(ctx, obj);
39890 return JS_NewInt64(ctx, res);
39891
39892 exception:
39893 JS_FreeValue(ctx, obj);
39894 return JS_EXCEPTION;
39895}
39896
39897static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
39898 int argc, JSValueConst *argv)
39899{
39900 JSValue obj, val;
39901 int64_t len, n, res;
39902 int present;
39903
39904 obj = JS_ToObject(ctx, this_val);
39905 if (js_get_length64(ctx, &len, obj))
39906 goto exception;
39907
39908 res = -1;
39909 if (len > 0) {
39910 n = len - 1;
39911 if (argc > 1) {
39912 if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
39913 goto exception;
39914 }
39915 /* XXX: should special case fast arrays */
39916 for (; n >= 0; n--) {
39917 present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
39918 if (present < 0)
39919 goto exception;
39920 if (present) {
39921 if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
39922 res = n;
39923 break;
39924 }
39925 }
39926 }
39927 }
39928 JS_FreeValue(ctx, obj);
39929 return JS_NewInt64(ctx, res);
39930
39931 exception:
39932 JS_FreeValue(ctx, obj);
39933 return JS_EXCEPTION;
39934}
39935
39936enum {
39937 ArrayFind,
39938 ArrayFindIndex,
39939 ArrayFindLast,
39940 ArrayFindLastIndex,
39941};
39942
39943static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
39944 int argc, JSValueConst *argv, int mode)
39945{
39946 JSValueConst func, this_arg;
39947 JSValueConst args[3];
39948 JSValue obj, val, index_val, res;
39949 int64_t len, k, end;
39950 int dir;
39951
39952 index_val = JS_UNDEFINED;
39953 val = JS_UNDEFINED;
39954 obj = JS_ToObject(ctx, this_val);
39955 if (js_get_length64(ctx, &len, obj))
39956 goto exception;
39957
39958 func = argv[0];
39959 if (check_function(ctx, func))
39960 goto exception;
39961
39962 this_arg = JS_UNDEFINED;
39963 if (argc > 1)
39964 this_arg = argv[1];
39965
39966 k = 0;
39967 dir = 1;
39968 end = len;
39969 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
39970 k = len - 1;
39971 dir = -1;
39972 end = -1;
39973 }
39974
39975 // TODO(bnoordhuis) add fast path for fast arrays
39976 for(; k != end; k += dir) {
39977 index_val = JS_NewInt64(ctx, k);
39978 if (JS_IsException(index_val))
39979 goto exception;
39980 val = JS_GetPropertyValue(ctx, obj, index_val);
39981 if (JS_IsException(val))
39982 goto exception;
39983 args[0] = val;
39984 args[1] = index_val;
39985 args[2] = this_val;
39986 res = JS_Call(ctx, func, this_arg, 3, args);
39987 if (JS_IsException(res))
39988 goto exception;
39989 if (JS_ToBoolFree(ctx, res)) {
39990 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
39991 JS_FreeValue(ctx, val);
39992 JS_FreeValue(ctx, obj);
39993 return index_val;
39994 } else {
39995 JS_FreeValue(ctx, index_val);
39996 JS_FreeValue(ctx, obj);
39997 return val;
39998 }
39999 }
40000 JS_FreeValue(ctx, val);
40001 JS_FreeValue(ctx, index_val);
40002 }
40003 JS_FreeValue(ctx, obj);
40004 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
40005 return JS_NewInt32(ctx, -1);
40006 else
40007 return JS_UNDEFINED;
40008
40009exception:
40010 JS_FreeValue(ctx, index_val);
40011 JS_FreeValue(ctx, val);
40012 JS_FreeValue(ctx, obj);
40013 return JS_EXCEPTION;
40014}
40015
40016static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
40017 int argc, JSValueConst *argv)
40018{
40019 JSValue obj, method, ret;
40020
40021 obj = JS_ToObject(ctx, this_val);
40022 if (JS_IsException(obj))
40023 return JS_EXCEPTION;
40024 method = JS_GetProperty(ctx, obj, JS_ATOM_join);
40025 if (JS_IsException(method)) {
40026 ret = JS_EXCEPTION;
40027 } else
40028 if (!JS_IsFunction(ctx, method)) {
40029 /* Use intrinsic Object.prototype.toString */
40030 JS_FreeValue(ctx, method);
40031 ret = js_object_toString(ctx, obj, 0, NULL);
40032 } else {
40033 ret = JS_CallFree(ctx, method, obj, 0, NULL);
40034 }
40035 JS_FreeValue(ctx, obj);
40036 return ret;
40037}
40038
40039static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
40040 int argc, JSValueConst *argv, int toLocaleString)
40041{
40042 JSValue obj, sep = JS_UNDEFINED, el;
40043 StringBuffer b_s, *b = &b_s;
40044 JSString *p = NULL;
40045 int64_t i, n;
40046 int c;
40047
40048 obj = JS_ToObject(ctx, this_val);
40049 if (js_get_length64(ctx, &n, obj))
40050 goto exception;
40051
40052 c = ','; /* default separator */
40053 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
40054 sep = JS_ToString(ctx, argv[0]);
40055 if (JS_IsException(sep))
40056 goto exception;
40057 p = JS_VALUE_GET_STRING(sep);
40058 if (p->len == 1 && !p->is_wide_char)
40059 c = p->u.str8[0];
40060 else
40061 c = -1;
40062 }
40063 string_buffer_init(ctx, b, 0);
40064
40065 for(i = 0; i < n; i++) {
40066 if (i > 0) {
40067 if (c >= 0) {
40068 string_buffer_putc8(b, c);
40069 } else {
40070 string_buffer_concat(b, p, 0, p->len);
40071 }
40072 }
40073 el = JS_GetPropertyUint32(ctx, obj, i);
40074 if (JS_IsException(el))
40075 goto fail;
40076 if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
40077 if (toLocaleString) {
40078 el = JS_ToLocaleStringFree(ctx, el);
40079 }
40080 if (string_buffer_concat_value_free(b, el))
40081 goto fail;
40082 }
40083 }
40084 JS_FreeValue(ctx, sep);
40085 JS_FreeValue(ctx, obj);
40086 return string_buffer_end(b);
40087
40088fail:
40089 string_buffer_free(b);
40090 JS_FreeValue(ctx, sep);
40091exception:
40092 JS_FreeValue(ctx, obj);
40093 return JS_EXCEPTION;
40094}
40095
40096static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
40097 int argc, JSValueConst *argv, int shift)
40098{
40099 JSValue obj, res = JS_UNDEFINED;
40100 int64_t len, newLen;
40101 JSValue *arrp;
40102 uint32_t count32;
40103
40104 obj = JS_ToObject(ctx, this_val);
40105 if (js_get_length64(ctx, &len, obj))
40106 goto exception;
40107 newLen = 0;
40108 if (len > 0) {
40109 newLen = len - 1;
40110 /* Special case fast arrays */
40111 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
40112 JSObject *p = JS_VALUE_GET_OBJ(obj);
40113 if (shift) {
40114 res = arrp[0];
40115 memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
40116 p->u.array.count--;
40117 } else {
40118 res = arrp[count32 - 1];
40119 p->u.array.count--;
40120 }
40121 } else {
40122 if (shift) {
40123 res = JS_GetPropertyInt64(ctx, obj, 0);
40124 if (JS_IsException(res))
40125 goto exception;
40126 if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
40127 goto exception;
40128 } else {
40129 res = JS_GetPropertyInt64(ctx, obj, newLen);
40130 if (JS_IsException(res))
40131 goto exception;
40132 }
40133 if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
40134 goto exception;
40135 }
40136 }
40137 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
40138 goto exception;
40139
40140 JS_FreeValue(ctx, obj);
40141 return res;
40142
40143 exception:
40144 JS_FreeValue(ctx, res);
40145 JS_FreeValue(ctx, obj);
40146 return JS_EXCEPTION;
40147}
40148
40149static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
40150 int argc, JSValueConst *argv, int unshift)
40151{
40152 JSValue obj;
40153 int i;
40154 int64_t len, from, newLen;
40155
40156 obj = JS_ToObject(ctx, this_val);
40157 if (js_get_length64(ctx, &len, obj))
40158 goto exception;
40159 newLen = len + argc;
40160 if (newLen > MAX_SAFE_INTEGER) {
40161 JS_ThrowTypeError(ctx, "Array loo long");
40162 goto exception;
40163 }
40164 from = len;
40165 if (unshift && argc > 0) {
40166 if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
40167 goto exception;
40168 from = 0;
40169 }
40170 for(i = 0; i < argc; i++) {
40171 if (JS_SetPropertyInt64(ctx, obj, from + i,
40172 JS_DupValue(ctx, argv[i])) < 0)
40173 goto exception;
40174 }
40175 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
40176 goto exception;
40177
40178 JS_FreeValue(ctx, obj);
40179 return JS_NewInt64(ctx, newLen);
40180
40181 exception:
40182 JS_FreeValue(ctx, obj);
40183 return JS_EXCEPTION;
40184}
40185
40186static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
40187 int argc, JSValueConst *argv)
40188{
40189 JSValue obj, lval, hval;
40190 JSValue *arrp;
40191 int64_t len, l, h;
40192 int l_present, h_present;
40193 uint32_t count32;
40194
40195 lval = JS_UNDEFINED;
40196 obj = JS_ToObject(ctx, this_val);
40197 if (js_get_length64(ctx, &len, obj))
40198 goto exception;
40199
40200 /* Special case fast arrays */
40201 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
40202 uint32_t ll, hh;
40203
40204 if (count32 > 1) {
40205 for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
40206 lval = arrp[ll];
40207 arrp[ll] = arrp[hh];
40208 arrp[hh] = lval;
40209 }
40210 }
40211 return obj;
40212 }
40213
40214 for (l = 0, h = len - 1; l < h; l++, h--) {
40215 l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
40216 if (l_present < 0)
40217 goto exception;
40218 h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
40219 if (h_present < 0)
40220 goto exception;
40221 if (h_present) {
40222 if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
40223 goto exception;
40224
40225 if (l_present) {
40226 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
40227 lval = JS_UNDEFINED;
40228 goto exception;
40229 }
40230 lval = JS_UNDEFINED;
40231 } else {
40232 if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
40233 goto exception;
40234 }
40235 } else {
40236 if (l_present) {
40237 if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
40238 goto exception;
40239 if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
40240 lval = JS_UNDEFINED;
40241 goto exception;
40242 }
40243 lval = JS_UNDEFINED;
40244 }
40245 }
40246 }
40247 return obj;
40248
40249 exception:
40250 JS_FreeValue(ctx, lval);
40251 JS_FreeValue(ctx, obj);
40252 return JS_EXCEPTION;
40253}
40254
40255// Note: a.toReversed() is a.slice().reverse() with the twist that a.slice()
40256// leaves holes in sparse arrays intact whereas a.toReversed() replaces them
40257// with undefined, thus in effect creating a dense array.
40258// Does not use Array[@@species], always returns a base Array.
40259static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val,
40260 int argc, JSValueConst *argv)
40261{
40262 JSValue arr, obj, ret, *arrp, *pval;
40263 JSObject *p;
40264 int64_t i, len;
40265 uint32_t count32;
40266
40267 ret = JS_EXCEPTION;
40268 arr = JS_UNDEFINED;
40269 obj = JS_ToObject(ctx, this_val);
40270 if (js_get_length64(ctx, &len, obj))
40271 goto exception;
40272
40273 arr = js_allocate_fast_array(ctx, len);
40274 if (JS_IsException(arr))
40275 goto exception;
40276
40277 if (len > 0) {
40278 p = JS_VALUE_GET_OBJ(arr);
40279
40280 i = len - 1;
40281 pval = p->u.array.u.values;
40282 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
40283 for (; i >= 0; i--, pval++)
40284 *pval = JS_DupValue(ctx, arrp[i]);
40285 } else {
40286 // Query order is observable; test262 expects descending order.
40287 for (; i >= 0; i--, pval++) {
40288 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
40289 // Exception; initialize remaining elements.
40290 for (; i >= 0; i--, pval++)
40291 *pval = JS_UNDEFINED;
40292 goto exception;
40293 }
40294 }
40295 }
40296
40297 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
40298 goto exception;
40299 }
40300
40301 ret = arr;
40302 arr = JS_UNDEFINED;
40303
40304exception:
40305 JS_FreeValue(ctx, arr);
40306 JS_FreeValue(ctx, obj);
40307 return ret;
40308}
40309
40310static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
40311 int argc, JSValueConst *argv, int splice)
40312{
40313 JSValue obj, arr, val, len_val;
40314 int64_t len, start, k, final, n, count, del_count, new_len;
40315 int kPresent;
40316 JSValue *arrp;
40317 uint32_t count32, i, item_count;
40318
40319 arr = JS_UNDEFINED;
40320 obj = JS_ToObject(ctx, this_val);
40321 if (js_get_length64(ctx, &len, obj))
40322 goto exception;
40323
40324 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
40325 goto exception;
40326
40327 if (splice) {
40328 if (argc == 0) {
40329 item_count = 0;
40330 del_count = 0;
40331 } else
40332 if (argc == 1) {
40333 item_count = 0;
40334 del_count = len - start;
40335 } else {
40336 item_count = argc - 2;
40337 if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
40338 goto exception;
40339 }
40340 if (len + item_count - del_count > MAX_SAFE_INTEGER) {
40341 JS_ThrowTypeError(ctx, "Array loo long");
40342 goto exception;
40343 }
40344 count = del_count;
40345 } else {
40346 item_count = 0; /* avoid warning */
40347 final = len;
40348 if (!JS_IsUndefined(argv[1])) {
40349 if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
40350 goto exception;
40351 }
40352 count = max_int64(final - start, 0);
40353 }
40354 len_val = JS_NewInt64(ctx, count);
40355 arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
40356 JS_FreeValue(ctx, len_val);
40357 if (JS_IsException(arr))
40358 goto exception;
40359
40360 k = start;
40361 final = start + count;
40362 n = 0;
40363 /* The fast array test on arr ensures that
40364 JS_CreateDataPropertyUint32() won't modify obj in case arr is
40365 an exotic object */
40366 /* Special case fast arrays */
40367 if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
40368 js_is_fast_array(ctx, arr)) {
40369 /* XXX: should share code with fast array constructor */
40370 for (; k < final && k < count32; k++, n++) {
40371 if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
40372 goto exception;
40373 }
40374 }
40375 /* Copy the remaining elements if any (handle case of inherited properties) */
40376 for (; k < final; k++, n++) {
40377 kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
40378 if (kPresent < 0)
40379 goto exception;
40380 if (kPresent) {
40381 if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
40382 goto exception;
40383 }
40384 }
40385 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
40386 goto exception;
40387
40388 if (splice) {
40389 new_len = len + item_count - del_count;
40390 if (item_count != del_count) {
40391 if (JS_CopySubArray(ctx, obj, start + item_count,
40392 start + del_count, len - (start + del_count),
40393 item_count <= del_count ? +1 : -1) < 0)
40394 goto exception;
40395
40396 for (k = len; k-- > new_len; ) {
40397 if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
40398 goto exception;
40399 }
40400 }
40401 for (i = 0; i < item_count; i++) {
40402 if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
40403 goto exception;
40404 }
40405 if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
40406 goto exception;
40407 }
40408 JS_FreeValue(ctx, obj);
40409 return arr;
40410
40411 exception:
40412 JS_FreeValue(ctx, obj);
40413 JS_FreeValue(ctx, arr);
40414 return JS_EXCEPTION;
40415}
40416
40417static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val,
40418 int argc, JSValueConst *argv)
40419{
40420 JSValue arr, obj, ret, *arrp, *pval, *last;
40421 JSObject *p;
40422 int64_t i, j, len, newlen, start, add, del;
40423 uint32_t count32;
40424
40425 pval = NULL;
40426 last = NULL;
40427 ret = JS_EXCEPTION;
40428 arr = JS_UNDEFINED;
40429
40430 obj = JS_ToObject(ctx, this_val);
40431 if (js_get_length64(ctx, &len, obj))
40432 goto exception;
40433
40434 start = 0;
40435 if (argc > 0)
40436 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
40437 goto exception;
40438
40439 del = 0;
40440 if (argc > 0)
40441 del = len - start;
40442 if (argc > 1)
40443 if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0))
40444 goto exception;
40445
40446 add = 0;
40447 if (argc > 2)
40448 add = argc - 2;
40449
40450 newlen = len + add - del;
40451 if (newlen > MAX_SAFE_INTEGER) {
40452 JS_ThrowTypeError(ctx, "invalid array length");
40453 goto exception;
40454 }
40455
40456 arr = js_allocate_fast_array(ctx, newlen);
40457 if (JS_IsException(arr))
40458 goto exception;
40459
40460 if (newlen <= 0)
40461 goto done;
40462
40463 p = JS_VALUE_GET_OBJ(arr);
40464 pval = &p->u.array.u.values[0];
40465 last = &p->u.array.u.values[newlen];
40466
40467 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
40468 for (i = 0; i < start; i++, pval++)
40469 *pval = JS_DupValue(ctx, arrp[i]);
40470 for (j = 0; j < add; j++, pval++)
40471 *pval = JS_DupValue(ctx, argv[2 + j]);
40472 for (i += del; i < len; i++, pval++)
40473 *pval = JS_DupValue(ctx, arrp[i]);
40474 } else {
40475 for (i = 0; i < start; i++, pval++)
40476 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
40477 goto exception;
40478 for (j = 0; j < add; j++, pval++)
40479 *pval = JS_DupValue(ctx, argv[2 + j]);
40480 for (i += del; i < len; i++, pval++)
40481 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval))
40482 goto exception;
40483 }
40484
40485 assert(pval == last);
40486
40487 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0)
40488 goto exception;
40489
40490done:
40491 ret = arr;
40492 arr = JS_UNDEFINED;
40493
40494exception:
40495 while (pval != last)
40496 *pval++ = JS_UNDEFINED;
40497
40498 JS_FreeValue(ctx, arr);
40499 JS_FreeValue(ctx, obj);
40500 return ret;
40501}
40502
40503static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
40504 int argc, JSValueConst *argv)
40505{
40506 JSValue obj;
40507 int64_t len, from, to, final, count;
40508
40509 obj = JS_ToObject(ctx, this_val);
40510 if (js_get_length64(ctx, &len, obj))
40511 goto exception;
40512
40513 if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
40514 goto exception;
40515
40516 if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
40517 goto exception;
40518
40519 final = len;
40520 if (argc > 2 && !JS_IsUndefined(argv[2])) {
40521 if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
40522 goto exception;
40523 }
40524
40525 count = min_int64(final - from, len - to);
40526
40527 if (JS_CopySubArray(ctx, obj, to, from, count,
40528 (from < to && to < from + count) ? -1 : +1))
40529 goto exception;
40530
40531 return obj;
40532
40533 exception:
40534 JS_FreeValue(ctx, obj);
40535 return JS_EXCEPTION;
40536}
40537
40538static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
40539 JSValueConst source, int64_t sourceLen,
40540 int64_t targetIndex, int depth,
40541 JSValueConst mapperFunction,
40542 JSValueConst thisArg)
40543{
40544 JSValue element;
40545 int64_t sourceIndex, elementLen;
40546 int present, is_array;
40547
40548 if (js_check_stack_overflow(ctx->rt, 0)) {
40549 JS_ThrowStackOverflow(ctx);
40550 return -1;
40551 }
40552
40553 for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
40554 present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
40555 if (present < 0)
40556 return -1;
40557 if (!present)
40558 continue;
40559 if (!JS_IsUndefined(mapperFunction)) {
40560 JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
40561 element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
40562 JS_FreeValue(ctx, (JSValue)args[0]);
40563 JS_FreeValue(ctx, (JSValue)args[1]);
40564 if (JS_IsException(element))
40565 return -1;
40566 }
40567 if (depth > 0) {
40568 is_array = JS_IsArray(ctx, element);
40569 if (is_array < 0)
40570 goto fail;
40571 if (is_array) {
40572 if (js_get_length64(ctx, &elementLen, element) < 0)
40573 goto fail;
40574 targetIndex = JS_FlattenIntoArray(ctx, target, element,
40575 elementLen, targetIndex,
40576 depth - 1,
40577 JS_UNDEFINED, JS_UNDEFINED);
40578 if (targetIndex < 0)
40579 goto fail;
40580 JS_FreeValue(ctx, element);
40581 continue;
40582 }
40583 }
40584 if (targetIndex >= MAX_SAFE_INTEGER) {
40585 JS_ThrowTypeError(ctx, "Array too long");
40586 goto fail;
40587 }
40588 if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
40589 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
40590 return -1;
40591 targetIndex++;
40592 }
40593 return targetIndex;
40594
40595fail:
40596 JS_FreeValue(ctx, element);
40597 return -1;
40598}
40599
40600static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
40601 int argc, JSValueConst *argv, int map)
40602{
40603 JSValue obj, arr;
40604 JSValueConst mapperFunction, thisArg;
40605 int64_t sourceLen;
40606 int depthNum;
40607
40608 arr = JS_UNDEFINED;
40609 obj = JS_ToObject(ctx, this_val);
40610 if (js_get_length64(ctx, &sourceLen, obj))
40611 goto exception;
40612
40613 depthNum = 1;
40614 mapperFunction = JS_UNDEFINED;
40615 thisArg = JS_UNDEFINED;
40616 if (map) {
40617 mapperFunction = argv[0];
40618 if (argc > 1) {
40619 thisArg = argv[1];
40620 }
40621 if (check_function(ctx, mapperFunction))
40622 goto exception;
40623 } else {
40624 if (argc > 0 && !JS_IsUndefined(argv[0])) {
40625 if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
40626 goto exception;
40627 }
40628 }
40629 arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
40630 if (JS_IsException(arr))
40631 goto exception;
40632 if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
40633 mapperFunction, thisArg) < 0)
40634 goto exception;
40635 JS_FreeValue(ctx, obj);
40636 return arr;
40637
40638exception:
40639 JS_FreeValue(ctx, obj);
40640 JS_FreeValue(ctx, arr);
40641 return JS_EXCEPTION;
40642}
40643
40644/* Array sort */
40645
40646typedef struct ValueSlot {
40647 JSValue val;
40648 JSString *str;
40649 int64_t pos;
40650} ValueSlot;
40651
40652struct array_sort_context {
40653 JSContext *ctx;
40654 int exception;
40655 int has_method;
40656 JSValueConst method;
40657};
40658
40659static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
40660 struct array_sort_context *psc = opaque;
40661 JSContext *ctx = psc->ctx;
40662 JSValueConst argv[2];
40663 JSValue res;
40664 ValueSlot *ap = (ValueSlot *)(void *)a;
40665 ValueSlot *bp = (ValueSlot *)(void *)b;
40666 int cmp;
40667
40668 if (psc->exception)
40669 return 0;
40670
40671 if (psc->has_method) {
40672 /* custom sort function is specified as returning 0 for identical
40673 * objects: avoid method call overhead.
40674 */
40675 if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
40676 goto cmp_same;
40677 argv[0] = ap->val;
40678 argv[1] = bp->val;
40679 res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
40680 if (JS_IsException(res))
40681 goto exception;
40682 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
40683 int val = JS_VALUE_GET_INT(res);
40684 cmp = (val > 0) - (val < 0);
40685 } else {
40686 double val;
40687 if (JS_ToFloat64Free(ctx, &val, res) < 0)
40688 goto exception;
40689 cmp = (val > 0) - (val < 0);
40690 }
40691 } else {
40692 /* Not supposed to bypass ToString even for identical objects as
40693 * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
40694 */
40695 if (!ap->str) {
40696 JSValue str = JS_ToString(ctx, ap->val);
40697 if (JS_IsException(str))
40698 goto exception;
40699 ap->str = JS_VALUE_GET_STRING(str);
40700 }
40701 if (!bp->str) {
40702 JSValue str = JS_ToString(ctx, bp->val);
40703 if (JS_IsException(str))
40704 goto exception;
40705 bp->str = JS_VALUE_GET_STRING(str);
40706 }
40707 cmp = js_string_compare(ctx, ap->str, bp->str);
40708 }
40709 if (cmp != 0)
40710 return cmp;
40711cmp_same:
40712 /* make sort stable: compare array offsets */
40713 return (ap->pos > bp->pos) - (ap->pos < bp->pos);
40714
40715exception:
40716 psc->exception = 1;
40717 return 0;
40718}
40719
40720static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
40721 int argc, JSValueConst *argv)
40722{
40723 struct array_sort_context asc = { ctx, 0, 0, argv[0] };
40724 JSValue obj = JS_UNDEFINED;
40725 ValueSlot *array = NULL;
40726 size_t array_size = 0, pos = 0, n = 0;
40727 int64_t i, len, undefined_count = 0;
40728 int present;
40729
40730 if (!JS_IsUndefined(asc.method)) {
40731 if (check_function(ctx, asc.method))
40732 goto exception;
40733 asc.has_method = 1;
40734 }
40735 obj = JS_ToObject(ctx, this_val);
40736 if (js_get_length64(ctx, &len, obj))
40737 goto exception;
40738
40739 /* XXX: should special case fast arrays */
40740 for (i = 0; i < len; i++) {
40741 if (pos >= array_size) {
40742 size_t new_size, slack;
40743 ValueSlot *new_array;
40744 new_size = (array_size + (array_size >> 1) + 31) & ~15;
40745 new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
40746 if (new_array == NULL)
40747 goto exception;
40748 new_size += slack / sizeof(*new_array);
40749 array = new_array;
40750 array_size = new_size;
40751 }
40752 present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
40753 if (present < 0)
40754 goto exception;
40755 if (present == 0)
40756 continue;
40757 if (JS_IsUndefined(array[pos].val)) {
40758 undefined_count++;
40759 continue;
40760 }
40761 array[pos].str = NULL;
40762 array[pos].pos = i;
40763 pos++;
40764 }
40765 rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
40766 if (asc.exception)
40767 goto exception;
40768
40769 /* XXX: should special case fast arrays */
40770 while (n < pos) {
40771 if (array[n].str)
40772 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
40773 if (array[n].pos == n) {
40774 JS_FreeValue(ctx, array[n].val);
40775 } else {
40776 if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
40777 n++;
40778 goto exception;
40779 }
40780 }
40781 n++;
40782 }
40783 js_free(ctx, array);
40784 for (i = n; undefined_count-- > 0; i++) {
40785 if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
40786 goto fail;
40787 }
40788 for (; i < len; i++) {
40789 if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
40790 goto fail;
40791 }
40792 return obj;
40793
40794exception:
40795 for (; n < pos; n++) {
40796 JS_FreeValue(ctx, array[n].val);
40797 if (array[n].str)
40798 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
40799 }
40800 js_free(ctx, array);
40801fail:
40802 JS_FreeValue(ctx, obj);
40803 return JS_EXCEPTION;
40804}
40805
40806// Note: a.toSorted() is a.slice().sort() with the twist that a.slice()
40807// leaves holes in sparse arrays intact whereas a.toSorted() replaces them
40808// with undefined, thus in effect creating a dense array.
40809// Does not use Array[@@species], always returns a base Array.
40810static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val,
40811 int argc, JSValueConst *argv)
40812{
40813 JSValue arr, obj, ret, *arrp, *pval;
40814 JSObject *p;
40815 int64_t i, len;
40816 uint32_t count32;
40817 int ok;
40818
40819 ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]);
40820 if (!ok)
40821 return JS_ThrowTypeError(ctx, "not a function");
40822
40823 ret = JS_EXCEPTION;
40824 arr = JS_UNDEFINED;
40825 obj = JS_ToObject(ctx, this_val);
40826 if (js_get_length64(ctx, &len, obj))
40827 goto exception;
40828
40829 arr = js_allocate_fast_array(ctx, len);
40830 if (JS_IsException(arr))
40831 goto exception;
40832
40833 if (len > 0) {
40834 p = JS_VALUE_GET_OBJ(arr);
40835 i = 0;
40836 pval = p->u.array.u.values;
40837 if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
40838 for (; i < len; i++, pval++)
40839 *pval = JS_DupValue(ctx, arrp[i]);
40840 } else {
40841 for (; i < len; i++, pval++) {
40842 if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) {
40843 for (; i < len; i++, pval++)
40844 *pval = JS_UNDEFINED;
40845 goto exception;
40846 }
40847 }
40848 }
40849
40850 if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0)
40851 goto exception;
40852 }
40853
40854 ret = js_array_sort(ctx, arr, argc, argv);
40855 if (JS_IsException(ret))
40856 goto exception;
40857 JS_FreeValue(ctx, ret);
40858
40859 ret = arr;
40860 arr = JS_UNDEFINED;
40861
40862exception:
40863 JS_FreeValue(ctx, arr);
40864 JS_FreeValue(ctx, obj);
40865 return ret;
40866}
40867
40868typedef struct JSArrayIteratorData {
40869 JSValue obj;
40870 JSIteratorKindEnum kind;
40871 uint32_t idx;
40872} JSArrayIteratorData;
40873
40874static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
40875{
40876 JSObject *p = JS_VALUE_GET_OBJ(val);
40877 JSArrayIteratorData *it = p->u.array_iterator_data;
40878 if (it) {
40879 JS_FreeValueRT(rt, it->obj);
40880 js_free_rt(rt, it);
40881 }
40882}
40883
40884static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
40885 JS_MarkFunc *mark_func)
40886{
40887 JSObject *p = JS_VALUE_GET_OBJ(val);
40888 JSArrayIteratorData *it = p->u.array_iterator_data;
40889 if (it) {
40890 JS_MarkValue(rt, it->obj, mark_func);
40891 }
40892}
40893
40894static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
40895{
40896 JSValue obj;
40897 int i;
40898
40899 obj = JS_NewArray(ctx);
40900 if (JS_IsException(obj))
40901 return JS_EXCEPTION;
40902 for(i = 0; i < len; i++) {
40903 if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
40904 JS_FreeValue(ctx, obj);
40905 return JS_EXCEPTION;
40906 }
40907 }
40908 return obj;
40909}
40910
40911static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
40912 int argc, JSValueConst *argv, int magic)
40913{
40914 JSValue enum_obj, arr;
40915 JSArrayIteratorData *it;
40916 JSIteratorKindEnum kind;
40917 int class_id;
40918
40919 kind = magic & 3;
40920 if (magic & 4) {
40921 /* string iterator case */
40922 arr = JS_ToStringCheckObject(ctx, this_val);
40923 class_id = JS_CLASS_STRING_ITERATOR;
40924 } else {
40925 arr = JS_ToObject(ctx, this_val);
40926 class_id = JS_CLASS_ARRAY_ITERATOR;
40927 }
40928 if (JS_IsException(arr))
40929 goto fail;
40930 enum_obj = JS_NewObjectClass(ctx, class_id);
40931 if (JS_IsException(enum_obj))
40932 goto fail;
40933 it = js_malloc(ctx, sizeof(*it));
40934 if (!it)
40935 goto fail1;
40936 it->obj = arr;
40937 it->kind = kind;
40938 it->idx = 0;
40939 JS_SetOpaque(enum_obj, it);
40940 return enum_obj;
40941 fail1:
40942 JS_FreeValue(ctx, enum_obj);
40943 fail:
40944 JS_FreeValue(ctx, arr);
40945 return JS_EXCEPTION;
40946}
40947
40948static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
40949 int argc, JSValueConst *argv,
40950 BOOL *pdone, int magic)
40951{
40952 JSArrayIteratorData *it;
40953 uint32_t len, idx;
40954 JSValue val, obj;
40955 JSObject *p;
40956
40957 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
40958 if (!it)
40959 goto fail1;
40960 if (JS_IsUndefined(it->obj))
40961 goto done;
40962 p = JS_VALUE_GET_OBJ(it->obj);
40963 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
40964 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
40965 if (typed_array_is_detached(ctx, p)) {
40966 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
40967 goto fail1;
40968 }
40969 len = p->u.array.count;
40970 } else {
40971 if (js_get_length32(ctx, &len, it->obj)) {
40972 fail1:
40973 *pdone = FALSE;
40974 return JS_EXCEPTION;
40975 }
40976 }
40977 idx = it->idx;
40978 if (idx >= len) {
40979 JS_FreeValue(ctx, it->obj);
40980 it->obj = JS_UNDEFINED;
40981 done:
40982 *pdone = TRUE;
40983 return JS_UNDEFINED;
40984 }
40985 it->idx = idx + 1;
40986 *pdone = FALSE;
40987 if (it->kind == JS_ITERATOR_KIND_KEY) {
40988 return JS_NewUint32(ctx, idx);
40989 } else {
40990 val = JS_GetPropertyUint32(ctx, it->obj, idx);
40991 if (JS_IsException(val))
40992 return JS_EXCEPTION;
40993 if (it->kind == JS_ITERATOR_KIND_VALUE) {
40994 return val;
40995 } else {
40996 JSValueConst args[2];
40997 JSValue num;
40998 num = JS_NewUint32(ctx, idx);
40999 args[0] = num;
41000 args[1] = val;
41001 obj = js_create_array(ctx, 2, args);
41002 JS_FreeValue(ctx, val);
41003 JS_FreeValue(ctx, num);
41004 return obj;
41005 }
41006 }
41007}
41008
41009static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
41010 int argc, JSValueConst *argv)
41011{
41012 return JS_DupValue(ctx, this_val);
41013}
41014
41015static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
41016 JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
41017};
41018
41019static const JSCFunctionListEntry js_array_proto_funcs[] = {
41020 JS_CFUNC_DEF("at", 1, js_array_at ),
41021 JS_CFUNC_DEF("with", 2, js_array_with ),
41022 JS_CFUNC_DEF("concat", 1, js_array_concat ),
41023 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
41024 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
41025 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
41026 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
41027 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
41028 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
41029 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
41030 JS_CFUNC_DEF("fill", 1, js_array_fill ),
41031 JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ),
41032 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ),
41033 JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ),
41034 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ),
41035 JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
41036 JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
41037 JS_CFUNC_DEF("includes", 1, js_array_includes ),
41038 JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
41039 JS_CFUNC_DEF("toString", 0, js_array_toString ),
41040 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
41041 JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
41042 JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
41043 JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
41044 JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
41045 JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
41046 JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ),
41047 JS_CFUNC_DEF("sort", 1, js_array_sort ),
41048 JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ),
41049 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
41050 JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
41051 JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ),
41052 JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
41053 JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
41054 JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
41055 JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
41056 JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
41057 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
41058 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
41059};
41060
41061static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
41062 JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
41063 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
41064};
41065
41066/* Number */
41067
41068static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
41069 int argc, JSValueConst *argv)
41070{
41071 JSValue val, obj;
41072 if (argc == 0) {
41073 val = JS_NewInt32(ctx, 0);
41074 } else {
41075 val = JS_ToNumeric(ctx, argv[0]);
41076 if (JS_IsException(val))
41077 return val;
41078 switch(JS_VALUE_GET_TAG(val)) {
41079 case JS_TAG_SHORT_BIG_INT:
41080 val = JS_NewInt64(ctx, JS_VALUE_GET_SHORT_BIG_INT(val));
41081 if (JS_IsException(val))
41082 return val;
41083 break;
41084 case JS_TAG_BIG_INT:
41085 {
41086 JSBigInt *p = JS_VALUE_GET_PTR(val);
41087 double d;
41088 d = js_bigint_to_float64(ctx, p);
41089 JS_FreeValue(ctx, val);
41090 val = JS_NewFloat64(ctx, d);
41091 }
41092 break;
41093 default:
41094 break;
41095 }
41096 }
41097 if (!JS_IsUndefined(new_target)) {
41098 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
41099 if (!JS_IsException(obj))
41100 JS_SetObjectData(ctx, obj, val);
41101 return obj;
41102 } else {
41103 return val;
41104 }
41105}
41106
41107#if 0
41108static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
41109 int argc, JSValueConst *argv)
41110{
41111 return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
41112}
41113
41114static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
41115 int argc, JSValueConst *argv)
41116{
41117 int64_t v;
41118 if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
41119 return JS_EXCEPTION;
41120 return JS_NewInt64(ctx, v);
41121}
41122#endif
41123
41124static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
41125 int argc, JSValueConst *argv)
41126{
41127 if (!JS_IsNumber(argv[0]))
41128 return JS_FALSE;
41129 return js_global_isNaN(ctx, this_val, argc, argv);
41130}
41131
41132static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
41133 int argc, JSValueConst *argv)
41134{
41135 if (!JS_IsNumber(argv[0]))
41136 return JS_FALSE;
41137 return js_global_isFinite(ctx, this_val, argc, argv);
41138}
41139
41140static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
41141 int argc, JSValueConst *argv)
41142{
41143 int ret;
41144 ret = JS_NumberIsInteger(ctx, argv[0]);
41145 if (ret < 0)
41146 return JS_EXCEPTION;
41147 else
41148 return JS_NewBool(ctx, ret);
41149}
41150
41151static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
41152 int argc, JSValueConst *argv)
41153{
41154 double d;
41155 if (!JS_IsNumber(argv[0]))
41156 return JS_FALSE;
41157 if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
41158 return JS_EXCEPTION;
41159 return JS_NewBool(ctx, is_safe_integer(d));
41160}
41161
41162static const JSCFunctionListEntry js_number_funcs[] = {
41163 /* global ParseInt and parseFloat should be defined already or delayed */
41164 JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
41165 JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
41166 JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
41167 JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
41168 JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
41169 JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
41170 JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
41171 JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
41172 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
41173 JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
41174 JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
41175 JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
41176 JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
41177 JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
41178 //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
41179 //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
41180};
41181
41182static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
41183{
41184 if (JS_IsNumber(this_val))
41185 return JS_DupValue(ctx, this_val);
41186
41187 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
41188 JSObject *p = JS_VALUE_GET_OBJ(this_val);
41189 if (p->class_id == JS_CLASS_NUMBER) {
41190 if (JS_IsNumber(p->u.object_data))
41191 return JS_DupValue(ctx, p->u.object_data);
41192 }
41193 }
41194 return JS_ThrowTypeError(ctx, "not a number");
41195}
41196
41197static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
41198 int argc, JSValueConst *argv)
41199{
41200 return js_thisNumberValue(ctx, this_val);
41201}
41202
41203static int js_get_radix(JSContext *ctx, JSValueConst val)
41204{
41205 int radix;
41206 if (JS_ToInt32Sat(ctx, &radix, val))
41207 return -1;
41208 if (radix < 2 || radix > 36) {
41209 JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
41210 return -1;
41211 }
41212 return radix;
41213}
41214
41215static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
41216 int argc, JSValueConst *argv, int magic)
41217{
41218 JSValue val;
41219 int base, flags;
41220 double d;
41221
41222 val = js_thisNumberValue(ctx, this_val);
41223 if (JS_IsException(val))
41224 return val;
41225 if (magic || JS_IsUndefined(argv[0])) {
41226 base = 10;
41227 } else {
41228 base = js_get_radix(ctx, argv[0]);
41229 if (base < 0)
41230 goto fail;
41231 }
41232 if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
41233 char buf1[70];
41234 int len;
41235 len = i64toa_radix(buf1, JS_VALUE_GET_INT(val), base);
41236 return js_new_string8_len(ctx, buf1, len);
41237 }
41238 if (JS_ToFloat64Free(ctx, &d, val))
41239 return JS_EXCEPTION;
41240 flags = JS_DTOA_FORMAT_FREE;
41241 if (base != 10)
41242 flags |= JS_DTOA_EXP_DISABLED;
41243 return js_dtoa2(ctx, d, base, 0, flags);
41244 fail:
41245 JS_FreeValue(ctx, val);
41246 return JS_EXCEPTION;
41247}
41248
41249static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
41250 int argc, JSValueConst *argv)
41251{
41252 JSValue val;
41253 int f, flags;
41254 double d;
41255
41256 val = js_thisNumberValue(ctx, this_val);
41257 if (JS_IsException(val))
41258 return val;
41259 if (JS_ToFloat64Free(ctx, &d, val))
41260 return JS_EXCEPTION;
41261 if (JS_ToInt32Sat(ctx, &f, argv[0]))
41262 return JS_EXCEPTION;
41263 if (f < 0 || f > 100)
41264 return JS_ThrowRangeError(ctx, "invalid number of digits");
41265 if (fabs(d) >= 1e21)
41266 flags = JS_DTOA_FORMAT_FREE;
41267 else
41268 flags = JS_DTOA_FORMAT_FRAC;
41269 return js_dtoa2(ctx, d, 10, f, flags);
41270}
41271
41272static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
41273 int argc, JSValueConst *argv)
41274{
41275 JSValue val;
41276 int f, flags;
41277 double d;
41278
41279 val = js_thisNumberValue(ctx, this_val);
41280 if (JS_IsException(val))
41281 return val;
41282 if (JS_ToFloat64Free(ctx, &d, val))
41283 return JS_EXCEPTION;
41284 if (JS_ToInt32Sat(ctx, &f, argv[0]))
41285 return JS_EXCEPTION;
41286 if (!isfinite(d)) {
41287 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
41288 }
41289 if (JS_IsUndefined(argv[0])) {
41290 flags = JS_DTOA_FORMAT_FREE;
41291 f = 0;
41292 } else {
41293 if (f < 0 || f > 100)
41294 return JS_ThrowRangeError(ctx, "invalid number of digits");
41295 f++;
41296 flags = JS_DTOA_FORMAT_FIXED;
41297 }
41298 return js_dtoa2(ctx, d, 10, f, flags | JS_DTOA_EXP_ENABLED);
41299}
41300
41301static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
41302 int argc, JSValueConst *argv)
41303{
41304 JSValue val;
41305 int p;
41306 double d;
41307
41308 val = js_thisNumberValue(ctx, this_val);
41309 if (JS_IsException(val))
41310 return val;
41311 if (JS_ToFloat64Free(ctx, &d, val))
41312 return JS_EXCEPTION;
41313 if (JS_IsUndefined(argv[0]))
41314 goto to_string;
41315 if (JS_ToInt32Sat(ctx, &p, argv[0]))
41316 return JS_EXCEPTION;
41317 if (!isfinite(d)) {
41318 to_string:
41319 return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
41320 }
41321 if (p < 1 || p > 100)
41322 return JS_ThrowRangeError(ctx, "invalid number of digits");
41323 return js_dtoa2(ctx, d, 10, p, JS_DTOA_FORMAT_FIXED);
41324}
41325
41326static const JSCFunctionListEntry js_number_proto_funcs[] = {
41327 JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
41328 JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
41329 JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
41330 JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
41331 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
41332 JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
41333};
41334
41335static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
41336 int argc, JSValueConst *argv)
41337{
41338 const char *str, *p;
41339 int radix, flags;
41340 JSValue ret;
41341
41342 str = JS_ToCString(ctx, argv[0]);
41343 if (!str)
41344 return JS_EXCEPTION;
41345 if (JS_ToInt32(ctx, &radix, argv[1])) {
41346 JS_FreeCString(ctx, str);
41347 return JS_EXCEPTION;
41348 }
41349 if (radix != 0 && (radix < 2 || radix > 36)) {
41350 ret = JS_NAN;
41351 } else {
41352 p = str;
41353 p += skip_spaces(p);
41354 flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
41355 ret = js_atof(ctx, p, NULL, radix, flags);
41356 }
41357 JS_FreeCString(ctx, str);
41358 return ret;
41359}
41360
41361static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
41362 int argc, JSValueConst *argv)
41363{
41364 const char *str, *p;
41365 JSValue ret;
41366
41367 str = JS_ToCString(ctx, argv[0]);
41368 if (!str)
41369 return JS_EXCEPTION;
41370 p = str;
41371 p += skip_spaces(p);
41372 ret = js_atof(ctx, p, NULL, 10, 0);
41373 JS_FreeCString(ctx, str);
41374 return ret;
41375}
41376
41377/* Boolean */
41378static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
41379 int argc, JSValueConst *argv)
41380{
41381 JSValue val, obj;
41382 val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
41383 if (!JS_IsUndefined(new_target)) {
41384 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
41385 if (!JS_IsException(obj))
41386 JS_SetObjectData(ctx, obj, val);
41387 return obj;
41388 } else {
41389 return val;
41390 }
41391}
41392
41393static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
41394{
41395 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
41396 return JS_DupValue(ctx, this_val);
41397
41398 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
41399 JSObject *p = JS_VALUE_GET_OBJ(this_val);
41400 if (p->class_id == JS_CLASS_BOOLEAN) {
41401 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
41402 return p->u.object_data;
41403 }
41404 }
41405 return JS_ThrowTypeError(ctx, "not a boolean");
41406}
41407
41408static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
41409 int argc, JSValueConst *argv)
41410{
41411 JSValue val = js_thisBooleanValue(ctx, this_val);
41412 if (JS_IsException(val))
41413 return val;
41414 return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
41415 JS_ATOM_true : JS_ATOM_false);
41416}
41417
41418static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
41419 int argc, JSValueConst *argv)
41420{
41421 return js_thisBooleanValue(ctx, this_val);
41422}
41423
41424static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
41425 JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
41426 JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
41427};
41428
41429/* String */
41430
41431static int js_string_get_own_property(JSContext *ctx,
41432 JSPropertyDescriptor *desc,
41433 JSValueConst obj, JSAtom prop)
41434{
41435 JSObject *p;
41436 JSString *p1;
41437 uint32_t idx, ch;
41438
41439 /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
41440 if (__JS_AtomIsTaggedInt(prop)) {
41441 p = JS_VALUE_GET_OBJ(obj);
41442 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
41443 p1 = JS_VALUE_GET_STRING(p->u.object_data);
41444 idx = __JS_AtomToUInt32(prop);
41445 if (idx < p1->len) {
41446 if (desc) {
41447 ch = string_get(p1, idx);
41448 desc->flags = JS_PROP_ENUMERABLE;
41449 desc->value = js_new_string_char(ctx, ch);
41450 desc->getter = JS_UNDEFINED;
41451 desc->setter = JS_UNDEFINED;
41452 }
41453 return TRUE;
41454 }
41455 }
41456 }
41457 return FALSE;
41458}
41459
41460static int js_string_define_own_property(JSContext *ctx,
41461 JSValueConst this_obj,
41462 JSAtom prop, JSValueConst val,
41463 JSValueConst getter,
41464 JSValueConst setter, int flags)
41465{
41466 uint32_t idx;
41467 JSObject *p;
41468 JSString *p1, *p2;
41469
41470 if (__JS_AtomIsTaggedInt(prop)) {
41471 idx = __JS_AtomToUInt32(prop);
41472 p = JS_VALUE_GET_OBJ(this_obj);
41473 if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
41474 goto def;
41475 p1 = JS_VALUE_GET_STRING(p->u.object_data);
41476 if (idx >= p1->len)
41477 goto def;
41478 if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
41479 goto fail;
41480 /* check that the same value is configured */
41481 if (flags & JS_PROP_HAS_VALUE) {
41482 if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
41483 goto fail;
41484 p2 = JS_VALUE_GET_STRING(val);
41485 if (p2->len != 1)
41486 goto fail;
41487 if (string_get(p1, idx) != string_get(p2, 0)) {
41488 fail:
41489 return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
41490 }
41491 }
41492 return TRUE;
41493 } else {
41494 def:
41495 return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
41496 flags | JS_PROP_NO_EXOTIC);
41497 }
41498}
41499
41500static int js_string_delete_property(JSContext *ctx,
41501 JSValueConst obj, JSAtom prop)
41502{
41503 uint32_t idx;
41504
41505 if (__JS_AtomIsTaggedInt(prop)) {
41506 idx = __JS_AtomToUInt32(prop);
41507 if (idx < js_string_obj_get_length(ctx, obj)) {
41508 return FALSE;
41509 }
41510 }
41511 return TRUE;
41512}
41513
41514static const JSClassExoticMethods js_string_exotic_methods = {
41515 .get_own_property = js_string_get_own_property,
41516 .define_own_property = js_string_define_own_property,
41517 .delete_property = js_string_delete_property,
41518};
41519
41520static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
41521 int argc, JSValueConst *argv)
41522{
41523 JSValue val, obj;
41524 if (argc == 0) {
41525 val = JS_AtomToString(ctx, JS_ATOM_empty_string);
41526 } else {
41527 if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
41528 JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
41529 val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
41530 } else {
41531 val = JS_ToString(ctx, argv[0]);
41532 }
41533 if (JS_IsException(val))
41534 return val;
41535 }
41536 if (!JS_IsUndefined(new_target)) {
41537 JSString *p1 = JS_VALUE_GET_STRING(val);
41538
41539 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
41540 if (JS_IsException(obj)) {
41541 JS_FreeValue(ctx, val);
41542 } else {
41543 JS_SetObjectData(ctx, obj, val);
41544 JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
41545 }
41546 return obj;
41547 } else {
41548 return val;
41549 }
41550}
41551
41552static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
41553{
41554 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING ||
41555 JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING_ROPE)
41556 return JS_DupValue(ctx, this_val);
41557
41558 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
41559 JSObject *p = JS_VALUE_GET_OBJ(this_val);
41560 if (p->class_id == JS_CLASS_STRING) {
41561 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
41562 return JS_DupValue(ctx, p->u.object_data);
41563 }
41564 }
41565 return JS_ThrowTypeError(ctx, "not a string");
41566}
41567
41568static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
41569 int argc, JSValueConst *argv)
41570{
41571 int i;
41572 StringBuffer b_s, *b = &b_s;
41573
41574 string_buffer_init(ctx, b, argc);
41575
41576 for(i = 0; i < argc; i++) {
41577 int32_t c;
41578 if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
41579 string_buffer_free(b);
41580 return JS_EXCEPTION;
41581 }
41582 }
41583 return string_buffer_end(b);
41584}
41585
41586static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
41587 int argc, JSValueConst *argv)
41588{
41589 double d;
41590 int i, c;
41591 StringBuffer b_s, *b = &b_s;
41592
41593 /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
41594
41595 if (string_buffer_init(ctx, b, argc))
41596 goto fail;
41597 for(i = 0; i < argc; i++) {
41598 if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
41599 c = JS_VALUE_GET_INT(argv[i]);
41600 if (c < 0 || c > 0x10ffff)
41601 goto range_error;
41602 } else {
41603 if (JS_ToFloat64(ctx, &d, argv[i]))
41604 goto fail;
41605 if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d)
41606 goto range_error;
41607 }
41608 if (string_buffer_putc(b, c))
41609 goto fail;
41610 }
41611 return string_buffer_end(b);
41612
41613 range_error:
41614 JS_ThrowRangeError(ctx, "invalid code point");
41615 fail:
41616 string_buffer_free(b);
41617 return JS_EXCEPTION;
41618}
41619
41620static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
41621 int argc, JSValueConst *argv)
41622{
41623 // raw(temp,...a)
41624 JSValue cooked, val, raw;
41625 StringBuffer b_s, *b = &b_s;
41626 int64_t i, n;
41627
41628 string_buffer_init(ctx, b, 0);
41629 raw = JS_UNDEFINED;
41630 cooked = JS_ToObject(ctx, argv[0]);
41631 if (JS_IsException(cooked))
41632 goto exception;
41633 raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
41634 if (JS_IsException(raw))
41635 goto exception;
41636 if (js_get_length64(ctx, &n, raw) < 0)
41637 goto exception;
41638
41639 for (i = 0; i < n; i++) {
41640 val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
41641 if (JS_IsException(val))
41642 goto exception;
41643 string_buffer_concat_value_free(b, val);
41644 if (i < n - 1 && i + 1 < argc) {
41645 if (string_buffer_concat_value(b, argv[i + 1]))
41646 goto exception;
41647 }
41648 }
41649 JS_FreeValue(ctx, cooked);
41650 JS_FreeValue(ctx, raw);
41651 return string_buffer_end(b);
41652
41653exception:
41654 JS_FreeValue(ctx, cooked);
41655 JS_FreeValue(ctx, raw);
41656 string_buffer_free(b);
41657 return JS_EXCEPTION;
41658}
41659
41660/* only used in test262 */
41661JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
41662 int argc, JSValueConst *argv)
41663{
41664 uint32_t start, end, i, n;
41665 StringBuffer b_s, *b = &b_s;
41666
41667 if (JS_ToUint32(ctx, &start, argv[0]) ||
41668 JS_ToUint32(ctx, &end, argv[1]))
41669 return JS_EXCEPTION;
41670 end = min_uint32(end, 0x10ffff + 1);
41671
41672 if (start > end) {
41673 start = end;
41674 }
41675 n = end - start;
41676 if (end > 0x10000) {
41677 n += end - max_uint32(start, 0x10000);
41678 }
41679 if (string_buffer_init2(ctx, b, n, end >= 0x100))
41680 return JS_EXCEPTION;
41681 for(i = start; i < end; i++) {
41682 string_buffer_putc(b, i);
41683 }
41684 return string_buffer_end(b);
41685}
41686
41687#if 0
41688static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
41689 int argc, JSValueConst *argv)
41690{
41691 int c;
41692 if (JS_ToInt32(ctx, &c, argv[0]))
41693 return JS_EXCEPTION;
41694 return JS_NewBool(ctx, lre_is_space(c));
41695}
41696#endif
41697
41698static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
41699 int argc, JSValueConst *argv)
41700{
41701 JSValue val, ret;
41702 JSString *p;
41703 int idx, c;
41704
41705 val = JS_ToStringCheckObject(ctx, this_val);
41706 if (JS_IsException(val))
41707 return val;
41708 p = JS_VALUE_GET_STRING(val);
41709 if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
41710 JS_FreeValue(ctx, val);
41711 return JS_EXCEPTION;
41712 }
41713 if (idx < 0 || idx >= p->len) {
41714 ret = JS_NAN;
41715 } else {
41716 c = string_get(p, idx);
41717 ret = JS_NewInt32(ctx, c);
41718 }
41719 JS_FreeValue(ctx, val);
41720 return ret;
41721}
41722
41723static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
41724 int argc, JSValueConst *argv, int is_at)
41725{
41726 JSValue val, ret;
41727 JSString *p;
41728 int idx, c;
41729
41730 val = JS_ToStringCheckObject(ctx, this_val);
41731 if (JS_IsException(val))
41732 return val;
41733 p = JS_VALUE_GET_STRING(val);
41734 if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
41735 JS_FreeValue(ctx, val);
41736 return JS_EXCEPTION;
41737 }
41738 if (idx < 0 && is_at)
41739 idx += p->len;
41740 if (idx < 0 || idx >= p->len) {
41741 if (is_at)
41742 ret = JS_UNDEFINED;
41743 else
41744 ret = JS_AtomToString(ctx, JS_ATOM_empty_string);
41745 } else {
41746 c = string_get(p, idx);
41747 ret = js_new_string_char(ctx, c);
41748 }
41749 JS_FreeValue(ctx, val);
41750 return ret;
41751}
41752
41753static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
41754 int argc, JSValueConst *argv)
41755{
41756 JSValue val, ret;
41757 JSString *p;
41758 int idx, c;
41759
41760 val = JS_ToStringCheckObject(ctx, this_val);
41761 if (JS_IsException(val))
41762 return val;
41763 p = JS_VALUE_GET_STRING(val);
41764 if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
41765 JS_FreeValue(ctx, val);
41766 return JS_EXCEPTION;
41767 }
41768 if (idx < 0 || idx >= p->len) {
41769 ret = JS_UNDEFINED;
41770 } else {
41771 c = string_getc(p, &idx);
41772 ret = JS_NewInt32(ctx, c);
41773 }
41774 JS_FreeValue(ctx, val);
41775 return ret;
41776}
41777
41778static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
41779 int argc, JSValueConst *argv)
41780{
41781 JSValue r;
41782 int i;
41783
41784 /* XXX: Use more efficient method */
41785 /* XXX: This method is OK if r has a single refcount */
41786 /* XXX: should use string_buffer? */
41787 r = JS_ToStringCheckObject(ctx, this_val);
41788 for (i = 0; i < argc; i++) {
41789 if (JS_IsException(r))
41790 break;
41791 r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
41792 }
41793 return r;
41794}
41795
41796static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
41797{
41798 int i, c1, c2;
41799 for (i = 0; i < len; i++) {
41800 if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
41801 return c1 - c2;
41802 }
41803 return 0;
41804}
41805
41806static int string_indexof_char(JSString *p, int c, int from)
41807{
41808 /* assuming 0 <= from <= p->len */
41809 int i, len = p->len;
41810 if (p->is_wide_char) {
41811 for (i = from; i < len; i++) {
41812 if (p->u.str16[i] == c)
41813 return i;
41814 }
41815 } else {
41816 if ((c & ~0xff) == 0) {
41817 for (i = from; i < len; i++) {
41818 if (p->u.str8[i] == (uint8_t)c)
41819 return i;
41820 }
41821 }
41822 }
41823 return -1;
41824}
41825
41826static int string_indexof(JSString *p1, JSString *p2, int from)
41827{
41828 /* assuming 0 <= from <= p1->len */
41829 int c, i, j, len1 = p1->len, len2 = p2->len;
41830 if (len2 == 0)
41831 return from;
41832 for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
41833 j = string_indexof_char(p1, c, i);
41834 if (j < 0 || j + len2 > len1)
41835 break;
41836 if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
41837 return j;
41838 }
41839 return -1;
41840}
41841
41842static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
41843{
41844 if (!unicode || index >= p->len || !p->is_wide_char) {
41845 index++;
41846 } else {
41847 int index32 = (int)index;
41848 string_getc(p, &index32);
41849 index = index32;
41850 }
41851 return index;
41852}
41853
41854/* return the position of the first invalid character in the string or
41855 -1 if none */
41856static int js_string_find_invalid_codepoint(JSString *p)
41857{
41858 int i;
41859 if (!p->is_wide_char)
41860 return -1;
41861 for(i = 0; i < p->len; i++) {
41862 uint32_t c = p->u.str16[i];
41863 if (is_surrogate(c)) {
41864 if (is_hi_surrogate(c) && (i + 1) < p->len
41865 && is_lo_surrogate(p->u.str16[i + 1])) {
41866 i++;
41867 } else {
41868 return i;
41869 }
41870 }
41871 }
41872 return -1;
41873}
41874
41875static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val,
41876 int argc, JSValueConst *argv)
41877{
41878 JSValue str;
41879 JSString *p;
41880 BOOL ret;
41881
41882 str = JS_ToStringCheckObject(ctx, this_val);
41883 if (JS_IsException(str))
41884 return JS_EXCEPTION;
41885 p = JS_VALUE_GET_STRING(str);
41886 ret = (js_string_find_invalid_codepoint(p) < 0);
41887 JS_FreeValue(ctx, str);
41888 return JS_NewBool(ctx, ret);
41889}
41890
41891static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val,
41892 int argc, JSValueConst *argv)
41893{
41894 JSValue str, ret;
41895 JSString *p;
41896 int i;
41897
41898 str = JS_ToStringCheckObject(ctx, this_val);
41899 if (JS_IsException(str))
41900 return JS_EXCEPTION;
41901
41902 p = JS_VALUE_GET_STRING(str);
41903 /* avoid reallocating the string if it is well-formed */
41904 i = js_string_find_invalid_codepoint(p);
41905 if (i < 0)
41906 return str;
41907
41908 ret = js_new_string16_len(ctx, p->u.str16, p->len);
41909 JS_FreeValue(ctx, str);
41910 if (JS_IsException(ret))
41911 return JS_EXCEPTION;
41912
41913 p = JS_VALUE_GET_STRING(ret);
41914 for (; i < p->len; i++) {
41915 uint32_t c = p->u.str16[i];
41916 if (is_surrogate(c)) {
41917 if (is_hi_surrogate(c) && (i + 1) < p->len
41918 && is_lo_surrogate(p->u.str16[i + 1])) {
41919 i++;
41920 } else {
41921 p->u.str16[i] = 0xFFFD;
41922 }
41923 }
41924 }
41925 return ret;
41926}
41927
41928static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
41929 int argc, JSValueConst *argv, int lastIndexOf)
41930{
41931 JSValue str, v;
41932 int i, len, v_len, pos, start, stop, ret, inc;
41933 JSString *p;
41934 JSString *p1;
41935
41936 str = JS_ToStringCheckObject(ctx, this_val);
41937 if (JS_IsException(str))
41938 return str;
41939 v = JS_ToString(ctx, argv[0]);
41940 if (JS_IsException(v))
41941 goto fail;
41942 p = JS_VALUE_GET_STRING(str);
41943 p1 = JS_VALUE_GET_STRING(v);
41944 len = p->len;
41945 v_len = p1->len;
41946 if (lastIndexOf) {
41947 pos = len - v_len;
41948 if (argc > 1) {
41949 double d;
41950 if (JS_ToFloat64(ctx, &d, argv[1]))
41951 goto fail;
41952 if (!isnan(d)) {
41953 if (d <= 0)
41954 pos = 0;
41955 else if (d < pos)
41956 pos = d;
41957 }
41958 }
41959 start = pos;
41960 stop = 0;
41961 inc = -1;
41962 } else {
41963 pos = 0;
41964 if (argc > 1) {
41965 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
41966 goto fail;
41967 }
41968 start = pos;
41969 stop = len - v_len;
41970 inc = 1;
41971 }
41972 ret = -1;
41973 if (len >= v_len && inc * (stop - start) >= 0) {
41974 for (i = start;; i += inc) {
41975 if (!string_cmp(p, p1, i, 0, v_len)) {
41976 ret = i;
41977 break;
41978 }
41979 if (i == stop)
41980 break;
41981 }
41982 }
41983 JS_FreeValue(ctx, str);
41984 JS_FreeValue(ctx, v);
41985 return JS_NewInt32(ctx, ret);
41986
41987fail:
41988 JS_FreeValue(ctx, str);
41989 JS_FreeValue(ctx, v);
41990 return JS_EXCEPTION;
41991}
41992
41993/* return < 0 if exception or TRUE/FALSE */
41994static int js_is_regexp(JSContext *ctx, JSValueConst obj);
41995
41996static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
41997 int argc, JSValueConst *argv, int magic)
41998{
41999 JSValue str, v = JS_UNDEFINED;
42000 int i, len, v_len, pos, start, stop, ret;
42001 JSString *p;
42002 JSString *p1;
42003
42004 str = JS_ToStringCheckObject(ctx, this_val);
42005 if (JS_IsException(str))
42006 return str;
42007 ret = js_is_regexp(ctx, argv[0]);
42008 if (ret) {
42009 if (ret > 0)
42010 JS_ThrowTypeError(ctx, "regexp not supported");
42011 goto fail;
42012 }
42013 v = JS_ToString(ctx, argv[0]);
42014 if (JS_IsException(v))
42015 goto fail;
42016 p = JS_VALUE_GET_STRING(str);
42017 p1 = JS_VALUE_GET_STRING(v);
42018 len = p->len;
42019 v_len = p1->len;
42020 pos = (magic == 2) ? len : 0;
42021 if (argc > 1 && !JS_IsUndefined(argv[1])) {
42022 if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
42023 goto fail;
42024 }
42025 len -= v_len;
42026 ret = 0;
42027 if (magic == 0) {
42028 start = pos;
42029 stop = len;
42030 } else {
42031 if (magic == 1) {
42032 if (pos > len)
42033 goto done;
42034 } else {
42035 pos -= v_len;
42036 }
42037 start = stop = pos;
42038 }
42039 if (start >= 0 && start <= stop) {
42040 for (i = start;; i++) {
42041 if (!string_cmp(p, p1, i, 0, v_len)) {
42042 ret = 1;
42043 break;
42044 }
42045 if (i == stop)
42046 break;
42047 }
42048 }
42049 done:
42050 JS_FreeValue(ctx, str);
42051 JS_FreeValue(ctx, v);
42052 return JS_NewBool(ctx, ret);
42053
42054fail:
42055 JS_FreeValue(ctx, str);
42056 JS_FreeValue(ctx, v);
42057 return JS_EXCEPTION;
42058}
42059
42060static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
42061{
42062 int ret;
42063 JSValue flags;
42064
42065 ret = js_is_regexp(ctx, regexp);
42066 if (ret < 0)
42067 return -1;
42068 if (ret) {
42069 flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
42070 if (JS_IsException(flags))
42071 return -1;
42072 if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
42073 JS_ThrowTypeError(ctx, "cannot convert to object");
42074 return -1;
42075 }
42076 flags = JS_ToStringFree(ctx, flags);
42077 if (JS_IsException(flags))
42078 return -1;
42079 ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
42080 JS_FreeValue(ctx, flags);
42081 if (ret < 0) {
42082 JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
42083 return -1;
42084 }
42085 }
42086 return 0;
42087}
42088
42089static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
42090 int argc, JSValueConst *argv, int atom)
42091{
42092 // match(rx), search(rx), matchAll(rx)
42093 // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
42094 JSValueConst O = this_val, regexp = argv[0], args[2];
42095 JSValue matcher, S, rx, result, str;
42096 int args_len;
42097
42098 if (JS_IsUndefined(O) || JS_IsNull(O))
42099 return JS_ThrowTypeError(ctx, "cannot convert to object");
42100
42101 if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
42102 matcher = JS_GetProperty(ctx, regexp, atom);
42103 if (JS_IsException(matcher))
42104 return JS_EXCEPTION;
42105 if (atom == JS_ATOM_Symbol_matchAll) {
42106 if (check_regexp_g_flag(ctx, regexp) < 0) {
42107 JS_FreeValue(ctx, matcher);
42108 return JS_EXCEPTION;
42109 }
42110 }
42111 if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
42112 return JS_CallFree(ctx, matcher, regexp, 1, &O);
42113 }
42114 }
42115 S = JS_ToString(ctx, O);
42116 if (JS_IsException(S))
42117 return JS_EXCEPTION;
42118 args_len = 1;
42119 args[0] = regexp;
42120 str = JS_UNDEFINED;
42121 if (atom == JS_ATOM_Symbol_matchAll) {
42122 str = js_new_string8(ctx, "g");
42123 if (JS_IsException(str))
42124 goto fail;
42125 args[args_len++] = (JSValueConst)str;
42126 }
42127 rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
42128 JS_FreeValue(ctx, str);
42129 if (JS_IsException(rx)) {
42130 fail:
42131 JS_FreeValue(ctx, S);
42132 return JS_EXCEPTION;
42133 }
42134 result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
42135 JS_FreeValue(ctx, S);
42136 return result;
42137}
42138
42139static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
42140 int argc, JSValueConst *argv)
42141{
42142 // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
42143 JSValueConst matched, str, captures, namedCaptures, rep;
42144 JSValue capture, name, s;
42145 uint32_t position, len, matched_len, captures_len;
42146 int i, j, j0, k, k1;
42147 int c, c1;
42148 StringBuffer b_s, *b = &b_s;
42149 JSString *sp, *rp;
42150
42151 matched = argv[0];
42152 str = argv[1];
42153 captures = argv[3];
42154 namedCaptures = argv[4];
42155 rep = argv[5];
42156
42157 if (!JS_IsString(rep) || !JS_IsString(str))
42158 return JS_ThrowTypeError(ctx, "not a string");
42159
42160 sp = JS_VALUE_GET_STRING(str);
42161 rp = JS_VALUE_GET_STRING(rep);
42162
42163 string_buffer_init(ctx, b, 0);
42164
42165 captures_len = 0;
42166 if (!JS_IsUndefined(captures)) {
42167 if (js_get_length32(ctx, &captures_len, captures))
42168 goto exception;
42169 }
42170 if (js_get_length32(ctx, &matched_len, matched))
42171 goto exception;
42172 if (JS_ToUint32(ctx, &position, argv[2]) < 0)
42173 goto exception;
42174
42175 len = rp->len;
42176 i = 0;
42177 for(;;) {
42178 j = string_indexof_char(rp, '$', i);
42179 if (j < 0 || j + 1 >= len)
42180 break;
42181 string_buffer_concat(b, rp, i, j);
42182 j0 = j++;
42183 c = string_get(rp, j++);
42184 if (c == '$') {
42185 string_buffer_putc8(b, '$');
42186 } else if (c == '&') {
42187 if (string_buffer_concat_value(b, matched))
42188 goto exception;
42189 } else if (c == '`') {
42190 string_buffer_concat(b, sp, 0, position);
42191 } else if (c == '\'') {
42192 string_buffer_concat(b, sp, position + matched_len, sp->len);
42193 } else if (c >= '0' && c <= '9') {
42194 k = c - '0';
42195 if (j < len) {
42196 c1 = string_get(rp, j);
42197 if (c1 >= '0' && c1 <= '9') {
42198 /* This behavior is specified in ES6 and refined in ECMA 2019 */
42199 /* ECMA 2019 does not have the extra test, but
42200 Test262 S15.5.4.11_A3_T1..3 require this behavior */
42201 k1 = k * 10 + c1 - '0';
42202 if (k1 >= 1 && k1 < captures_len) {
42203 k = k1;
42204 j++;
42205 }
42206 }
42207 }
42208 if (k >= 1 && k < captures_len) {
42209 s = JS_GetPropertyInt64(ctx, captures, k);
42210 if (JS_IsException(s))
42211 goto exception;
42212 if (!JS_IsUndefined(s)) {
42213 if (string_buffer_concat_value_free(b, s))
42214 goto exception;
42215 }
42216 } else {
42217 goto norep;
42218 }
42219 } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
42220 k = string_indexof_char(rp, '>', j);
42221 if (k < 0)
42222 goto norep;
42223 name = js_sub_string(ctx, rp, j, k);
42224 if (JS_IsException(name))
42225 goto exception;
42226 capture = JS_GetPropertyValue(ctx, namedCaptures, name);
42227 if (JS_IsException(capture))
42228 goto exception;
42229 if (!JS_IsUndefined(capture)) {
42230 if (string_buffer_concat_value_free(b, capture))
42231 goto exception;
42232 }
42233 j = k + 1;
42234 } else {
42235 norep:
42236 string_buffer_concat(b, rp, j0, j);
42237 }
42238 i = j;
42239 }
42240 string_buffer_concat(b, rp, i, rp->len);
42241 return string_buffer_end(b);
42242exception:
42243 string_buffer_free(b);
42244 return JS_EXCEPTION;
42245}
42246
42247static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
42248 int argc, JSValueConst *argv,
42249 int is_replaceAll)
42250{
42251 // replace(rx, rep)
42252 JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
42253 JSValueConst args[6];
42254 JSValue str, search_str, replaceValue_str, repl_str;
42255 JSString *sp, *searchp;
42256 StringBuffer b_s, *b = &b_s;
42257 int pos, functionalReplace, endOfLastMatch;
42258 BOOL is_first;
42259
42260 if (JS_IsUndefined(O) || JS_IsNull(O))
42261 return JS_ThrowTypeError(ctx, "cannot convert to object");
42262
42263 search_str = JS_UNDEFINED;
42264 replaceValue_str = JS_UNDEFINED;
42265 repl_str = JS_UNDEFINED;
42266
42267 if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
42268 JSValue replacer;
42269 if (is_replaceAll) {
42270 if (check_regexp_g_flag(ctx, searchValue) < 0)
42271 return JS_EXCEPTION;
42272 }
42273 replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
42274 if (JS_IsException(replacer))
42275 return JS_EXCEPTION;
42276 if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
42277 args[0] = O;
42278 args[1] = replaceValue;
42279 return JS_CallFree(ctx, replacer, searchValue, 2, args);
42280 }
42281 }
42282 string_buffer_init(ctx, b, 0);
42283
42284 str = JS_ToString(ctx, O);
42285 if (JS_IsException(str))
42286 goto exception;
42287 search_str = JS_ToString(ctx, searchValue);
42288 if (JS_IsException(search_str))
42289 goto exception;
42290 functionalReplace = JS_IsFunction(ctx, replaceValue);
42291 if (!functionalReplace) {
42292 replaceValue_str = JS_ToString(ctx, replaceValue);
42293 if (JS_IsException(replaceValue_str))
42294 goto exception;
42295 }
42296
42297 sp = JS_VALUE_GET_STRING(str);
42298 searchp = JS_VALUE_GET_STRING(search_str);
42299 endOfLastMatch = 0;
42300 is_first = TRUE;
42301 for(;;) {
42302 if (unlikely(searchp->len == 0)) {
42303 if (is_first)
42304 pos = 0;
42305 else if (endOfLastMatch >= sp->len)
42306 pos = -1;
42307 else
42308 pos = endOfLastMatch + 1;
42309 } else {
42310 pos = string_indexof(sp, searchp, endOfLastMatch);
42311 }
42312 if (pos < 0) {
42313 if (is_first) {
42314 string_buffer_free(b);
42315 JS_FreeValue(ctx, search_str);
42316 JS_FreeValue(ctx, replaceValue_str);
42317 return str;
42318 } else {
42319 break;
42320 }
42321 }
42322 if (functionalReplace) {
42323 args[0] = search_str;
42324 args[1] = JS_NewInt32(ctx, pos);
42325 args[2] = str;
42326 repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
42327 } else {
42328 args[0] = search_str;
42329 args[1] = str;
42330 args[2] = JS_NewInt32(ctx, pos);
42331 args[3] = JS_UNDEFINED;
42332 args[4] = JS_UNDEFINED;
42333 args[5] = replaceValue_str;
42334 repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
42335 }
42336 if (JS_IsException(repl_str))
42337 goto exception;
42338
42339 string_buffer_concat(b, sp, endOfLastMatch, pos);
42340 string_buffer_concat_value_free(b, repl_str);
42341 endOfLastMatch = pos + searchp->len;
42342 is_first = FALSE;
42343 if (!is_replaceAll)
42344 break;
42345 }
42346 string_buffer_concat(b, sp, endOfLastMatch, sp->len);
42347 JS_FreeValue(ctx, search_str);
42348 JS_FreeValue(ctx, replaceValue_str);
42349 JS_FreeValue(ctx, str);
42350 return string_buffer_end(b);
42351
42352exception:
42353 string_buffer_free(b);
42354 JS_FreeValue(ctx, search_str);
42355 JS_FreeValue(ctx, replaceValue_str);
42356 JS_FreeValue(ctx, str);
42357 return JS_EXCEPTION;
42358}
42359
42360static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
42361 int argc, JSValueConst *argv)
42362{
42363 // split(sep, limit)
42364 JSValueConst O = this_val, separator = argv[0], limit = argv[1];
42365 JSValueConst args[2];
42366 JSValue S, A, R, T;
42367 uint32_t lim, lengthA;
42368 int64_t p, q, s, r, e;
42369 JSString *sp, *rp;
42370
42371 if (JS_IsUndefined(O) || JS_IsNull(O))
42372 return JS_ThrowTypeError(ctx, "cannot convert to object");
42373
42374 S = JS_UNDEFINED;
42375 A = JS_UNDEFINED;
42376 R = JS_UNDEFINED;
42377
42378 if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
42379 JSValue splitter;
42380 splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
42381 if (JS_IsException(splitter))
42382 return JS_EXCEPTION;
42383 if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
42384 args[0] = O;
42385 args[1] = limit;
42386 return JS_CallFree(ctx, splitter, separator, 2, args);
42387 }
42388 }
42389 S = JS_ToString(ctx, O);
42390 if (JS_IsException(S))
42391 goto exception;
42392 A = JS_NewArray(ctx);
42393 if (JS_IsException(A))
42394 goto exception;
42395 lengthA = 0;
42396 if (JS_IsUndefined(limit)) {
42397 lim = 0xffffffff;
42398 } else {
42399 if (JS_ToUint32(ctx, &lim, limit) < 0)
42400 goto exception;
42401 }
42402 sp = JS_VALUE_GET_STRING(S);
42403 s = sp->len;
42404 R = JS_ToString(ctx, separator);
42405 if (JS_IsException(R))
42406 goto exception;
42407 rp = JS_VALUE_GET_STRING(R);
42408 r = rp->len;
42409 p = 0;
42410 if (lim == 0)
42411 goto done;
42412 if (JS_IsUndefined(separator))
42413 goto add_tail;
42414 if (s == 0) {
42415 if (r != 0)
42416 goto add_tail;
42417 goto done;
42418 }
42419 q = p;
42420 for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
42421 e = string_indexof(sp, rp, q);
42422 if (e < 0)
42423 break;
42424 T = js_sub_string(ctx, sp, p, e);
42425 if (JS_IsException(T))
42426 goto exception;
42427 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
42428 goto exception;
42429 if (lengthA == lim)
42430 goto done;
42431 }
42432add_tail:
42433 T = js_sub_string(ctx, sp, p, s);
42434 if (JS_IsException(T))
42435 goto exception;
42436 if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
42437 goto exception;
42438done:
42439 JS_FreeValue(ctx, S);
42440 JS_FreeValue(ctx, R);
42441 return A;
42442
42443exception:
42444 JS_FreeValue(ctx, A);
42445 JS_FreeValue(ctx, S);
42446 JS_FreeValue(ctx, R);
42447 return JS_EXCEPTION;
42448}
42449
42450static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
42451 int argc, JSValueConst *argv)
42452{
42453 JSValue str, ret;
42454 int a, b, start, end;
42455 JSString *p;
42456
42457 str = JS_ToStringCheckObject(ctx, this_val);
42458 if (JS_IsException(str))
42459 return str;
42460 p = JS_VALUE_GET_STRING(str);
42461 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
42462 JS_FreeValue(ctx, str);
42463 return JS_EXCEPTION;
42464 }
42465 b = p->len;
42466 if (!JS_IsUndefined(argv[1])) {
42467 if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
42468 JS_FreeValue(ctx, str);
42469 return JS_EXCEPTION;
42470 }
42471 }
42472 if (a < b) {
42473 start = a;
42474 end = b;
42475 } else {
42476 start = b;
42477 end = a;
42478 }
42479 ret = js_sub_string(ctx, p, start, end);
42480 JS_FreeValue(ctx, str);
42481 return ret;
42482}
42483
42484static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
42485 int argc, JSValueConst *argv)
42486{
42487 JSValue str, ret;
42488 int a, len, n;
42489 JSString *p;
42490
42491 str = JS_ToStringCheckObject(ctx, this_val);
42492 if (JS_IsException(str))
42493 return str;
42494 p = JS_VALUE_GET_STRING(str);
42495 len = p->len;
42496 if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
42497 JS_FreeValue(ctx, str);
42498 return JS_EXCEPTION;
42499 }
42500 n = len - a;
42501 if (!JS_IsUndefined(argv[1])) {
42502 if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
42503 JS_FreeValue(ctx, str);
42504 return JS_EXCEPTION;
42505 }
42506 }
42507 ret = js_sub_string(ctx, p, a, a + n);
42508 JS_FreeValue(ctx, str);
42509 return ret;
42510}
42511
42512static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
42513 int argc, JSValueConst *argv)
42514{
42515 JSValue str, ret;
42516 int len, start, end;
42517 JSString *p;
42518
42519 str = JS_ToStringCheckObject(ctx, this_val);
42520 if (JS_IsException(str))
42521 return str;
42522 p = JS_VALUE_GET_STRING(str);
42523 len = p->len;
42524 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
42525 JS_FreeValue(ctx, str);
42526 return JS_EXCEPTION;
42527 }
42528 end = len;
42529 if (!JS_IsUndefined(argv[1])) {
42530 if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
42531 JS_FreeValue(ctx, str);
42532 return JS_EXCEPTION;
42533 }
42534 }
42535 ret = js_sub_string(ctx, p, start, max_int(end, start));
42536 JS_FreeValue(ctx, str);
42537 return ret;
42538}
42539
42540static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
42541 int argc, JSValueConst *argv, int padEnd)
42542{
42543 JSValue str, v = JS_UNDEFINED;
42544 StringBuffer b_s, *b = &b_s;
42545 JSString *p, *p1 = NULL;
42546 int n, len, c = ' ';
42547
42548 str = JS_ToStringCheckObject(ctx, this_val);
42549 if (JS_IsException(str))
42550 goto fail1;
42551 if (JS_ToInt32Sat(ctx, &n, argv[0]))
42552 goto fail2;
42553 p = JS_VALUE_GET_STRING(str);
42554 len = p->len;
42555 if (len >= n)
42556 return str;
42557 if (argc > 1 && !JS_IsUndefined(argv[1])) {
42558 v = JS_ToString(ctx, argv[1]);
42559 if (JS_IsException(v))
42560 goto fail2;
42561 p1 = JS_VALUE_GET_STRING(v);
42562 if (p1->len == 0) {
42563 JS_FreeValue(ctx, v);
42564 return str;
42565 }
42566 if (p1->len == 1) {
42567 c = string_get(p1, 0);
42568 p1 = NULL;
42569 }
42570 }
42571 if (n > JS_STRING_LEN_MAX) {
42572 JS_ThrowRangeError(ctx, "invalid string length");
42573 goto fail3;
42574 }
42575 if (string_buffer_init(ctx, b, n))
42576 goto fail3;
42577 n -= len;
42578 if (padEnd) {
42579 if (string_buffer_concat(b, p, 0, len))
42580 goto fail;
42581 }
42582 if (p1) {
42583 while (n > 0) {
42584 int chunk = min_int(n, p1->len);
42585 if (string_buffer_concat(b, p1, 0, chunk))
42586 goto fail;
42587 n -= chunk;
42588 }
42589 } else {
42590 if (string_buffer_fill(b, c, n))
42591 goto fail;
42592 }
42593 if (!padEnd) {
42594 if (string_buffer_concat(b, p, 0, len))
42595 goto fail;
42596 }
42597 JS_FreeValue(ctx, v);
42598 JS_FreeValue(ctx, str);
42599 return string_buffer_end(b);
42600
42601fail:
42602 string_buffer_free(b);
42603fail3:
42604 JS_FreeValue(ctx, v);
42605fail2:
42606 JS_FreeValue(ctx, str);
42607fail1:
42608 return JS_EXCEPTION;
42609}
42610
42611static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
42612 int argc, JSValueConst *argv)
42613{
42614 JSValue str;
42615 StringBuffer b_s, *b = &b_s;
42616 JSString *p;
42617 int64_t val;
42618 int n, len;
42619
42620 str = JS_ToStringCheckObject(ctx, this_val);
42621 if (JS_IsException(str))
42622 goto fail;
42623 if (JS_ToInt64Sat(ctx, &val, argv[0]))
42624 goto fail;
42625 if (val < 0 || val > 2147483647) {
42626 JS_ThrowRangeError(ctx, "invalid repeat count");
42627 goto fail;
42628 }
42629 n = val;
42630 p = JS_VALUE_GET_STRING(str);
42631 len = p->len;
42632 if (len == 0 || n == 1)
42633 return str;
42634 // XXX: potential arithmetic overflow
42635 if (val * len > JS_STRING_LEN_MAX) {
42636 JS_ThrowRangeError(ctx, "invalid string length");
42637 goto fail;
42638 }
42639 if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
42640 goto fail;
42641 if (len == 1) {
42642 string_buffer_fill(b, string_get(p, 0), n);
42643 } else {
42644 while (n-- > 0) {
42645 string_buffer_concat(b, p, 0, len);
42646 }
42647 }
42648 JS_FreeValue(ctx, str);
42649 return string_buffer_end(b);
42650
42651fail:
42652 JS_FreeValue(ctx, str);
42653 return JS_EXCEPTION;
42654}
42655
42656static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
42657 int argc, JSValueConst *argv, int magic)
42658{
42659 JSValue str, ret;
42660 int a, b, len;
42661 JSString *p;
42662
42663 str = JS_ToStringCheckObject(ctx, this_val);
42664 if (JS_IsException(str))
42665 return str;
42666 p = JS_VALUE_GET_STRING(str);
42667 a = 0;
42668 b = len = p->len;
42669 if (magic & 1) {
42670 while (a < len && lre_is_space(string_get(p, a)))
42671 a++;
42672 }
42673 if (magic & 2) {
42674 while (b > a && lre_is_space(string_get(p, b - 1)))
42675 b--;
42676 }
42677 ret = js_sub_string(ctx, p, a, b);
42678 JS_FreeValue(ctx, str);
42679 return ret;
42680}
42681
42682static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
42683 int argc, JSValueConst *argv)
42684{
42685 return JS_ToQuotedString(ctx, this_val);
42686}
42687
42688/* return 0 if before the first char */
42689static int string_prevc(JSString *p, int *pidx)
42690{
42691 int idx, c, c1;
42692
42693 idx = *pidx;
42694 if (idx <= 0)
42695 return 0;
42696 idx--;
42697 if (p->is_wide_char) {
42698 c = p->u.str16[idx];
42699 if (is_lo_surrogate(c) && idx > 0) {
42700 c1 = p->u.str16[idx - 1];
42701 if (is_hi_surrogate(c1)) {
42702 c = from_surrogate(c1, c);
42703 idx--;
42704 }
42705 }
42706 } else {
42707 c = p->u.str8[idx];
42708 }
42709 *pidx = idx;
42710 return c;
42711}
42712
42713static BOOL test_final_sigma(JSString *p, int sigma_pos)
42714{
42715 int k, c1;
42716
42717 /* before C: skip case ignorable chars and check there is
42718 a cased letter */
42719 k = sigma_pos;
42720 for(;;) {
42721 c1 = string_prevc(p, &k);
42722 if (!lre_is_case_ignorable(c1))
42723 break;
42724 }
42725 if (!lre_is_cased(c1))
42726 return FALSE;
42727
42728 /* after C: skip case ignorable chars and check there is
42729 no cased letter */
42730 k = sigma_pos + 1;
42731 for(;;) {
42732 if (k >= p->len)
42733 return TRUE;
42734 c1 = string_getc(p, &k);
42735 if (!lre_is_case_ignorable(c1))
42736 break;
42737 }
42738 return !lre_is_cased(c1);
42739}
42740
42741static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
42742 int argc, JSValueConst *argv, int to_lower)
42743{
42744 JSValue val;
42745 StringBuffer b_s, *b = &b_s;
42746 JSString *p;
42747 int i, c, j, l;
42748 uint32_t res[LRE_CC_RES_LEN_MAX];
42749
42750 val = JS_ToStringCheckObject(ctx, this_val);
42751 if (JS_IsException(val))
42752 return val;
42753 p = JS_VALUE_GET_STRING(val);
42754 if (p->len == 0)
42755 return val;
42756 if (string_buffer_init(ctx, b, p->len))
42757 goto fail;
42758 for(i = 0; i < p->len;) {
42759 c = string_getc(p, &i);
42760 if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
42761 res[0] = 0x3c2; /* final sigma */
42762 l = 1;
42763 } else {
42764 l = lre_case_conv(res, c, to_lower);
42765 }
42766 for(j = 0; j < l; j++) {
42767 if (string_buffer_putc(b, res[j]))
42768 goto fail;
42769 }
42770 }
42771 JS_FreeValue(ctx, val);
42772 return string_buffer_end(b);
42773 fail:
42774 JS_FreeValue(ctx, val);
42775 string_buffer_free(b);
42776 return JS_EXCEPTION;
42777}
42778
42779#ifdef CONFIG_ALL_UNICODE
42780
42781/* return (-1, NULL) if exception, otherwise (len, buf) */
42782static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
42783{
42784 JSValue val;
42785 JSString *p;
42786 uint32_t *buf;
42787 int i, j, len;
42788
42789 val = JS_ToString(ctx, val1);
42790 if (JS_IsException(val))
42791 return -1;
42792 p = JS_VALUE_GET_STRING(val);
42793 len = p->len;
42794 /* UTF32 buffer length is len minus the number of correct surrogates pairs */
42795 buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
42796 if (!buf) {
42797 JS_FreeValue(ctx, val);
42798 goto fail;
42799 }
42800 for(i = j = 0; i < len;)
42801 buf[j++] = string_getc(p, &i);
42802 JS_FreeValue(ctx, val);
42803 *pbuf = buf;
42804 return j;
42805 fail:
42806 *pbuf = NULL;
42807 return -1;
42808}
42809
42810static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
42811{
42812 int i;
42813 StringBuffer b_s, *b = &b_s;
42814 if (string_buffer_init(ctx, b, len))
42815 return JS_EXCEPTION;
42816 for(i = 0; i < len; i++) {
42817 if (string_buffer_putc(b, buf[i]))
42818 goto fail;
42819 }
42820 return string_buffer_end(b);
42821 fail:
42822 string_buffer_free(b);
42823 return JS_EXCEPTION;
42824}
42825
42826static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf,
42827 JSValueConst val,
42828 UnicodeNormalizationEnum n_type)
42829{
42830 int buf_len, out_len;
42831 uint32_t *buf, *out_buf;
42832
42833 buf_len = JS_ToUTF32String(ctx, &buf, val);
42834 if (buf_len < 0)
42835 return -1;
42836 out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
42837 ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
42838 js_free(ctx, buf);
42839 if (out_len < 0)
42840 return -1;
42841 *pout_buf = out_buf;
42842 return out_len;
42843}
42844
42845static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
42846 int argc, JSValueConst *argv)
42847{
42848 const char *form, *p;
42849 size_t form_len;
42850 int is_compat, out_len;
42851 UnicodeNormalizationEnum n_type;
42852 JSValue val;
42853 uint32_t *out_buf;
42854
42855 val = JS_ToStringCheckObject(ctx, this_val);
42856 if (JS_IsException(val))
42857 return val;
42858
42859 if (argc == 0 || JS_IsUndefined(argv[0])) {
42860 n_type = UNICODE_NFC;
42861 } else {
42862 form = JS_ToCStringLen(ctx, &form_len, argv[0]);
42863 if (!form)
42864 goto fail1;
42865 p = form;
42866 if (p[0] != 'N' || p[1] != 'F')
42867 goto bad_form;
42868 p += 2;
42869 is_compat = FALSE;
42870 if (*p == 'K') {
42871 is_compat = TRUE;
42872 p++;
42873 }
42874 if (*p == 'C' || *p == 'D') {
42875 n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
42876 if ((p + 1 - form) != form_len)
42877 goto bad_form;
42878 } else {
42879 bad_form:
42880 JS_FreeCString(ctx, form);
42881 JS_ThrowRangeError(ctx, "bad normalization form");
42882 fail1:
42883 JS_FreeValue(ctx, val);
42884 return JS_EXCEPTION;
42885 }
42886 JS_FreeCString(ctx, form);
42887 }
42888
42889 out_len = js_string_normalize1(ctx, &out_buf, val, n_type);
42890 JS_FreeValue(ctx, val);
42891 if (out_len < 0)
42892 return JS_EXCEPTION;
42893 val = JS_NewUTF32String(ctx, out_buf, out_len);
42894 js_free(ctx, out_buf);
42895 return val;
42896}
42897
42898/* return < 0, 0 or > 0 */
42899static int js_UTF32_compare(const uint32_t *buf1, int buf1_len,
42900 const uint32_t *buf2, int buf2_len)
42901{
42902 int i, len, c, res;
42903 len = min_int(buf1_len, buf2_len);
42904 for(i = 0; i < len; i++) {
42905 /* Note: range is limited so a subtraction is valid */
42906 c = buf1[i] - buf2[i];
42907 if (c != 0)
42908 return c;
42909 }
42910 if (buf1_len == buf2_len)
42911 res = 0;
42912 else if (buf1_len < buf2_len)
42913 res = -1;
42914 else
42915 res = 1;
42916 return res;
42917}
42918
42919static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
42920 int argc, JSValueConst *argv)
42921{
42922 JSValue a, b;
42923 int cmp, a_len, b_len;
42924 uint32_t *a_buf, *b_buf;
42925
42926 a = JS_ToStringCheckObject(ctx, this_val);
42927 if (JS_IsException(a))
42928 return JS_EXCEPTION;
42929 b = JS_ToString(ctx, argv[0]);
42930 if (JS_IsException(b)) {
42931 JS_FreeValue(ctx, a);
42932 return JS_EXCEPTION;
42933 }
42934 a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC);
42935 JS_FreeValue(ctx, a);
42936 if (a_len < 0) {
42937 JS_FreeValue(ctx, b);
42938 return JS_EXCEPTION;
42939 }
42940
42941 b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC);
42942 JS_FreeValue(ctx, b);
42943 if (b_len < 0) {
42944 js_free(ctx, a_buf);
42945 return JS_EXCEPTION;
42946 }
42947 cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len);
42948 js_free(ctx, a_buf);
42949 js_free(ctx, b_buf);
42950 return JS_NewInt32(ctx, cmp);
42951}
42952#else /* CONFIG_ALL_UNICODE */
42953static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
42954 int argc, JSValueConst *argv)
42955{
42956 JSValue a, b;
42957 int cmp;
42958
42959 a = JS_ToStringCheckObject(ctx, this_val);
42960 if (JS_IsException(a))
42961 return JS_EXCEPTION;
42962 b = JS_ToString(ctx, argv[0]);
42963 if (JS_IsException(b)) {
42964 JS_FreeValue(ctx, a);
42965 return JS_EXCEPTION;
42966 }
42967 cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
42968 JS_FreeValue(ctx, a);
42969 JS_FreeValue(ctx, b);
42970 return JS_NewInt32(ctx, cmp);
42971}
42972#endif /* !CONFIG_ALL_UNICODE */
42973
42974/* also used for String.prototype.valueOf */
42975static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
42976 int argc, JSValueConst *argv)
42977{
42978 return js_thisStringValue(ctx, this_val);
42979}
42980
42981#if 0
42982static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
42983 int argc, JSValueConst *argv)
42984{
42985 return JS_ToStringCheckObject(ctx, argv[0]);
42986}
42987
42988static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
42989 int argc, JSValueConst *argv)
42990{
42991 return JS_ToString(ctx, argv[0]);
42992}
42993
42994static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
42995 this_val,
42996 int argc, JSValueConst *argv)
42997{
42998 JSValue str;
42999 int idx;
43000 BOOL is_unicode;
43001 JSString *p;
43002
43003 str = JS_ToString(ctx, argv[0]);
43004 if (JS_IsException(str))
43005 return str;
43006 if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
43007 JS_FreeValue(ctx, str);
43008 return JS_EXCEPTION;
43009 }
43010 is_unicode = JS_ToBool(ctx, argv[2]);
43011 p = JS_VALUE_GET_STRING(str);
43012 if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
43013 idx++;
43014 } else {
43015 string_getc(p, &idx);
43016 }
43017 JS_FreeValue(ctx, str);
43018 return JS_NewInt32(ctx, idx);
43019}
43020#endif
43021
43022/* String Iterator */
43023
43024static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
43025 int argc, JSValueConst *argv,
43026 BOOL *pdone, int magic)
43027{
43028 JSArrayIteratorData *it;
43029 uint32_t idx, c, start;
43030 JSString *p;
43031
43032 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
43033 if (!it) {
43034 *pdone = FALSE;
43035 return JS_EXCEPTION;
43036 }
43037 if (JS_IsUndefined(it->obj))
43038 goto done;
43039 p = JS_VALUE_GET_STRING(it->obj);
43040 idx = it->idx;
43041 if (idx >= p->len) {
43042 JS_FreeValue(ctx, it->obj);
43043 it->obj = JS_UNDEFINED;
43044 done:
43045 *pdone = TRUE;
43046 return JS_UNDEFINED;
43047 }
43048
43049 start = idx;
43050 c = string_getc(p, (int *)&idx);
43051 it->idx = idx;
43052 *pdone = FALSE;
43053 if (c <= 0xffff) {
43054 return js_new_string_char(ctx, c);
43055 } else {
43056 return js_new_string16_len(ctx, p->u.str16 + start, 2);
43057 }
43058}
43059
43060/* ES6 Annex B 2.3.2 etc. */
43061enum {
43062 magic_string_anchor,
43063 magic_string_big,
43064 magic_string_blink,
43065 magic_string_bold,
43066 magic_string_fixed,
43067 magic_string_fontcolor,
43068 magic_string_fontsize,
43069 magic_string_italics,
43070 magic_string_link,
43071 magic_string_small,
43072 magic_string_strike,
43073 magic_string_sub,
43074 magic_string_sup,
43075};
43076
43077static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
43078 int argc, JSValueConst *argv, int magic)
43079{
43080 JSValue str;
43081 const JSString *p;
43082 StringBuffer b_s, *b = &b_s;
43083 static struct { const char *tag, *attr; } const defs[] = {
43084 { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
43085 { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
43086 { "a", "href" }, { "small", NULL }, { "strike", NULL },
43087 { "sub", NULL }, { "sup", NULL },
43088 };
43089
43090 str = JS_ToStringCheckObject(ctx, this_val);
43091 if (JS_IsException(str))
43092 return JS_EXCEPTION;
43093 string_buffer_init(ctx, b, 7);
43094 string_buffer_putc8(b, '<');
43095 string_buffer_puts8(b, defs[magic].tag);
43096 if (defs[magic].attr) {
43097 // r += " " + attr + "=\"" + value + "\"";
43098 JSValue value;
43099 int i;
43100
43101 string_buffer_putc8(b, ' ');
43102 string_buffer_puts8(b, defs[magic].attr);
43103 string_buffer_puts8(b, "=\"");
43104 value = JS_ToStringCheckObject(ctx, argv[0]);
43105 if (JS_IsException(value)) {
43106 JS_FreeValue(ctx, str);
43107 string_buffer_free(b);
43108 return JS_EXCEPTION;
43109 }
43110 p = JS_VALUE_GET_STRING(value);
43111 for (i = 0; i < p->len; i++) {
43112 int c = string_get(p, i);
43113 if (c == '"') {
43114 string_buffer_puts8(b, "&quot;");
43115 } else {
43116 string_buffer_putc16(b, c);
43117 }
43118 }
43119 JS_FreeValue(ctx, value);
43120 string_buffer_putc8(b, '\"');
43121 }
43122 // return r + ">" + str + "</" + tag + ">";
43123 string_buffer_putc8(b, '>');
43124 string_buffer_concat_value_free(b, str);
43125 string_buffer_puts8(b, "</");
43126 string_buffer_puts8(b, defs[magic].tag);
43127 string_buffer_putc8(b, '>');
43128 return string_buffer_end(b);
43129}
43130
43131static const JSCFunctionListEntry js_string_funcs[] = {
43132 JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
43133 JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
43134 JS_CFUNC_DEF("raw", 1, js_string_raw ),
43135 //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
43136 //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
43137 //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
43138 //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
43139 //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
43140};
43141
43142static const JSCFunctionListEntry js_string_proto_funcs[] = {
43143 JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
43144 JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ),
43145 JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
43146 JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ),
43147 JS_CFUNC_DEF("concat", 1, js_string_concat ),
43148 JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
43149 JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ),
43150 JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ),
43151 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
43152 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
43153 JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
43154 JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
43155 JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
43156 JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
43157 JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
43158 JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
43159 JS_CFUNC_DEF("split", 2, js_string_split ),
43160 JS_CFUNC_DEF("substring", 2, js_string_substring ),
43161 JS_CFUNC_DEF("substr", 2, js_string_substr ),
43162 JS_CFUNC_DEF("slice", 2, js_string_slice ),
43163 JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
43164 JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
43165 JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
43166 JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
43167 JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
43168 JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
43169 JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
43170 JS_ALIAS_DEF("trimRight", "trimEnd" ),
43171 JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
43172 JS_ALIAS_DEF("trimLeft", "trimStart" ),
43173 JS_CFUNC_DEF("toString", 0, js_string_toString ),
43174 JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
43175 JS_CFUNC_DEF("__quote", 1, js_string___quote ),
43176 JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
43177 JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
43178 JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
43179 JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
43180 JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
43181 /* ES6 Annex B 2.3.2 etc. */
43182 JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
43183 JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
43184 JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
43185 JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
43186 JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
43187 JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
43188 JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
43189 JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
43190 JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
43191 JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
43192 JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
43193 JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
43194 JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
43195};
43196
43197static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
43198 JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
43199 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
43200};
43201
43202static const JSCFunctionListEntry js_string_proto_normalize[] = {
43203#ifdef CONFIG_ALL_UNICODE
43204 JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
43205#endif
43206 JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
43207};
43208
43209void JS_AddIntrinsicStringNormalize(JSContext *ctx)
43210{
43211 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
43212 countof(js_string_proto_normalize));
43213}
43214
43215/* Math */
43216
43217/* precondition: a and b are not NaN */
43218static double js_fmin(double a, double b)
43219{
43220 if (a == 0 && b == 0) {
43221 JSFloat64Union a1, b1;
43222 a1.d = a;
43223 b1.d = b;
43224 a1.u64 |= b1.u64;
43225 return a1.d;
43226 } else {
43227 return fmin(a, b);
43228 }
43229}
43230
43231/* precondition: a and b are not NaN */
43232static double js_fmax(double a, double b)
43233{
43234 if (a == 0 && b == 0) {
43235 JSFloat64Union a1, b1;
43236 a1.d = a;
43237 b1.d = b;
43238 a1.u64 &= b1.u64;
43239 return a1.d;
43240 } else {
43241 return fmax(a, b);
43242 }
43243}
43244
43245static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
43246 int argc, JSValueConst *argv, int magic)
43247{
43248 BOOL is_max = magic;
43249 double r, a;
43250 int i;
43251 uint32_t tag;
43252
43253 if (unlikely(argc == 0)) {
43254 return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
43255 }
43256
43257 tag = JS_VALUE_GET_TAG(argv[0]);
43258 if (tag == JS_TAG_INT) {
43259 int a1, r1 = JS_VALUE_GET_INT(argv[0]);
43260 for(i = 1; i < argc; i++) {
43261 tag = JS_VALUE_GET_TAG(argv[i]);
43262 if (tag != JS_TAG_INT) {
43263 r = r1;
43264 goto generic_case;
43265 }
43266 a1 = JS_VALUE_GET_INT(argv[i]);
43267 if (is_max)
43268 r1 = max_int(r1, a1);
43269 else
43270 r1 = min_int(r1, a1);
43271
43272 }
43273 return JS_NewInt32(ctx, r1);
43274 } else {
43275 if (JS_ToFloat64(ctx, &r, argv[0]))
43276 return JS_EXCEPTION;
43277 i = 1;
43278 generic_case:
43279 while (i < argc) {
43280 if (JS_ToFloat64(ctx, &a, argv[i]))
43281 return JS_EXCEPTION;
43282 if (!isnan(r)) {
43283 if (isnan(a)) {
43284 r = a;
43285 } else {
43286 if (is_max)
43287 r = js_fmax(r, a);
43288 else
43289 r = js_fmin(r, a);
43290 }
43291 }
43292 i++;
43293 }
43294 return JS_NewFloat64(ctx, r);
43295 }
43296}
43297
43298static double js_math_sign(double a)
43299{
43300 if (isnan(a) || a == 0.0)
43301 return a;
43302 if (a < 0)
43303 return -1;
43304 else
43305 return 1;
43306}
43307
43308static double js_math_round(double a)
43309{
43310 JSFloat64Union u;
43311 uint64_t frac_mask, one;
43312 unsigned int e, s;
43313
43314 u.d = a;
43315 e = (u.u64 >> 52) & 0x7ff;
43316 if (e < 1023) {
43317 /* abs(a) < 1 */
43318 if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
43319 /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
43320 u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
43321 } else {
43322 /* return +/-0.0 */
43323 u.u64 &= (uint64_t)1 << 63;
43324 }
43325 } else if (e < (1023 + 52)) {
43326 s = u.u64 >> 63;
43327 one = (uint64_t)1 << (52 - (e - 1023));
43328 frac_mask = one - 1;
43329 u.u64 += (one >> 1) - s;
43330 u.u64 &= ~frac_mask; /* truncate to an integer */
43331 }
43332 /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
43333 return u.d;
43334}
43335
43336static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
43337 int argc, JSValueConst *argv)
43338{
43339 double r, a;
43340 int i;
43341
43342 r = 0;
43343 if (argc > 0) {
43344 if (JS_ToFloat64(ctx, &r, argv[0]))
43345 return JS_EXCEPTION;
43346 if (argc == 1) {
43347 r = fabs(r);
43348 } else {
43349 /* use the built-in function to minimize precision loss */
43350 for (i = 1; i < argc; i++) {
43351 if (JS_ToFloat64(ctx, &a, argv[i]))
43352 return JS_EXCEPTION;
43353 r = hypot(r, a);
43354 }
43355 }
43356 }
43357 return JS_NewFloat64(ctx, r);
43358}
43359
43360static double js_math_fround(double a)
43361{
43362 return (float)a;
43363}
43364
43365static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
43366 int argc, JSValueConst *argv)
43367{
43368 uint32_t a, b, c;
43369 int32_t d;
43370
43371 if (JS_ToUint32(ctx, &a, argv[0]))
43372 return JS_EXCEPTION;
43373 if (JS_ToUint32(ctx, &b, argv[1]))
43374 return JS_EXCEPTION;
43375 c = a * b;
43376 memcpy(&d, &c, sizeof(d));
43377 return JS_NewInt32(ctx, d);
43378}
43379
43380static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
43381 int argc, JSValueConst *argv)
43382{
43383 uint32_t a, r;
43384
43385 if (JS_ToUint32(ctx, &a, argv[0]))
43386 return JS_EXCEPTION;
43387 if (a == 0)
43388 r = 32;
43389 else
43390 r = clz32(a);
43391 return JS_NewInt32(ctx, r);
43392}
43393
43394/* xorshift* random number generator by Marsaglia */
43395static uint64_t xorshift64star(uint64_t *pstate)
43396{
43397 uint64_t x;
43398 x = *pstate;
43399 x ^= x >> 12;
43400 x ^= x << 25;
43401 x ^= x >> 27;
43402 *pstate = x;
43403 return x * 0x2545F4914F6CDD1D;
43404}
43405
43406static void js_random_init(JSContext *ctx)
43407{
43408 struct timeval tv;
43409 gettimeofday(&tv, NULL);
43410 ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
43411 /* the state must be non zero */
43412 if (ctx->random_state == 0)
43413 ctx->random_state = 1;
43414}
43415
43416static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
43417 int argc, JSValueConst *argv)
43418{
43419 JSFloat64Union u;
43420 uint64_t v;
43421
43422 v = xorshift64star(&ctx->random_state);
43423 /* 1.0 <= u.d < 2 */
43424 u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
43425 return __JS_NewFloat64(ctx, u.d - 1.0);
43426}
43427
43428static const JSCFunctionListEntry js_math_funcs[] = {
43429 JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
43430 JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
43431 JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
43432 JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
43433 JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
43434 JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
43435 JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
43436
43437 JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
43438 JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
43439 JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
43440 JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
43441 JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
43442 JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
43443 JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
43444 JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
43445 JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
43446 JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
43447 /* ES6 */
43448 JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
43449 JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
43450 JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
43451 JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
43452 JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
43453 JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
43454 JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
43455 JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
43456 JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
43457 JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
43458 JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
43459 JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
43460 JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
43461 JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
43462 JS_CFUNC_DEF("random", 0, js_math_random ),
43463 JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
43464 JS_CFUNC_DEF("imul", 2, js_math_imul ),
43465 JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
43466 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
43467 JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
43468 JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
43469 JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
43470 JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
43471 JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
43472 JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
43473 JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
43474 JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
43475};
43476
43477static const JSCFunctionListEntry js_math_obj[] = {
43478 JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
43479};
43480
43481/* Date */
43482
43483/* OS dependent. d = argv[0] is in ms from 1970. Return the difference
43484 between UTC time and local time 'd' in minutes */
43485static int getTimezoneOffset(int64_t time)
43486{
43487 time_t ti;
43488 int res;
43489
43490 time /= 1000; /* convert to seconds */
43491 if (sizeof(time_t) == 4) {
43492 /* on 32-bit systems, we need to clamp the time value to the
43493 range of `time_t`. This is better than truncating values to
43494 32 bits and hopefully provides the same result as 64-bit
43495 implementation of localtime_r.
43496 */
43497 if ((time_t)-1 < 0) {
43498 if (time < INT32_MIN) {
43499 time = INT32_MIN;
43500 } else if (time > INT32_MAX) {
43501 time = INT32_MAX;
43502 }
43503 } else {
43504 if (time < 0) {
43505 time = 0;
43506 } else if (time > UINT32_MAX) {
43507 time = UINT32_MAX;
43508 }
43509 }
43510 }
43511 ti = time;
43512#if defined(_WIN32)
43513 {
43514 struct tm *tm;
43515 time_t gm_ti, loc_ti;
43516
43517 tm = gmtime(&ti);
43518 gm_ti = mktime(tm);
43519
43520 tm = localtime(&ti);
43521 loc_ti = mktime(tm);
43522
43523 res = (gm_ti - loc_ti) / 60;
43524 }
43525#else
43526 {
43527 struct tm tm;
43528 localtime_r(&ti, &tm);
43529 res = -tm.tm_gmtoff / 60;
43530 }
43531#endif
43532 return res;
43533}
43534
43535#if 0
43536static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
43537 int argc, JSValueConst *argv)
43538{
43539 double dd;
43540
43541 if (JS_ToFloat64(ctx, &dd, argv[0]))
43542 return JS_EXCEPTION;
43543 if (isnan(dd))
43544 return __JS_NewFloat64(ctx, dd);
43545 else
43546 return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
43547}
43548
43549static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
43550 JSValueConst def_proto)
43551{
43552 JSValue proto;
43553 proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
43554 if (JS_IsException(proto))
43555 return proto;
43556 if (!JS_IsObject(proto)) {
43557 JS_FreeValue(ctx, proto);
43558 proto = JS_DupValue(ctx, def_proto);
43559 }
43560 return proto;
43561}
43562
43563/* create a new date object */
43564static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
43565 int argc, JSValueConst *argv)
43566{
43567 JSValue obj, proto;
43568 proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
43569 if (JS_IsException(proto))
43570 return proto;
43571 obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
43572 JS_FreeValue(ctx, proto);
43573 if (!JS_IsException(obj))
43574 JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
43575 return obj;
43576}
43577#endif
43578
43579/* RegExp */
43580
43581static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
43582{
43583 JSObject *p = JS_VALUE_GET_OBJ(val);
43584 JSRegExp *re = &p->u.regexp;
43585 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
43586 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
43587}
43588
43589/* create a string containing the RegExp bytecode */
43590static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
43591 JSValueConst flags)
43592{
43593 const char *str;
43594 int re_flags, mask;
43595 uint8_t *re_bytecode_buf;
43596 size_t i, len;
43597 int re_bytecode_len;
43598 JSValue ret;
43599 char error_msg[64];
43600
43601 re_flags = 0;
43602 if (!JS_IsUndefined(flags)) {
43603 str = JS_ToCStringLen(ctx, &len, flags);
43604 if (!str)
43605 return JS_EXCEPTION;
43606 /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
43607 for (i = 0; i < len; i++) {
43608 switch(str[i]) {
43609 case 'd':
43610 mask = LRE_FLAG_INDICES;
43611 break;
43612 case 'g':
43613 mask = LRE_FLAG_GLOBAL;
43614 break;
43615 case 'i':
43616 mask = LRE_FLAG_IGNORECASE;
43617 break;
43618 case 'm':
43619 mask = LRE_FLAG_MULTILINE;
43620 break;
43621 case 's':
43622 mask = LRE_FLAG_DOTALL;
43623 break;
43624 case 'u':
43625 mask = LRE_FLAG_UNICODE;
43626 break;
43627 case 'y':
43628 mask = LRE_FLAG_STICKY;
43629 break;
43630 default:
43631 goto bad_flags;
43632 }
43633 if ((re_flags & mask) != 0) {
43634 bad_flags:
43635 JS_FreeCString(ctx, str);
43636 return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
43637 }
43638 re_flags |= mask;
43639 }
43640 JS_FreeCString(ctx, str);
43641 }
43642
43643 str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE));
43644 if (!str)
43645 return JS_EXCEPTION;
43646 re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
43647 sizeof(error_msg), str, len, re_flags, ctx);
43648 JS_FreeCString(ctx, str);
43649 if (!re_bytecode_buf) {
43650 JS_ThrowSyntaxError(ctx, "%s", error_msg);
43651 return JS_EXCEPTION;
43652 }
43653
43654 ret = js_new_string8_len(ctx, (const char *)re_bytecode_buf, re_bytecode_len);
43655 js_free(ctx, re_bytecode_buf);
43656 return ret;
43657}
43658
43659/* create a RegExp object from a string containing the RegExp bytecode
43660 and the source pattern */
43661static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
43662 JSValue pattern, JSValue bc)
43663{
43664 JSValue obj;
43665 JSObject *p;
43666 JSRegExp *re;
43667
43668 /* sanity check */
43669 if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
43670 JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
43671 JS_ThrowTypeError(ctx, "string expected");
43672 fail:
43673 JS_FreeValue(ctx, bc);
43674 JS_FreeValue(ctx, pattern);
43675 return JS_EXCEPTION;
43676 }
43677
43678 obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
43679 if (JS_IsException(obj))
43680 goto fail;
43681 p = JS_VALUE_GET_OBJ(obj);
43682 re = &p->u.regexp;
43683 re->pattern = JS_VALUE_GET_STRING(pattern);
43684 re->bytecode = JS_VALUE_GET_STRING(bc);
43685 JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
43686 JS_PROP_WRITABLE);
43687 return obj;
43688}
43689
43690static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
43691{
43692 if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
43693 JSObject *p = JS_VALUE_GET_OBJ(obj);
43694 if (p->class_id == JS_CLASS_REGEXP)
43695 return &p->u.regexp;
43696 }
43697 if (throw_error) {
43698 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
43699 }
43700 return NULL;
43701}
43702
43703/* return < 0 if exception or TRUE/FALSE */
43704static int js_is_regexp(JSContext *ctx, JSValueConst obj)
43705{
43706 JSValue m;
43707
43708 if (!JS_IsObject(obj))
43709 return FALSE;
43710 m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
43711 if (JS_IsException(m))
43712 return -1;
43713 if (!JS_IsUndefined(m))
43714 return JS_ToBoolFree(ctx, m);
43715 return js_get_regexp(ctx, obj, FALSE) != NULL;
43716}
43717
43718static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
43719 int argc, JSValueConst *argv)
43720{
43721 JSValue pattern, flags, bc, val;
43722 JSValueConst pat, flags1;
43723 JSRegExp *re;
43724 int pat_is_regexp;
43725
43726 pat = argv[0];
43727 flags1 = argv[1];
43728 pat_is_regexp = js_is_regexp(ctx, pat);
43729 if (pat_is_regexp < 0)
43730 return JS_EXCEPTION;
43731 if (JS_IsUndefined(new_target)) {
43732 /* called as a function */
43733 new_target = JS_GetActiveFunction(ctx);
43734 if (pat_is_regexp && JS_IsUndefined(flags1)) {
43735 JSValue ctor;
43736 BOOL res;
43737 ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
43738 if (JS_IsException(ctor))
43739 return ctor;
43740 res = js_same_value(ctx, ctor, new_target);
43741 JS_FreeValue(ctx, ctor);
43742 if (res)
43743 return JS_DupValue(ctx, pat);
43744 }
43745 }
43746 re = js_get_regexp(ctx, pat, FALSE);
43747 if (re) {
43748 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
43749 if (JS_IsUndefined(flags1)) {
43750 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
43751 goto no_compilation;
43752 } else {
43753 flags = JS_ToString(ctx, flags1);
43754 if (JS_IsException(flags))
43755 goto fail;
43756 }
43757 } else {
43758 flags = JS_UNDEFINED;
43759 if (pat_is_regexp) {
43760 pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
43761 if (JS_IsException(pattern))
43762 goto fail;
43763 if (JS_IsUndefined(flags1)) {
43764 flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
43765 if (JS_IsException(flags))
43766 goto fail;
43767 } else {
43768 flags = JS_DupValue(ctx, flags1);
43769 }
43770 } else {
43771 pattern = JS_DupValue(ctx, pat);
43772 flags = JS_DupValue(ctx, flags1);
43773 }
43774 if (JS_IsUndefined(pattern)) {
43775 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
43776 } else {
43777 val = pattern;
43778 pattern = JS_ToString(ctx, val);
43779 JS_FreeValue(ctx, val);
43780 if (JS_IsException(pattern))
43781 goto fail;
43782 }
43783 }
43784 bc = js_compile_regexp(ctx, pattern, flags);
43785 if (JS_IsException(bc))
43786 goto fail;
43787 JS_FreeValue(ctx, flags);
43788 no_compilation:
43789 return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
43790 fail:
43791 JS_FreeValue(ctx, pattern);
43792 JS_FreeValue(ctx, flags);
43793 return JS_EXCEPTION;
43794}
43795
43796static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
43797 int argc, JSValueConst *argv)
43798{
43799 JSRegExp *re1, *re;
43800 JSValueConst pattern1, flags1;
43801 JSValue bc, pattern;
43802
43803 re = js_get_regexp(ctx, this_val, TRUE);
43804 if (!re)
43805 return JS_EXCEPTION;
43806 pattern1 = argv[0];
43807 flags1 = argv[1];
43808 re1 = js_get_regexp(ctx, pattern1, FALSE);
43809 if (re1) {
43810 if (!JS_IsUndefined(flags1))
43811 return JS_ThrowTypeError(ctx, "flags must be undefined");
43812 pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
43813 bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
43814 } else {
43815 bc = JS_UNDEFINED;
43816 if (JS_IsUndefined(pattern1))
43817 pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
43818 else
43819 pattern = JS_ToString(ctx, pattern1);
43820 if (JS_IsException(pattern))
43821 goto fail;
43822 bc = js_compile_regexp(ctx, pattern, flags1);
43823 if (JS_IsException(bc))
43824 goto fail;
43825 }
43826 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
43827 JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
43828 re->pattern = JS_VALUE_GET_STRING(pattern);
43829 re->bytecode = JS_VALUE_GET_STRING(bc);
43830 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
43831 JS_NewInt32(ctx, 0)) < 0)
43832 return JS_EXCEPTION;
43833 return JS_DupValue(ctx, this_val);
43834 fail:
43835 JS_FreeValue(ctx, pattern);
43836 JS_FreeValue(ctx, bc);
43837 return JS_EXCEPTION;
43838}
43839
43840#if 0
43841static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
43842{
43843 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
43844 if (!re)
43845 return JS_EXCEPTION;
43846 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
43847}
43848
43849static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
43850{
43851 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
43852 int flags;
43853
43854 if (!re)
43855 return JS_EXCEPTION;
43856 flags = lre_get_flags(re->bytecode->u.str8);
43857 return JS_NewInt32(ctx, flags);
43858}
43859#endif
43860
43861static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
43862{
43863 JSRegExp *re;
43864 JSString *p;
43865 StringBuffer b_s, *b = &b_s;
43866 int i, n, c, c2, bra;
43867
43868 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
43869 return JS_ThrowTypeErrorNotAnObject(ctx);
43870
43871 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
43872 goto empty_regex;
43873
43874 re = js_get_regexp(ctx, this_val, TRUE);
43875 if (!re)
43876 return JS_EXCEPTION;
43877
43878 p = re->pattern;
43879
43880 if (p->len == 0) {
43881 empty_regex:
43882 return js_new_string8(ctx, "(?:)");
43883 }
43884 string_buffer_init2(ctx, b, p->len, p->is_wide_char);
43885
43886 /* Escape '/' and newline sequences as needed */
43887 bra = 0;
43888 for (i = 0, n = p->len; i < n;) {
43889 c2 = -1;
43890 switch (c = string_get(p, i++)) {
43891 case '\\':
43892 if (i < n)
43893 c2 = string_get(p, i++);
43894 break;
43895 case ']':
43896 bra = 0;
43897 break;
43898 case '[':
43899 if (!bra) {
43900 if (i < n && string_get(p, i) == ']')
43901 c2 = string_get(p, i++);
43902 bra = 1;
43903 }
43904 break;
43905 case '\n':
43906 c = '\\';
43907 c2 = 'n';
43908 break;
43909 case '\r':
43910 c = '\\';
43911 c2 = 'r';
43912 break;
43913 case '/':
43914 if (!bra) {
43915 c = '\\';
43916 c2 = '/';
43917 }
43918 break;
43919 }
43920 string_buffer_putc16(b, c);
43921 if (c2 >= 0)
43922 string_buffer_putc16(b, c2);
43923 }
43924 return string_buffer_end(b);
43925}
43926
43927static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
43928{
43929 JSRegExp *re;
43930 int flags;
43931
43932 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
43933 return JS_ThrowTypeErrorNotAnObject(ctx);
43934
43935 re = js_get_regexp(ctx, this_val, FALSE);
43936 if (!re) {
43937 if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
43938 return JS_UNDEFINED;
43939 else
43940 return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
43941 }
43942
43943 flags = lre_get_flags(re->bytecode->u.str8);
43944 return JS_NewBool(ctx, flags & mask);
43945}
43946
43947static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
43948{
43949 char str[8], *p = str;
43950 int res;
43951
43952 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
43953 return JS_ThrowTypeErrorNotAnObject(ctx);
43954
43955 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices"));
43956 if (res < 0)
43957 goto exception;
43958 if (res)
43959 *p++ = 'd';
43960 res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
43961 if (res < 0)
43962 goto exception;
43963 if (res)
43964 *p++ = 'g';
43965 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
43966 if (res < 0)
43967 goto exception;
43968 if (res)
43969 *p++ = 'i';
43970 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
43971 if (res < 0)
43972 goto exception;
43973 if (res)
43974 *p++ = 'm';
43975 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
43976 if (res < 0)
43977 goto exception;
43978 if (res)
43979 *p++ = 's';
43980 res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
43981 if (res < 0)
43982 goto exception;
43983 if (res)
43984 *p++ = 'u';
43985 res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
43986 if (res < 0)
43987 goto exception;
43988 if (res)
43989 *p++ = 'y';
43990 return JS_NewStringLen(ctx, str, p - str);
43991
43992exception:
43993 return JS_EXCEPTION;
43994}
43995
43996static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
43997 int argc, JSValueConst *argv)
43998{
43999 JSValue pattern, flags;
44000 StringBuffer b_s, *b = &b_s;
44001
44002 if (!JS_IsObject(this_val))
44003 return JS_ThrowTypeErrorNotAnObject(ctx);
44004
44005 string_buffer_init(ctx, b, 0);
44006 string_buffer_putc8(b, '/');
44007 pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
44008 if (string_buffer_concat_value_free(b, pattern))
44009 goto fail;
44010 string_buffer_putc8(b, '/');
44011 flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
44012 if (string_buffer_concat_value_free(b, flags))
44013 goto fail;
44014 return string_buffer_end(b);
44015
44016fail:
44017 string_buffer_free(b);
44018 return JS_EXCEPTION;
44019}
44020
44021int lre_check_stack_overflow(void *opaque, size_t alloca_size)
44022{
44023 JSContext *ctx = opaque;
44024 return js_check_stack_overflow(ctx->rt, alloca_size);
44025}
44026
44027int lre_check_timeout(void *opaque)
44028{
44029 JSContext *ctx = opaque;
44030 JSRuntime *rt = ctx->rt;
44031 return (rt->interrupt_handler &&
44032 rt->interrupt_handler(rt, rt->interrupt_opaque));
44033}
44034
44035void *lre_realloc(void *opaque, void *ptr, size_t size)
44036{
44037 JSContext *ctx = opaque;
44038 /* No JS exception is raised here */
44039 return js_realloc_rt(ctx->rt, ptr, size);
44040}
44041
44042static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
44043 int argc, JSValueConst *argv)
44044{
44045 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
44046 JSString *str;
44047 JSValue t, ret, str_val, obj, val, groups;
44048 JSValue indices, indices_groups;
44049 uint8_t *re_bytecode;
44050 uint8_t **capture, *str_buf;
44051 int rc, capture_count, shift, i, re_flags;
44052 int64_t last_index;
44053 const char *group_name_ptr;
44054
44055 if (!re)
44056 return JS_EXCEPTION;
44057
44058 str_val = JS_ToString(ctx, argv[0]);
44059 if (JS_IsException(str_val))
44060 return JS_EXCEPTION;
44061
44062 ret = JS_EXCEPTION;
44063 obj = JS_NULL;
44064 groups = JS_UNDEFINED;
44065 indices = JS_UNDEFINED;
44066 indices_groups = JS_UNDEFINED;
44067 capture = NULL;
44068
44069 val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
44070 if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
44071 goto fail;
44072
44073 re_bytecode = re->bytecode->u.str8;
44074 re_flags = lre_get_flags(re_bytecode);
44075 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
44076 last_index = 0;
44077 }
44078 str = JS_VALUE_GET_STRING(str_val);
44079 capture_count = lre_get_capture_count(re_bytecode);
44080 if (capture_count > 0) {
44081 capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
44082 if (!capture)
44083 goto fail;
44084 }
44085 shift = str->is_wide_char;
44086 str_buf = str->u.str8;
44087 if (last_index > str->len) {
44088 rc = 2;
44089 } else {
44090 rc = lre_exec(capture, re_bytecode,
44091 str_buf, last_index, str->len,
44092 shift, ctx);
44093 }
44094 if (rc != 1) {
44095 if (rc >= 0) {
44096 if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
44097 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
44098 JS_NewInt32(ctx, 0)) < 0)
44099 goto fail;
44100 }
44101 } else {
44102 if (rc == LRE_RET_TIMEOUT) {
44103 JS_ThrowInterrupted(ctx);
44104 } else {
44105 JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44106 }
44107 goto fail;
44108 }
44109 } else {
44110 int prop_flags;
44111 if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
44112 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
44113 JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
44114 goto fail;
44115 }
44116 obj = JS_NewArray(ctx);
44117 if (JS_IsException(obj))
44118 goto fail;
44119 prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
44120 group_name_ptr = lre_get_groupnames(re_bytecode);
44121 if (group_name_ptr) {
44122 groups = JS_NewObjectProto(ctx, JS_NULL);
44123 if (JS_IsException(groups))
44124 goto fail;
44125 }
44126 if (re_flags & LRE_FLAG_INDICES) {
44127 indices = JS_NewArray(ctx);
44128 if (JS_IsException(indices))
44129 goto fail;
44130 if (group_name_ptr) {
44131 indices_groups = JS_NewObjectProto(ctx, JS_NULL);
44132 if (JS_IsException(indices_groups))
44133 goto fail;
44134 }
44135 }
44136
44137 for(i = 0; i < capture_count; i++) {
44138 const char *name = NULL;
44139 uint8_t **match = &capture[2 * i];
44140 int start = -1;
44141 int end = -1;
44142 JSValue val;
44143
44144 if (group_name_ptr && i > 0) {
44145 if (*group_name_ptr) name = group_name_ptr;
44146 group_name_ptr += strlen(group_name_ptr) + 1;
44147 }
44148
44149 if (match[0] && match[1]) {
44150 start = (match[0] - str_buf) >> shift;
44151 end = (match[1] - str_buf) >> shift;
44152 }
44153
44154 if (!JS_IsUndefined(indices)) {
44155 val = JS_UNDEFINED;
44156 if (start != -1) {
44157 val = JS_NewArray(ctx);
44158 if (JS_IsException(val))
44159 goto fail;
44160 if (JS_DefinePropertyValueUint32(ctx, val, 0,
44161 JS_NewInt32(ctx, start),
44162 prop_flags) < 0) {
44163 JS_FreeValue(ctx, val);
44164 goto fail;
44165 }
44166 if (JS_DefinePropertyValueUint32(ctx, val, 1,
44167 JS_NewInt32(ctx, end),
44168 prop_flags) < 0) {
44169 JS_FreeValue(ctx, val);
44170 goto fail;
44171 }
44172 }
44173 if (name && !JS_IsUndefined(indices_groups)) {
44174 val = JS_DupValue(ctx, val);
44175 if (JS_DefinePropertyValueStr(ctx, indices_groups,
44176 name, val, prop_flags) < 0) {
44177 JS_FreeValue(ctx, val);
44178 goto fail;
44179 }
44180 }
44181 if (JS_DefinePropertyValueUint32(ctx, indices, i, val,
44182 prop_flags) < 0) {
44183 goto fail;
44184 }
44185 }
44186
44187 val = JS_UNDEFINED;
44188 if (start != -1) {
44189 val = js_sub_string(ctx, str, start, end);
44190 if (JS_IsException(val))
44191 goto fail;
44192 }
44193
44194 if (name) {
44195 if (JS_DefinePropertyValueStr(ctx, groups, name,
44196 JS_DupValue(ctx, val),
44197 prop_flags) < 0) {
44198 JS_FreeValue(ctx, val);
44199 goto fail;
44200 }
44201 }
44202
44203 if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
44204 goto fail;
44205 }
44206
44207 t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift);
44208 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0)
44209 goto fail;
44210
44211 t = str_val, str_val = JS_UNDEFINED;
44212 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0)
44213 goto fail;
44214
44215 t = groups, groups = JS_UNDEFINED;
44216 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
44217 t, prop_flags) < 0) {
44218 goto fail;
44219 }
44220
44221 if (!JS_IsUndefined(indices)) {
44222 t = indices_groups, indices_groups = JS_UNDEFINED;
44223 if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups,
44224 t, prop_flags) < 0) {
44225 goto fail;
44226 }
44227 t = indices, indices = JS_UNDEFINED;
44228 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices,
44229 t, prop_flags) < 0) {
44230 goto fail;
44231 }
44232 }
44233 }
44234 ret = obj;
44235 obj = JS_UNDEFINED;
44236fail:
44237 JS_FreeValue(ctx, indices_groups);
44238 JS_FreeValue(ctx, indices);
44239 JS_FreeValue(ctx, str_val);
44240 JS_FreeValue(ctx, groups);
44241 JS_FreeValue(ctx, obj);
44242 js_free(ctx, capture);
44243 return ret;
44244}
44245
44246/* delete portions of a string that match a given regex */
44247static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
44248{
44249 JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
44250 JSString *str;
44251 JSValue str_val, val;
44252 uint8_t *re_bytecode;
44253 int ret;
44254 uint8_t **capture, *str_buf;
44255 int capture_count, shift, re_flags;
44256 int next_src_pos, start, end;
44257 int64_t last_index;
44258 StringBuffer b_s, *b = &b_s;
44259
44260 if (!re)
44261 return JS_EXCEPTION;
44262
44263 string_buffer_init(ctx, b, 0);
44264
44265 capture = NULL;
44266 str_val = JS_ToString(ctx, arg);
44267 if (JS_IsException(str_val))
44268 goto fail;
44269 str = JS_VALUE_GET_STRING(str_val);
44270 re_bytecode = re->bytecode->u.str8;
44271 re_flags = lre_get_flags(re_bytecode);
44272 if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
44273 last_index = 0;
44274 } else {
44275 val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
44276 if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
44277 goto fail;
44278 }
44279 capture_count = lre_get_capture_count(re_bytecode);
44280 if (capture_count > 0) {
44281 capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
44282 if (!capture)
44283 goto fail;
44284 }
44285 shift = str->is_wide_char;
44286 str_buf = str->u.str8;
44287 next_src_pos = 0;
44288 for (;;) {
44289 if (last_index > str->len)
44290 break;
44291
44292 ret = lre_exec(capture, re_bytecode,
44293 str_buf, last_index, str->len, shift, ctx);
44294 if (ret != 1) {
44295 if (ret >= 0) {
44296 if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
44297 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
44298 JS_NewInt32(ctx, 0)) < 0)
44299 goto fail;
44300 }
44301 } else {
44302 if (ret == LRE_RET_TIMEOUT) {
44303 JS_ThrowInterrupted(ctx);
44304 } else {
44305 JS_ThrowInternalError(ctx, "out of memory in regexp execution");
44306 }
44307 goto fail;
44308 }
44309 break;
44310 }
44311 start = (capture[0] - str_buf) >> shift;
44312 end = (capture[1] - str_buf) >> shift;
44313 last_index = end;
44314 if (next_src_pos < start) {
44315 if (string_buffer_concat(b, str, next_src_pos, start))
44316 goto fail;
44317 }
44318 next_src_pos = end;
44319 if (!(re_flags & LRE_FLAG_GLOBAL)) {
44320 if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
44321 JS_NewInt32(ctx, end)) < 0)
44322 goto fail;
44323 break;
44324 }
44325 if (end == start) {
44326 if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) {
44327 end++;
44328 } else {
44329 string_getc(str, &end);
44330 }
44331 }
44332 last_index = end;
44333 }
44334 if (string_buffer_concat(b, str, next_src_pos, str->len))
44335 goto fail;
44336 JS_FreeValue(ctx, str_val);
44337 js_free(ctx, capture);
44338 return string_buffer_end(b);
44339fail:
44340 JS_FreeValue(ctx, str_val);
44341 js_free(ctx, capture);
44342 string_buffer_free(b);
44343 return JS_EXCEPTION;
44344}
44345
44346static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
44347{
44348 JSValue method, ret;
44349
44350 method = JS_GetProperty(ctx, r, JS_ATOM_exec);
44351 if (JS_IsException(method))
44352 return method;
44353 if (JS_IsFunction(ctx, method)) {
44354 ret = JS_CallFree(ctx, method, r, 1, &s);
44355 if (JS_IsException(ret))
44356 return ret;
44357 if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
44358 JS_FreeValue(ctx, ret);
44359 return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
44360 }
44361 return ret;
44362 }
44363 JS_FreeValue(ctx, method);
44364 return js_regexp_exec(ctx, r, 1, &s);
44365}
44366
44367#if 0
44368static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
44369 int argc, JSValueConst *argv)
44370{
44371 return JS_RegExpExec(ctx, argv[0], argv[1]);
44372}
44373static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
44374 int argc, JSValueConst *argv)
44375{
44376 return JS_RegExpDelete(ctx, argv[0], argv[1]);
44377}
44378#endif
44379
44380static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
44381 int argc, JSValueConst *argv)
44382{
44383 JSValue val;
44384 BOOL ret;
44385
44386 val = JS_RegExpExec(ctx, this_val, argv[0]);
44387 if (JS_IsException(val))
44388 return JS_EXCEPTION;
44389 ret = !JS_IsNull(val);
44390 JS_FreeValue(ctx, val);
44391 return JS_NewBool(ctx, ret);
44392}
44393
44394static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
44395 int argc, JSValueConst *argv)
44396{
44397 // [Symbol.match](str)
44398 JSValueConst rx = this_val;
44399 JSValue A, S, flags, result, matchStr;
44400 int global, n, fullUnicode, isEmpty;
44401 JSString *p;
44402
44403 if (!JS_IsObject(rx))
44404 return JS_ThrowTypeErrorNotAnObject(ctx);
44405
44406 A = JS_UNDEFINED;
44407 flags = JS_UNDEFINED;
44408 result = JS_UNDEFINED;
44409 matchStr = JS_UNDEFINED;
44410 S = JS_ToString(ctx, argv[0]);
44411 if (JS_IsException(S))
44412 goto exception;
44413
44414 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
44415 if (JS_IsException(flags))
44416 goto exception;
44417 flags = JS_ToStringFree(ctx, flags);
44418 if (JS_IsException(flags))
44419 goto exception;
44420 p = JS_VALUE_GET_STRING(flags);
44421
44422 // TODO(bnoordhuis) query 'u' flag the same way?
44423 global = (-1 != string_indexof_char(p, 'g', 0));
44424 if (!global) {
44425 A = JS_RegExpExec(ctx, rx, S);
44426 } else {
44427 fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
44428 if (fullUnicode < 0)
44429 goto exception;
44430
44431 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
44432 goto exception;
44433 A = JS_NewArray(ctx);
44434 if (JS_IsException(A))
44435 goto exception;
44436 n = 0;
44437 for(;;) {
44438 JS_FreeValue(ctx, result);
44439 result = JS_RegExpExec(ctx, rx, S);
44440 if (JS_IsException(result))
44441 goto exception;
44442 if (JS_IsNull(result))
44443 break;
44444 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
44445 if (JS_IsException(matchStr))
44446 goto exception;
44447 isEmpty = JS_IsEmptyString(matchStr);
44448 if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
44449 goto exception;
44450 if (isEmpty) {
44451 int64_t thisIndex, nextIndex;
44452 if (JS_ToLengthFree(ctx, &thisIndex,
44453 JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
44454 goto exception;
44455 p = JS_VALUE_GET_STRING(S);
44456 nextIndex = string_advance_index(p, thisIndex, fullUnicode);
44457 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
44458 goto exception;
44459 }
44460 }
44461 if (n == 0) {
44462 JS_FreeValue(ctx, A);
44463 A = JS_NULL;
44464 }
44465 }
44466 JS_FreeValue(ctx, result);
44467 JS_FreeValue(ctx, flags);
44468 JS_FreeValue(ctx, S);
44469 return A;
44470
44471exception:
44472 JS_FreeValue(ctx, A);
44473 JS_FreeValue(ctx, result);
44474 JS_FreeValue(ctx, flags);
44475 JS_FreeValue(ctx, S);
44476 return JS_EXCEPTION;
44477}
44478
44479typedef struct JSRegExpStringIteratorData {
44480 JSValue iterating_regexp;
44481 JSValue iterated_string;
44482 BOOL global;
44483 BOOL unicode;
44484 BOOL done;
44485} JSRegExpStringIteratorData;
44486
44487static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
44488{
44489 JSObject *p = JS_VALUE_GET_OBJ(val);
44490 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
44491 if (it) {
44492 JS_FreeValueRT(rt, it->iterating_regexp);
44493 JS_FreeValueRT(rt, it->iterated_string);
44494 js_free_rt(rt, it);
44495 }
44496}
44497
44498static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
44499 JS_MarkFunc *mark_func)
44500{
44501 JSObject *p = JS_VALUE_GET_OBJ(val);
44502 JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
44503 if (it) {
44504 JS_MarkValue(rt, it->iterating_regexp, mark_func);
44505 JS_MarkValue(rt, it->iterated_string, mark_func);
44506 }
44507}
44508
44509static JSValue js_regexp_string_iterator_next(JSContext *ctx,
44510 JSValueConst this_val,
44511 int argc, JSValueConst *argv,
44512 BOOL *pdone, int magic)
44513{
44514 JSRegExpStringIteratorData *it;
44515 JSValueConst R, S;
44516 JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
44517 JSString *sp;
44518
44519 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
44520 if (!it)
44521 goto exception;
44522 if (it->done) {
44523 *pdone = TRUE;
44524 return JS_UNDEFINED;
44525 }
44526 R = it->iterating_regexp;
44527 S = it->iterated_string;
44528 match = JS_RegExpExec(ctx, R, S);
44529 if (JS_IsException(match))
44530 goto exception;
44531 if (JS_IsNull(match)) {
44532 it->done = TRUE;
44533 *pdone = TRUE;
44534 return JS_UNDEFINED;
44535 } else if (it->global) {
44536 matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
44537 if (JS_IsException(matchStr))
44538 goto exception;
44539 if (JS_IsEmptyString(matchStr)) {
44540 int64_t thisIndex, nextIndex;
44541 if (JS_ToLengthFree(ctx, &thisIndex,
44542 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
44543 goto exception;
44544 sp = JS_VALUE_GET_STRING(S);
44545 nextIndex = string_advance_index(sp, thisIndex, it->unicode);
44546 if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
44547 JS_NewInt64(ctx, nextIndex)) < 0)
44548 goto exception;
44549 }
44550 JS_FreeValue(ctx, matchStr);
44551 } else {
44552 it->done = TRUE;
44553 }
44554 *pdone = FALSE;
44555 return match;
44556 exception:
44557 JS_FreeValue(ctx, match);
44558 JS_FreeValue(ctx, matchStr);
44559 *pdone = FALSE;
44560 return JS_EXCEPTION;
44561}
44562
44563static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
44564 int argc, JSValueConst *argv)
44565{
44566 // [Symbol.matchAll](str)
44567 JSValueConst R = this_val;
44568 JSValue S, C, flags, matcher, iter;
44569 JSValueConst args[2];
44570 JSString *strp;
44571 int64_t lastIndex;
44572 JSRegExpStringIteratorData *it;
44573
44574 if (!JS_IsObject(R))
44575 return JS_ThrowTypeErrorNotAnObject(ctx);
44576
44577 C = JS_UNDEFINED;
44578 flags = JS_UNDEFINED;
44579 matcher = JS_UNDEFINED;
44580 iter = JS_UNDEFINED;
44581
44582 S = JS_ToString(ctx, argv[0]);
44583 if (JS_IsException(S))
44584 goto exception;
44585 C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
44586 if (JS_IsException(C))
44587 goto exception;
44588 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
44589 if (JS_IsException(flags))
44590 goto exception;
44591 args[0] = R;
44592 args[1] = flags;
44593 matcher = JS_CallConstructor(ctx, C, 2, args);
44594 if (JS_IsException(matcher))
44595 goto exception;
44596 if (JS_ToLengthFree(ctx, &lastIndex,
44597 JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
44598 goto exception;
44599 if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
44600 JS_NewInt64(ctx, lastIndex)) < 0)
44601 goto exception;
44602
44603 iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
44604 if (JS_IsException(iter))
44605 goto exception;
44606 it = js_malloc(ctx, sizeof(*it));
44607 if (!it)
44608 goto exception;
44609 it->iterating_regexp = matcher;
44610 it->iterated_string = S;
44611 strp = JS_VALUE_GET_STRING(flags);
44612 it->global = string_indexof_char(strp, 'g', 0) >= 0;
44613 it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
44614 it->done = FALSE;
44615 JS_SetOpaque(iter, it);
44616
44617 JS_FreeValue(ctx, C);
44618 JS_FreeValue(ctx, flags);
44619 return iter;
44620 exception:
44621 JS_FreeValue(ctx, S);
44622 JS_FreeValue(ctx, C);
44623 JS_FreeValue(ctx, flags);
44624 JS_FreeValue(ctx, matcher);
44625 JS_FreeValue(ctx, iter);
44626 return JS_EXCEPTION;
44627}
44628
44629typedef struct ValueBuffer {
44630 JSContext *ctx;
44631 JSValue *arr;
44632 JSValue def[4];
44633 int len;
44634 int size;
44635 int error_status;
44636} ValueBuffer;
44637
44638static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
44639{
44640 b->ctx = ctx;
44641 b->len = 0;
44642 b->size = 4;
44643 b->error_status = 0;
44644 b->arr = b->def;
44645 return 0;
44646}
44647
44648static void value_buffer_free(ValueBuffer *b)
44649{
44650 while (b->len > 0)
44651 JS_FreeValue(b->ctx, b->arr[--b->len]);
44652 if (b->arr != b->def)
44653 js_free(b->ctx, b->arr);
44654 b->arr = b->def;
44655 b->size = 4;
44656}
44657
44658static int value_buffer_append(ValueBuffer *b, JSValue val)
44659{
44660 if (b->error_status)
44661 return -1;
44662
44663 if (b->len >= b->size) {
44664 int new_size = (b->len + (b->len >> 1) + 31) & ~16;
44665 size_t slack;
44666 JSValue *new_arr;
44667
44668 if (b->arr == b->def) {
44669 new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
44670 if (new_arr)
44671 memcpy(new_arr, b->def, sizeof b->def);
44672 } else {
44673 new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
44674 }
44675 if (!new_arr) {
44676 value_buffer_free(b);
44677 JS_FreeValue(b->ctx, val);
44678 b->error_status = -1;
44679 return -1;
44680 }
44681 new_size += slack / sizeof(*new_arr);
44682 b->arr = new_arr;
44683 b->size = new_size;
44684 }
44685 b->arr[b->len++] = val;
44686 return 0;
44687}
44688
44689static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
44690{
44691 JSValue val;
44692 int res;
44693
44694 val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
44695 if (JS_IsException(val))
44696 return -1;
44697 // rx.constructor === RegExp
44698 res = js_same_value(ctx, val, ctx->regexp_ctor);
44699 JS_FreeValue(ctx, val);
44700 if (res) {
44701 val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
44702 if (JS_IsException(val))
44703 return -1;
44704 // rx.exec === RE_exec
44705 res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
44706 JS_FreeValue(ctx, val);
44707 }
44708 return res;
44709}
44710
44711static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
44712 int argc, JSValueConst *argv)
44713{
44714 // [Symbol.replace](str, rep)
44715 JSValueConst rx = this_val, rep = argv[1];
44716 JSValueConst args[6];
44717 JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res;
44718 JSString *p, *sp, *rp;
44719 StringBuffer b_s, *b = &b_s;
44720 ValueBuffer v_b, *results = &v_b;
44721 int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
44722 uint32_t nCaptures;
44723 int64_t position;
44724
44725 if (!JS_IsObject(rx))
44726 return JS_ThrowTypeErrorNotAnObject(ctx);
44727
44728 string_buffer_init(ctx, b, 0);
44729 value_buffer_init(ctx, results);
44730
44731 rep_val = JS_UNDEFINED;
44732 matched = JS_UNDEFINED;
44733 tab = JS_UNDEFINED;
44734 flags = JS_UNDEFINED;
44735 rep_str = JS_UNDEFINED;
44736 namedCaptures = JS_UNDEFINED;
44737
44738 str = JS_ToString(ctx, argv[0]);
44739 if (JS_IsException(str))
44740 goto exception;
44741
44742 sp = JS_VALUE_GET_STRING(str);
44743 rp = NULL;
44744 functionalReplace = JS_IsFunction(ctx, rep);
44745 if (!functionalReplace) {
44746 rep_val = JS_ToString(ctx, rep);
44747 if (JS_IsException(rep_val))
44748 goto exception;
44749 rp = JS_VALUE_GET_STRING(rep_val);
44750 }
44751
44752 flags = JS_GetProperty(ctx, rx, JS_ATOM_flags);
44753 if (JS_IsException(flags))
44754 goto exception;
44755 flags = JS_ToStringFree(ctx, flags);
44756 if (JS_IsException(flags))
44757 goto exception;
44758 p = JS_VALUE_GET_STRING(flags);
44759
44760 // TODO(bnoordhuis) query 'u' flag the same way?
44761 fullUnicode = 0;
44762 is_global = (-1 != string_indexof_char(p, 'g', 0));
44763 if (is_global) {
44764 fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
44765 if (fullUnicode < 0)
44766 goto exception;
44767 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
44768 goto exception;
44769 }
44770
44771 if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
44772 /* use faster version for simple cases */
44773 res = JS_RegExpDelete(ctx, rx, str);
44774 goto done;
44775 }
44776 for(;;) {
44777 JSValue result;
44778 result = JS_RegExpExec(ctx, rx, str);
44779 if (JS_IsException(result))
44780 goto exception;
44781 if (JS_IsNull(result))
44782 break;
44783 if (value_buffer_append(results, result) < 0)
44784 goto exception;
44785 if (!is_global)
44786 break;
44787 JS_FreeValue(ctx, matched);
44788 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
44789 if (JS_IsException(matched))
44790 goto exception;
44791 if (JS_IsEmptyString(matched)) {
44792 /* always advance of at least one char */
44793 int64_t thisIndex, nextIndex;
44794 if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
44795 goto exception;
44796 nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
44797 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
44798 goto exception;
44799 }
44800 }
44801 nextSourcePosition = 0;
44802 for(j = 0; j < results->len; j++) {
44803 JSValueConst result;
44804 result = results->arr[j];
44805 if (js_get_length32(ctx, &nCaptures, result) < 0)
44806 goto exception;
44807 JS_FreeValue(ctx, matched);
44808 matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
44809 if (JS_IsException(matched))
44810 goto exception;
44811 if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
44812 goto exception;
44813 if (position > sp->len)
44814 position = sp->len;
44815 else if (position < 0)
44816 position = 0;
44817 /* ignore substition if going backward (can happen
44818 with custom regexp object) */
44819 JS_FreeValue(ctx, tab);
44820 tab = JS_NewArray(ctx);
44821 if (JS_IsException(tab))
44822 goto exception;
44823 if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
44824 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
44825 goto exception;
44826 for(n = 1; n < nCaptures; n++) {
44827 JSValue capN;
44828 capN = JS_GetPropertyInt64(ctx, result, n);
44829 if (JS_IsException(capN))
44830 goto exception;
44831 if (!JS_IsUndefined(capN)) {
44832 capN = JS_ToStringFree(ctx, capN);
44833 if (JS_IsException(capN))
44834 goto exception;
44835 }
44836 if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
44837 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
44838 goto exception;
44839 }
44840 JS_FreeValue(ctx, namedCaptures);
44841 namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
44842 if (JS_IsException(namedCaptures))
44843 goto exception;
44844 if (functionalReplace) {
44845 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
44846 goto exception;
44847 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
44848 goto exception;
44849 if (!JS_IsUndefined(namedCaptures)) {
44850 if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
44851 goto exception;
44852 }
44853 args[0] = JS_UNDEFINED;
44854 args[1] = tab;
44855 JS_FreeValue(ctx, rep_str);
44856 rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
44857 } else {
44858 JSValue namedCaptures1;
44859 if (!JS_IsUndefined(namedCaptures)) {
44860 namedCaptures1 = JS_ToObject(ctx, namedCaptures);
44861 if (JS_IsException(namedCaptures1))
44862 goto exception;
44863 } else {
44864 namedCaptures1 = JS_UNDEFINED;
44865 }
44866 args[0] = matched;
44867 args[1] = str;
44868 args[2] = JS_NewInt32(ctx, position);
44869 args[3] = tab;
44870 args[4] = namedCaptures1;
44871 args[5] = rep_val;
44872 JS_FreeValue(ctx, rep_str);
44873 rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
44874 JS_FreeValue(ctx, namedCaptures1);
44875 }
44876 if (JS_IsException(rep_str))
44877 goto exception;
44878 if (position >= nextSourcePosition) {
44879 string_buffer_concat(b, sp, nextSourcePosition, position);
44880 string_buffer_concat_value(b, rep_str);
44881 nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
44882 }
44883 }
44884 string_buffer_concat(b, sp, nextSourcePosition, sp->len);
44885 res = string_buffer_end(b);
44886 goto done1;
44887
44888exception:
44889 res = JS_EXCEPTION;
44890done:
44891 string_buffer_free(b);
44892done1:
44893 value_buffer_free(results);
44894 JS_FreeValue(ctx, rep_val);
44895 JS_FreeValue(ctx, matched);
44896 JS_FreeValue(ctx, flags);
44897 JS_FreeValue(ctx, tab);
44898 JS_FreeValue(ctx, rep_str);
44899 JS_FreeValue(ctx, namedCaptures);
44900 JS_FreeValue(ctx, str);
44901 return res;
44902}
44903
44904static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
44905 int argc, JSValueConst *argv)
44906{
44907 JSValueConst rx = this_val;
44908 JSValue str, previousLastIndex, currentLastIndex, result, index;
44909
44910 if (!JS_IsObject(rx))
44911 return JS_ThrowTypeErrorNotAnObject(ctx);
44912
44913 result = JS_UNDEFINED;
44914 currentLastIndex = JS_UNDEFINED;
44915 previousLastIndex = JS_UNDEFINED;
44916 str = JS_ToString(ctx, argv[0]);
44917 if (JS_IsException(str))
44918 goto exception;
44919
44920 previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
44921 if (JS_IsException(previousLastIndex))
44922 goto exception;
44923
44924 if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
44925 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
44926 goto exception;
44927 }
44928 }
44929 result = JS_RegExpExec(ctx, rx, str);
44930 if (JS_IsException(result))
44931 goto exception;
44932 currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
44933 if (JS_IsException(currentLastIndex))
44934 goto exception;
44935 if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
44936 JS_FreeValue(ctx, previousLastIndex);
44937 } else {
44938 if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
44939 previousLastIndex = JS_UNDEFINED;
44940 goto exception;
44941 }
44942 }
44943 JS_FreeValue(ctx, str);
44944 JS_FreeValue(ctx, currentLastIndex);
44945
44946 if (JS_IsNull(result)) {
44947 return JS_NewInt32(ctx, -1);
44948 } else {
44949 index = JS_GetProperty(ctx, result, JS_ATOM_index);
44950 JS_FreeValue(ctx, result);
44951 return index;
44952 }
44953
44954exception:
44955 JS_FreeValue(ctx, result);
44956 JS_FreeValue(ctx, str);
44957 JS_FreeValue(ctx, currentLastIndex);
44958 JS_FreeValue(ctx, previousLastIndex);
44959 return JS_EXCEPTION;
44960}
44961
44962static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
44963 int argc, JSValueConst *argv)
44964{
44965 // [Symbol.split](str, limit)
44966 JSValueConst rx = this_val;
44967 JSValueConst args[2];
44968 JSValue str, ctor, splitter, A, flags, z, sub;
44969 JSString *strp;
44970 uint32_t lim, size, p, q;
44971 int unicodeMatching;
44972 int64_t lengthA, e, numberOfCaptures, i;
44973
44974 if (!JS_IsObject(rx))
44975 return JS_ThrowTypeErrorNotAnObject(ctx);
44976
44977 ctor = JS_UNDEFINED;
44978 splitter = JS_UNDEFINED;
44979 A = JS_UNDEFINED;
44980 flags = JS_UNDEFINED;
44981 z = JS_UNDEFINED;
44982 str = JS_ToString(ctx, argv[0]);
44983 if (JS_IsException(str))
44984 goto exception;
44985 ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
44986 if (JS_IsException(ctor))
44987 goto exception;
44988 flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
44989 if (JS_IsException(flags))
44990 goto exception;
44991 strp = JS_VALUE_GET_STRING(flags);
44992 unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
44993 if (string_indexof_char(strp, 'y', 0) < 0) {
44994 flags = JS_ConcatString3(ctx, "", flags, "y");
44995 if (JS_IsException(flags))
44996 goto exception;
44997 }
44998 args[0] = rx;
44999 args[1] = flags;
45000 splitter = JS_CallConstructor(ctx, ctor, 2, args);
45001 if (JS_IsException(splitter))
45002 goto exception;
45003 A = JS_NewArray(ctx);
45004 if (JS_IsException(A))
45005 goto exception;
45006 lengthA = 0;
45007 if (JS_IsUndefined(argv[1])) {
45008 lim = 0xffffffff;
45009 } else {
45010 if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
45011 goto exception;
45012 if (lim == 0)
45013 goto done;
45014 }
45015 strp = JS_VALUE_GET_STRING(str);
45016 p = q = 0;
45017 size = strp->len;
45018 if (size == 0) {
45019 z = JS_RegExpExec(ctx, splitter, str);
45020 if (JS_IsException(z))
45021 goto exception;
45022 if (JS_IsNull(z))
45023 goto add_tail;
45024 goto done;
45025 }
45026 while (q < size) {
45027 if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
45028 goto exception;
45029 JS_FreeValue(ctx, z);
45030 z = JS_RegExpExec(ctx, splitter, str);
45031 if (JS_IsException(z))
45032 goto exception;
45033 if (JS_IsNull(z)) {
45034 q = string_advance_index(strp, q, unicodeMatching);
45035 } else {
45036 if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
45037 goto exception;
45038 if (e > size)
45039 e = size;
45040 if (e == p) {
45041 q = string_advance_index(strp, q, unicodeMatching);
45042 } else {
45043 sub = js_sub_string(ctx, strp, p, q);
45044 if (JS_IsException(sub))
45045 goto exception;
45046 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
45047 JS_PROP_C_W_E | JS_PROP_THROW) < 0)
45048 goto exception;
45049 if (lengthA == lim)
45050 goto done;
45051 p = e;
45052 if (js_get_length64(ctx, &numberOfCaptures, z))
45053 goto exception;
45054 for(i = 1; i < numberOfCaptures; i++) {
45055 sub = JS_GetPropertyInt64(ctx, z, i);
45056 if (JS_IsException(sub))
45057 goto exception;
45058 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
45059 goto exception;
45060 if (lengthA == lim)
45061 goto done;
45062 }
45063 q = p;
45064 }
45065 }
45066 }
45067add_tail:
45068 if (p > size)
45069 p = size;
45070 sub = js_sub_string(ctx, strp, p, size);
45071 if (JS_IsException(sub))
45072 goto exception;
45073 if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
45074 goto exception;
45075 goto done;
45076exception:
45077 JS_FreeValue(ctx, A);
45078 A = JS_EXCEPTION;
45079done:
45080 JS_FreeValue(ctx, str);
45081 JS_FreeValue(ctx, ctor);
45082 JS_FreeValue(ctx, splitter);
45083 JS_FreeValue(ctx, flags);
45084 JS_FreeValue(ctx, z);
45085 return A;
45086}
45087
45088static const JSCFunctionListEntry js_regexp_funcs[] = {
45089 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
45090 //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
45091 //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
45092};
45093
45094static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
45095 JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
45096 JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
45097 JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ),
45098 JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ),
45099 JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ),
45100 JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ),
45101 JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ),
45102 JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ),
45103 JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ),
45104 JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
45105 JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
45106 JS_CFUNC_DEF("test", 1, js_regexp_test ),
45107 JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
45108 JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
45109 JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
45110 JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
45111 JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
45112 JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
45113 //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
45114 //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
45115};
45116
45117static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
45118 JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
45119 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
45120};
45121
45122void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
45123{
45124 ctx->compile_regexp = js_compile_regexp;
45125}
45126
45127void JS_AddIntrinsicRegExp(JSContext *ctx)
45128{
45129 JSValueConst obj;
45130
45131 JS_AddIntrinsicRegExpCompiler(ctx);
45132
45133 ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
45134 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
45135 countof(js_regexp_proto_funcs));
45136 obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
45137 ctx->class_proto[JS_CLASS_REGEXP]);
45138 ctx->regexp_ctor = JS_DupValue(ctx, obj);
45139 JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
45140
45141 ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
45142 JS_NewObjectProto(ctx, ctx->iterator_proto);
45143 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
45144 js_regexp_string_iterator_proto_funcs,
45145 countof(js_regexp_string_iterator_proto_funcs));
45146}
45147
45148/* JSON */
45149
45150static int json_parse_expect(JSParseState *s, int tok)
45151{
45152 if (s->token.val != tok) {
45153 /* XXX: dump token correctly in all cases */
45154 return js_parse_error(s, "expecting '%c'", tok);
45155 }
45156 return json_next_token(s);
45157}
45158
45159static JSValue json_parse_value(JSParseState *s)
45160{
45161 JSContext *ctx = s->ctx;
45162 JSValue val = JS_NULL;
45163 int ret;
45164
45165 switch(s->token.val) {
45166 case '{':
45167 {
45168 JSValue prop_val;
45169 JSAtom prop_name;
45170
45171 if (json_next_token(s))
45172 goto fail;
45173 val = JS_NewObject(ctx);
45174 if (JS_IsException(val))
45175 goto fail;
45176 if (s->token.val != '}') {
45177 for(;;) {
45178 if (s->token.val == TOK_STRING) {
45179 prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
45180 if (prop_name == JS_ATOM_NULL)
45181 goto fail;
45182 } else if (s->ext_json && s->token.val == TOK_IDENT) {
45183 prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
45184 } else {
45185 js_parse_error(s, "expecting property name");
45186 goto fail;
45187 }
45188 if (json_next_token(s))
45189 goto fail1;
45190 if (json_parse_expect(s, ':'))
45191 goto fail1;
45192 prop_val = json_parse_value(s);
45193 if (JS_IsException(prop_val)) {
45194 fail1:
45195 JS_FreeAtom(ctx, prop_name);
45196 goto fail;
45197 }
45198 ret = JS_DefinePropertyValue(ctx, val, prop_name,
45199 prop_val, JS_PROP_C_W_E);
45200 JS_FreeAtom(ctx, prop_name);
45201 if (ret < 0)
45202 goto fail;
45203
45204 if (s->token.val != ',')
45205 break;
45206 if (json_next_token(s))
45207 goto fail;
45208 if (s->ext_json && s->token.val == '}')
45209 break;
45210 }
45211 }
45212 if (json_parse_expect(s, '}'))
45213 goto fail;
45214 }
45215 break;
45216 case '[':
45217 {
45218 JSValue el;
45219 uint32_t idx;
45220
45221 if (json_next_token(s))
45222 goto fail;
45223 val = JS_NewArray(ctx);
45224 if (JS_IsException(val))
45225 goto fail;
45226 if (s->token.val != ']') {
45227 idx = 0;
45228 for(;;) {
45229 el = json_parse_value(s);
45230 if (JS_IsException(el))
45231 goto fail;
45232 ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
45233 if (ret < 0)
45234 goto fail;
45235 if (s->token.val != ',')
45236 break;
45237 if (json_next_token(s))
45238 goto fail;
45239 idx++;
45240 if (s->ext_json && s->token.val == ']')
45241 break;
45242 }
45243 }
45244 if (json_parse_expect(s, ']'))
45245 goto fail;
45246 }
45247 break;
45248 case TOK_STRING:
45249 val = JS_DupValue(ctx, s->token.u.str.str);
45250 if (json_next_token(s))
45251 goto fail;
45252 break;
45253 case TOK_NUMBER:
45254 val = s->token.u.num.val;
45255 if (json_next_token(s))
45256 goto fail;
45257 break;
45258 case TOK_IDENT:
45259 if (s->token.u.ident.atom == JS_ATOM_false ||
45260 s->token.u.ident.atom == JS_ATOM_true) {
45261 val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true);
45262 } else if (s->token.u.ident.atom == JS_ATOM_null) {
45263 val = JS_NULL;
45264 } else {
45265 goto def_token;
45266 }
45267 if (json_next_token(s))
45268 goto fail;
45269 break;
45270 default:
45271 def_token:
45272 if (s->token.val == TOK_EOF) {
45273 js_parse_error(s, "Unexpected end of JSON input");
45274 } else {
45275 js_parse_error(s, "unexpected token: '%.*s'",
45276 (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
45277 }
45278 goto fail;
45279 }
45280 return val;
45281 fail:
45282 JS_FreeValue(ctx, val);
45283 return JS_EXCEPTION;
45284}
45285
45286JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
45287 const char *filename, int flags)
45288{
45289 JSParseState s1, *s = &s1;
45290 JSValue val = JS_UNDEFINED;
45291
45292 js_parse_init(ctx, s, buf, buf_len, filename);
45293 s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
45294 if (json_next_token(s))
45295 goto fail;
45296 val = json_parse_value(s);
45297 if (JS_IsException(val))
45298 goto fail;
45299 if (s->token.val != TOK_EOF) {
45300 if (js_parse_error(s, "unexpected data at the end"))
45301 goto fail;
45302 }
45303 return val;
45304 fail:
45305 JS_FreeValue(ctx, val);
45306 free_token(s, &s->token);
45307 return JS_EXCEPTION;
45308}
45309
45310JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
45311 const char *filename)
45312{
45313 return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
45314}
45315
45316static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
45317 JSAtom name, JSValueConst reviver)
45318{
45319 JSValue val, new_el, name_val, res;
45320 JSValueConst args[2];
45321 int ret, is_array;
45322 uint32_t i, len = 0;
45323 JSAtom prop;
45324 JSPropertyEnum *atoms = NULL;
45325
45326 if (js_check_stack_overflow(ctx->rt, 0)) {
45327 return JS_ThrowStackOverflow(ctx);
45328 }
45329
45330 val = JS_GetProperty(ctx, holder, name);
45331 if (JS_IsException(val))
45332 return val;
45333 if (JS_IsObject(val)) {
45334 is_array = JS_IsArray(ctx, val);
45335 if (is_array < 0)
45336 goto fail;
45337 if (is_array) {
45338 if (js_get_length32(ctx, &len, val))
45339 goto fail;
45340 } else {
45341 ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
45342 if (ret < 0)
45343 goto fail;
45344 }
45345 for(i = 0; i < len; i++) {
45346 if (is_array) {
45347 prop = JS_NewAtomUInt32(ctx, i);
45348 if (prop == JS_ATOM_NULL)
45349 goto fail;
45350 } else {
45351 prop = JS_DupAtom(ctx, atoms[i].atom);
45352 }
45353 new_el = internalize_json_property(ctx, val, prop, reviver);
45354 if (JS_IsException(new_el)) {
45355 JS_FreeAtom(ctx, prop);
45356 goto fail;
45357 }
45358 if (JS_IsUndefined(new_el)) {
45359 ret = JS_DeleteProperty(ctx, val, prop, 0);
45360 } else {
45361 ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
45362 }
45363 JS_FreeAtom(ctx, prop);
45364 if (ret < 0)
45365 goto fail;
45366 }
45367 }
45368 js_free_prop_enum(ctx, atoms, len);
45369 atoms = NULL;
45370 name_val = JS_AtomToValue(ctx, name);
45371 if (JS_IsException(name_val))
45372 goto fail;
45373 args[0] = name_val;
45374 args[1] = val;
45375 res = JS_Call(ctx, reviver, holder, 2, args);
45376 JS_FreeValue(ctx, name_val);
45377 JS_FreeValue(ctx, val);
45378 return res;
45379 fail:
45380 js_free_prop_enum(ctx, atoms, len);
45381 JS_FreeValue(ctx, val);
45382 return JS_EXCEPTION;
45383}
45384
45385static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
45386 int argc, JSValueConst *argv)
45387{
45388 JSValue obj, root;
45389 JSValueConst reviver;
45390 const char *str;
45391 size_t len;
45392
45393 str = JS_ToCStringLen(ctx, &len, argv[0]);
45394 if (!str)
45395 return JS_EXCEPTION;
45396 obj = JS_ParseJSON(ctx, str, len, "<input>");
45397 JS_FreeCString(ctx, str);
45398 if (JS_IsException(obj))
45399 return obj;
45400 if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
45401 reviver = argv[1];
45402 root = JS_NewObject(ctx);
45403 if (JS_IsException(root)) {
45404 JS_FreeValue(ctx, obj);
45405 return JS_EXCEPTION;
45406 }
45407 if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
45408 JS_PROP_C_W_E) < 0) {
45409 JS_FreeValue(ctx, root);
45410 return JS_EXCEPTION;
45411 }
45412 obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
45413 reviver);
45414 JS_FreeValue(ctx, root);
45415 }
45416 return obj;
45417}
45418
45419typedef struct JSONStringifyContext {
45420 JSValueConst replacer_func;
45421 JSValue stack;
45422 JSValue property_list;
45423 JSValue gap;
45424 JSValue empty;
45425 StringBuffer *b;
45426} JSONStringifyContext;
45427
45428static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
45429 JSValue r = JS_ToQuotedString(ctx, val);
45430 JS_FreeValue(ctx, val);
45431 return r;
45432}
45433
45434static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
45435 JSValueConst holder, JSValue val, JSValueConst key)
45436{
45437 JSValue v;
45438 JSValueConst args[2];
45439
45440 /* check for object.toJSON method */
45441 /* ECMA specifies this is done only for Object and BigInt */
45442 if (JS_IsObject(val) || JS_IsBigInt(ctx, val)) {
45443 JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
45444 if (JS_IsException(f))
45445 goto exception;
45446 if (JS_IsFunction(ctx, f)) {
45447 v = JS_CallFree(ctx, f, val, 1, &key);
45448 JS_FreeValue(ctx, val);
45449 val = v;
45450 if (JS_IsException(val))
45451 goto exception;
45452 } else {
45453 JS_FreeValue(ctx, f);
45454 }
45455 }
45456
45457 if (!JS_IsUndefined(jsc->replacer_func)) {
45458 args[0] = key;
45459 args[1] = val;
45460 v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
45461 JS_FreeValue(ctx, val);
45462 val = v;
45463 if (JS_IsException(val))
45464 goto exception;
45465 }
45466
45467 switch (JS_VALUE_GET_NORM_TAG(val)) {
45468 case JS_TAG_OBJECT:
45469 if (JS_IsFunction(ctx, val))
45470 break;
45471 case JS_TAG_STRING:
45472 case JS_TAG_STRING_ROPE:
45473 case JS_TAG_INT:
45474 case JS_TAG_FLOAT64:
45475 case JS_TAG_BOOL:
45476 case JS_TAG_NULL:
45477 case JS_TAG_SHORT_BIG_INT:
45478 case JS_TAG_BIG_INT:
45479 case JS_TAG_EXCEPTION:
45480 return val;
45481 default:
45482 break;
45483 }
45484 JS_FreeValue(ctx, val);
45485 return JS_UNDEFINED;
45486
45487exception:
45488 JS_FreeValue(ctx, val);
45489 return JS_EXCEPTION;
45490}
45491
45492static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
45493 JSValueConst holder, JSValue val,
45494 JSValueConst indent)
45495{
45496 JSValue indent1, sep, sep1, tab, v, prop;
45497 JSObject *p;
45498 int64_t i, len;
45499 int cl, ret;
45500 BOOL has_content;
45501
45502 indent1 = JS_UNDEFINED;
45503 sep = JS_UNDEFINED;
45504 sep1 = JS_UNDEFINED;
45505 tab = JS_UNDEFINED;
45506 prop = JS_UNDEFINED;
45507
45508 if (js_check_stack_overflow(ctx->rt, 0)) {
45509 JS_ThrowStackOverflow(ctx);
45510 goto exception;
45511 }
45512
45513 if (JS_IsObject(val)) {
45514 p = JS_VALUE_GET_OBJ(val);
45515 cl = p->class_id;
45516 if (cl == JS_CLASS_STRING) {
45517 val = JS_ToStringFree(ctx, val);
45518 if (JS_IsException(val))
45519 goto exception;
45520 goto concat_primitive;
45521 } else if (cl == JS_CLASS_NUMBER) {
45522 val = JS_ToNumberFree(ctx, val);
45523 if (JS_IsException(val))
45524 goto exception;
45525 goto concat_primitive;
45526 } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT)
45527 {
45528 /* This will thow the same error as for the primitive object */
45529 set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data));
45530 goto concat_primitive;
45531 }
45532 v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
45533 if (JS_IsException(v))
45534 goto exception;
45535 if (JS_ToBoolFree(ctx, v)) {
45536 JS_ThrowTypeError(ctx, "circular reference");
45537 goto exception;
45538 }
45539 indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
45540 if (JS_IsException(indent1))
45541 goto exception;
45542 if (!JS_IsEmptyString(jsc->gap)) {
45543 sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
45544 if (JS_IsException(sep))
45545 goto exception;
45546 sep1 = js_new_string8(ctx, " ");
45547 if (JS_IsException(sep1))
45548 goto exception;
45549 } else {
45550 sep = JS_DupValue(ctx, jsc->empty);
45551 sep1 = JS_DupValue(ctx, jsc->empty);
45552 }
45553 v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
45554 if (check_exception_free(ctx, v))
45555 goto exception;
45556 ret = JS_IsArray(ctx, val);
45557 if (ret < 0)
45558 goto exception;
45559 if (ret) {
45560 if (js_get_length64(ctx, &len, val))
45561 goto exception;
45562 string_buffer_putc8(jsc->b, '[');
45563 for(i = 0; i < len; i++) {
45564 if (i > 0)
45565 string_buffer_putc8(jsc->b, ',');
45566 string_buffer_concat_value(jsc->b, sep);
45567 v = JS_GetPropertyInt64(ctx, val, i);
45568 if (JS_IsException(v))
45569 goto exception;
45570 /* XXX: could do this string conversion only when needed */
45571 prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
45572 if (JS_IsException(prop))
45573 goto exception;
45574 v = js_json_check(ctx, jsc, val, v, prop);
45575 JS_FreeValue(ctx, prop);
45576 prop = JS_UNDEFINED;
45577 if (JS_IsException(v))
45578 goto exception;
45579 if (JS_IsUndefined(v))
45580 v = JS_NULL;
45581 if (js_json_to_str(ctx, jsc, val, v, indent1))
45582 goto exception;
45583 }
45584 if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
45585 string_buffer_putc8(jsc->b, '\n');
45586 string_buffer_concat_value(jsc->b, indent);
45587 }
45588 string_buffer_putc8(jsc->b, ']');
45589 } else {
45590 if (!JS_IsUndefined(jsc->property_list))
45591 tab = JS_DupValue(ctx, jsc->property_list);
45592 else
45593 tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
45594 if (JS_IsException(tab))
45595 goto exception;
45596 if (js_get_length64(ctx, &len, tab))
45597 goto exception;
45598 string_buffer_putc8(jsc->b, '{');
45599 has_content = FALSE;
45600 for(i = 0; i < len; i++) {
45601 JS_FreeValue(ctx, prop);
45602 prop = JS_GetPropertyInt64(ctx, tab, i);
45603 if (JS_IsException(prop))
45604 goto exception;
45605 v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
45606 if (JS_IsException(v))
45607 goto exception;
45608 v = js_json_check(ctx, jsc, val, v, prop);
45609 if (JS_IsException(v))
45610 goto exception;
45611 if (!JS_IsUndefined(v)) {
45612 if (has_content)
45613 string_buffer_putc8(jsc->b, ',');
45614 prop = JS_ToQuotedStringFree(ctx, prop);
45615 if (JS_IsException(prop)) {
45616 JS_FreeValue(ctx, v);
45617 goto exception;
45618 }
45619 string_buffer_concat_value(jsc->b, sep);
45620 string_buffer_concat_value(jsc->b, prop);
45621 string_buffer_putc8(jsc->b, ':');
45622 string_buffer_concat_value(jsc->b, sep1);
45623 if (js_json_to_str(ctx, jsc, val, v, indent1))
45624 goto exception;
45625 has_content = TRUE;
45626 }
45627 }
45628 if (has_content && !JS_IsEmptyString(jsc->gap)) {
45629 string_buffer_putc8(jsc->b, '\n');
45630 string_buffer_concat_value(jsc->b, indent);
45631 }
45632 string_buffer_putc8(jsc->b, '}');
45633 }
45634 if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
45635 goto exception;
45636 JS_FreeValue(ctx, val);
45637 JS_FreeValue(ctx, tab);
45638 JS_FreeValue(ctx, sep);
45639 JS_FreeValue(ctx, sep1);
45640 JS_FreeValue(ctx, indent1);
45641 JS_FreeValue(ctx, prop);
45642 return 0;
45643 }
45644 concat_primitive:
45645 switch (JS_VALUE_GET_NORM_TAG(val)) {
45646 case JS_TAG_STRING:
45647 case JS_TAG_STRING_ROPE:
45648 val = JS_ToQuotedStringFree(ctx, val);
45649 if (JS_IsException(val))
45650 goto exception;
45651 goto concat_value;
45652 case JS_TAG_FLOAT64:
45653 if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
45654 val = JS_NULL;
45655 }
45656 goto concat_value;
45657 case JS_TAG_INT:
45658 case JS_TAG_BOOL:
45659 case JS_TAG_NULL:
45660 concat_value:
45661 return string_buffer_concat_value_free(jsc->b, val);
45662 case JS_TAG_SHORT_BIG_INT:
45663 case JS_TAG_BIG_INT:
45664 /* reject big numbers: use toJSON method to override */
45665 JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt");
45666 goto exception;
45667 default:
45668 JS_FreeValue(ctx, val);
45669 return 0;
45670 }
45671
45672exception:
45673 JS_FreeValue(ctx, val);
45674 JS_FreeValue(ctx, tab);
45675 JS_FreeValue(ctx, sep);
45676 JS_FreeValue(ctx, sep1);
45677 JS_FreeValue(ctx, indent1);
45678 JS_FreeValue(ctx, prop);
45679 return -1;
45680}
45681
45682JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
45683 JSValueConst replacer, JSValueConst space0)
45684{
45685 StringBuffer b_s;
45686 JSONStringifyContext jsc_s, *jsc = &jsc_s;
45687 JSValue val, v, space, ret, wrapper;
45688 int res;
45689 int64_t i, j, n;
45690
45691 jsc->replacer_func = JS_UNDEFINED;
45692 jsc->stack = JS_UNDEFINED;
45693 jsc->property_list = JS_UNDEFINED;
45694 jsc->gap = JS_UNDEFINED;
45695 jsc->b = &b_s;
45696 jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
45697 ret = JS_UNDEFINED;
45698 wrapper = JS_UNDEFINED;
45699
45700 string_buffer_init(ctx, jsc->b, 0);
45701 jsc->stack = JS_NewArray(ctx);
45702 if (JS_IsException(jsc->stack))
45703 goto exception;
45704 if (JS_IsFunction(ctx, replacer)) {
45705 jsc->replacer_func = replacer;
45706 } else {
45707 res = JS_IsArray(ctx, replacer);
45708 if (res < 0)
45709 goto exception;
45710 if (res) {
45711 /* XXX: enumeration is not fully correct */
45712 jsc->property_list = JS_NewArray(ctx);
45713 if (JS_IsException(jsc->property_list))
45714 goto exception;
45715 if (js_get_length64(ctx, &n, replacer))
45716 goto exception;
45717 for (i = j = 0; i < n; i++) {
45718 JSValue present;
45719 v = JS_GetPropertyInt64(ctx, replacer, i);
45720 if (JS_IsException(v))
45721 goto exception;
45722 if (JS_IsObject(v)) {
45723 JSObject *p = JS_VALUE_GET_OBJ(v);
45724 if (p->class_id == JS_CLASS_STRING ||
45725 p->class_id == JS_CLASS_NUMBER) {
45726 v = JS_ToStringFree(ctx, v);
45727 if (JS_IsException(v))
45728 goto exception;
45729 } else {
45730 JS_FreeValue(ctx, v);
45731 continue;
45732 }
45733 } else if (JS_IsNumber(v)) {
45734 v = JS_ToStringFree(ctx, v);
45735 if (JS_IsException(v))
45736 goto exception;
45737 } else if (!JS_IsString(v)) {
45738 JS_FreeValue(ctx, v);
45739 continue;
45740 }
45741 present = js_array_includes(ctx, jsc->property_list,
45742 1, (JSValueConst *)&v);
45743 if (JS_IsException(present)) {
45744 JS_FreeValue(ctx, v);
45745 goto exception;
45746 }
45747 if (!JS_ToBoolFree(ctx, present)) {
45748 JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
45749 } else {
45750 JS_FreeValue(ctx, v);
45751 }
45752 }
45753 }
45754 }
45755 space = JS_DupValue(ctx, space0);
45756 if (JS_IsObject(space)) {
45757 JSObject *p = JS_VALUE_GET_OBJ(space);
45758 if (p->class_id == JS_CLASS_NUMBER) {
45759 space = JS_ToNumberFree(ctx, space);
45760 } else if (p->class_id == JS_CLASS_STRING) {
45761 space = JS_ToStringFree(ctx, space);
45762 }
45763 if (JS_IsException(space)) {
45764 JS_FreeValue(ctx, space);
45765 goto exception;
45766 }
45767 }
45768 if (JS_IsNumber(space)) {
45769 int n;
45770 if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
45771 goto exception;
45772 jsc->gap = js_new_string8_len(ctx, " ", n);
45773 } else if (JS_IsString(space)) {
45774 JSString *p = JS_VALUE_GET_STRING(space);
45775 jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
45776 } else {
45777 jsc->gap = JS_DupValue(ctx, jsc->empty);
45778 }
45779 JS_FreeValue(ctx, space);
45780 if (JS_IsException(jsc->gap))
45781 goto exception;
45782 wrapper = JS_NewObject(ctx);
45783 if (JS_IsException(wrapper))
45784 goto exception;
45785 if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
45786 JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
45787 goto exception;
45788 val = JS_DupValue(ctx, obj);
45789
45790 val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
45791 if (JS_IsException(val))
45792 goto exception;
45793 if (JS_IsUndefined(val)) {
45794 ret = JS_UNDEFINED;
45795 goto done1;
45796 }
45797 if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
45798 goto exception;
45799
45800 ret = string_buffer_end(jsc->b);
45801 goto done;
45802
45803exception:
45804 ret = JS_EXCEPTION;
45805done1:
45806 string_buffer_free(jsc->b);
45807done:
45808 JS_FreeValue(ctx, wrapper);
45809 JS_FreeValue(ctx, jsc->empty);
45810 JS_FreeValue(ctx, jsc->gap);
45811 JS_FreeValue(ctx, jsc->property_list);
45812 JS_FreeValue(ctx, jsc->stack);
45813 return ret;
45814}
45815
45816static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
45817 int argc, JSValueConst *argv)
45818{
45819 // stringify(val, replacer, space)
45820 return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
45821}
45822
45823static const JSCFunctionListEntry js_json_funcs[] = {
45824 JS_CFUNC_DEF("parse", 2, js_json_parse ),
45825 JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
45826 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
45827};
45828
45829static const JSCFunctionListEntry js_json_obj[] = {
45830 JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
45831};
45832
45833void JS_AddIntrinsicJSON(JSContext *ctx)
45834{
45835 /* add JSON as autoinit object */
45836 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
45837}
45838
45839/* Reflect */
45840
45841static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
45842 int argc, JSValueConst *argv)
45843{
45844 return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
45845}
45846
45847static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
45848 int argc, JSValueConst *argv)
45849{
45850 JSValueConst func, array_arg, new_target;
45851 JSValue *tab, ret;
45852 uint32_t len;
45853
45854 func = argv[0];
45855 array_arg = argv[1];
45856 if (argc > 2) {
45857 new_target = argv[2];
45858 if (!JS_IsConstructor(ctx, new_target))
45859 return JS_ThrowTypeError(ctx, "not a constructor");
45860 } else {
45861 new_target = func;
45862 }
45863 tab = build_arg_list(ctx, &len, array_arg);
45864 if (!tab)
45865 return JS_EXCEPTION;
45866 ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
45867 free_arg_list(ctx, tab, len);
45868 return ret;
45869}
45870
45871static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
45872 int argc, JSValueConst *argv)
45873{
45874 JSValueConst obj;
45875 JSAtom atom;
45876 int ret;
45877
45878 obj = argv[0];
45879 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
45880 return JS_ThrowTypeErrorNotAnObject(ctx);
45881 atom = JS_ValueToAtom(ctx, argv[1]);
45882 if (unlikely(atom == JS_ATOM_NULL))
45883 return JS_EXCEPTION;
45884 ret = JS_DeleteProperty(ctx, obj, atom, 0);
45885 JS_FreeAtom(ctx, atom);
45886 if (ret < 0)
45887 return JS_EXCEPTION;
45888 else
45889 return JS_NewBool(ctx, ret);
45890}
45891
45892static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
45893 int argc, JSValueConst *argv)
45894{
45895 JSValueConst obj, prop, receiver;
45896 JSAtom atom;
45897 JSValue ret;
45898
45899 obj = argv[0];
45900 prop = argv[1];
45901 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
45902 return JS_ThrowTypeErrorNotAnObject(ctx);
45903 if (argc > 2)
45904 receiver = argv[2];
45905 else
45906 receiver = obj;
45907 atom = JS_ValueToAtom(ctx, prop);
45908 if (unlikely(atom == JS_ATOM_NULL))
45909 return JS_EXCEPTION;
45910 ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
45911 JS_FreeAtom(ctx, atom);
45912 return ret;
45913}
45914
45915static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
45916 int argc, JSValueConst *argv)
45917{
45918 JSValueConst obj, prop;
45919 JSAtom atom;
45920 int ret;
45921
45922 obj = argv[0];
45923 prop = argv[1];
45924 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
45925 return JS_ThrowTypeErrorNotAnObject(ctx);
45926 atom = JS_ValueToAtom(ctx, prop);
45927 if (unlikely(atom == JS_ATOM_NULL))
45928 return JS_EXCEPTION;
45929 ret = JS_HasProperty(ctx, obj, atom);
45930 JS_FreeAtom(ctx, atom);
45931 if (ret < 0)
45932 return JS_EXCEPTION;
45933 else
45934 return JS_NewBool(ctx, ret);
45935}
45936
45937static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
45938 int argc, JSValueConst *argv)
45939{
45940 JSValueConst obj, prop, val, receiver;
45941 int ret;
45942 JSAtom atom;
45943
45944 obj = argv[0];
45945 prop = argv[1];
45946 val = argv[2];
45947 if (argc > 3)
45948 receiver = argv[3];
45949 else
45950 receiver = obj;
45951 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
45952 return JS_ThrowTypeErrorNotAnObject(ctx);
45953 atom = JS_ValueToAtom(ctx, prop);
45954 if (unlikely(atom == JS_ATOM_NULL))
45955 return JS_EXCEPTION;
45956 ret = JS_SetPropertyInternal(ctx, obj, atom,
45957 JS_DupValue(ctx, val), receiver, 0);
45958 JS_FreeAtom(ctx, atom);
45959 if (ret < 0)
45960 return JS_EXCEPTION;
45961 else
45962 return JS_NewBool(ctx, ret);
45963}
45964
45965static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
45966 int argc, JSValueConst *argv)
45967{
45968 int ret;
45969 ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
45970 if (ret < 0)
45971 return JS_EXCEPTION;
45972 else
45973 return JS_NewBool(ctx, ret);
45974}
45975
45976static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
45977 int argc, JSValueConst *argv)
45978{
45979 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
45980 return JS_ThrowTypeErrorNotAnObject(ctx);
45981 return JS_GetOwnPropertyNames2(ctx, argv[0],
45982 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
45983 JS_ITERATOR_KIND_KEY);
45984}
45985
45986static const JSCFunctionListEntry js_reflect_funcs[] = {
45987 JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
45988 JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
45989 JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
45990 JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
45991 JS_CFUNC_DEF("get", 2, js_reflect_get ),
45992 JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
45993 JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
45994 JS_CFUNC_DEF("has", 2, js_reflect_has ),
45995 JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
45996 JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
45997 JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
45998 JS_CFUNC_DEF("set", 3, js_reflect_set ),
45999 JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
46000 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
46001};
46002
46003static const JSCFunctionListEntry js_reflect_obj[] = {
46004 JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
46005};
46006
46007/* Proxy */
46008
46009static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
46010{
46011 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
46012 if (s) {
46013 JS_FreeValueRT(rt, s->target);
46014 JS_FreeValueRT(rt, s->handler);
46015 js_free_rt(rt, s);
46016 }
46017}
46018
46019static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
46020 JS_MarkFunc *mark_func)
46021{
46022 JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
46023 if (s) {
46024 JS_MarkValue(rt, s->target, mark_func);
46025 JS_MarkValue(rt, s->handler, mark_func);
46026 }
46027}
46028
46029static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
46030{
46031 return JS_ThrowTypeError(ctx, "revoked proxy");
46032}
46033
46034static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
46035 JSValueConst obj, JSAtom name)
46036{
46037 JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
46038 JSValue method;
46039
46040 /* safer to test recursion in all proxy methods */
46041 if (js_check_stack_overflow(ctx->rt, 0)) {
46042 JS_ThrowStackOverflow(ctx);
46043 return NULL;
46044 }
46045
46046 /* 's' should never be NULL */
46047 if (s->is_revoked) {
46048 JS_ThrowTypeErrorRevokedProxy(ctx);
46049 return NULL;
46050 }
46051 method = JS_GetProperty(ctx, s->handler, name);
46052 if (JS_IsException(method))
46053 return NULL;
46054 if (JS_IsNull(method))
46055 method = JS_UNDEFINED;
46056 *pmethod = method;
46057 return s;
46058}
46059
46060static JSValue js_proxy_get_prototype(JSContext *ctx, JSValueConst obj)
46061{
46062 JSProxyData *s;
46063 JSValue method, ret, proto1;
46064 int res;
46065
46066 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
46067 if (!s)
46068 return JS_EXCEPTION;
46069 if (JS_IsUndefined(method))
46070 return JS_GetPrototype(ctx, s->target);
46071 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
46072 if (JS_IsException(ret))
46073 return ret;
46074 if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
46075 JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
46076 goto fail;
46077 }
46078 res = JS_IsExtensible(ctx, s->target);
46079 if (res < 0) {
46080 JS_FreeValue(ctx, ret);
46081 return JS_EXCEPTION;
46082 }
46083 if (!res) {
46084 /* check invariant */
46085 proto1 = JS_GetPrototype(ctx, s->target);
46086 if (JS_IsException(proto1)) {
46087 JS_FreeValue(ctx, ret);
46088 return JS_EXCEPTION;
46089 }
46090 if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
46091 JS_FreeValue(ctx, proto1);
46092 fail:
46093 JS_FreeValue(ctx, ret);
46094 return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
46095 }
46096 JS_FreeValue(ctx, proto1);
46097 }
46098 return ret;
46099}
46100
46101static int js_proxy_set_prototype(JSContext *ctx, JSValueConst obj,
46102 JSValueConst proto_val)
46103{
46104 JSProxyData *s;
46105 JSValue method, ret, proto1;
46106 JSValueConst args[2];
46107 BOOL res;
46108 int res2;
46109
46110 s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
46111 if (!s)
46112 return -1;
46113 if (JS_IsUndefined(method))
46114 return JS_SetPrototypeInternal(ctx, s->target, proto_val, FALSE);
46115 args[0] = s->target;
46116 args[1] = proto_val;
46117 ret = JS_CallFree(ctx, method, s->handler, 2, args);
46118 if (JS_IsException(ret))
46119 return -1;
46120 res = JS_ToBoolFree(ctx, ret);
46121 if (!res)
46122 return FALSE;
46123 res2 = JS_IsExtensible(ctx, s->target);
46124 if (res2 < 0)
46125 return -1;
46126 if (!res2) {
46127 proto1 = JS_GetPrototype(ctx, s->target);
46128 if (JS_IsException(proto1))
46129 return -1;
46130 if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
46131 JS_FreeValue(ctx, proto1);
46132 JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
46133 return -1;
46134 }
46135 JS_FreeValue(ctx, proto1);
46136 }
46137 return TRUE;
46138}
46139
46140static int js_proxy_is_extensible(JSContext *ctx, JSValueConst obj)
46141{
46142 JSProxyData *s;
46143 JSValue method, ret;
46144 BOOL res;
46145 int res2;
46146
46147 s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
46148 if (!s)
46149 return -1;
46150 if (JS_IsUndefined(method))
46151 return JS_IsExtensible(ctx, s->target);
46152 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
46153 if (JS_IsException(ret))
46154 return -1;
46155 res = JS_ToBoolFree(ctx, ret);
46156 res2 = JS_IsExtensible(ctx, s->target);
46157 if (res2 < 0)
46158 return res2;
46159 if (res != res2) {
46160 JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
46161 return -1;
46162 }
46163 return res;
46164}
46165
46166static int js_proxy_prevent_extensions(JSContext *ctx, JSValueConst obj)
46167{
46168 JSProxyData *s;
46169 JSValue method, ret;
46170 BOOL res;
46171 int res2;
46172
46173 s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
46174 if (!s)
46175 return -1;
46176 if (JS_IsUndefined(method))
46177 return JS_PreventExtensions(ctx, s->target);
46178 ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
46179 if (JS_IsException(ret))
46180 return -1;
46181 res = JS_ToBoolFree(ctx, ret);
46182 if (res) {
46183 res2 = JS_IsExtensible(ctx, s->target);
46184 if (res2 < 0)
46185 return res2;
46186 if (res2) {
46187 JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
46188 return -1;
46189 }
46190 }
46191 return res;
46192}
46193
46194static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
46195{
46196 JSProxyData *s;
46197 JSValue method, ret1, atom_val;
46198 int ret, res;
46199 JSObject *p;
46200 JSValueConst args[2];
46201 BOOL res2;
46202
46203 s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
46204 if (!s)
46205 return -1;
46206 if (JS_IsUndefined(method))
46207 return JS_HasProperty(ctx, s->target, atom);
46208 atom_val = JS_AtomToValue(ctx, atom);
46209 if (JS_IsException(atom_val)) {
46210 JS_FreeValue(ctx, method);
46211 return -1;
46212 }
46213 args[0] = s->target;
46214 args[1] = atom_val;
46215 ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
46216 JS_FreeValue(ctx, atom_val);
46217 if (JS_IsException(ret1))
46218 return -1;
46219 ret = JS_ToBoolFree(ctx, ret1);
46220 if (!ret) {
46221 JSPropertyDescriptor desc;
46222 p = JS_VALUE_GET_OBJ(s->target);
46223 res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
46224 if (res < 0)
46225 return -1;
46226 if (res) {
46227 res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
46228 js_free_desc(ctx, &desc);
46229 if (res2 || !p->extensible) {
46230 JS_ThrowTypeError(ctx, "proxy: inconsistent has");
46231 return -1;
46232 }
46233 }
46234 }
46235 return ret;
46236}
46237
46238static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
46239 JSValueConst receiver)
46240{
46241 JSProxyData *s;
46242 JSValue method, ret, atom_val;
46243 int res;
46244 JSValueConst args[3];
46245 JSPropertyDescriptor desc;
46246
46247 s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
46248 if (!s)
46249 return JS_EXCEPTION;
46250 /* Note: recursion is possible thru the prototype of s->target */
46251 if (JS_IsUndefined(method))
46252 return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
46253 atom_val = JS_AtomToValue(ctx, atom);
46254 if (JS_IsException(atom_val)) {
46255 JS_FreeValue(ctx, method);
46256 return JS_EXCEPTION;
46257 }
46258 args[0] = s->target;
46259 args[1] = atom_val;
46260 args[2] = receiver;
46261 ret = JS_CallFree(ctx, method, s->handler, 3, args);
46262 JS_FreeValue(ctx, atom_val);
46263 if (JS_IsException(ret))
46264 return JS_EXCEPTION;
46265 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
46266 if (res < 0) {
46267 JS_FreeValue(ctx, ret);
46268 return JS_EXCEPTION;
46269 }
46270 if (res) {
46271 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
46272 if (!js_same_value(ctx, desc.value, ret)) {
46273 goto fail;
46274 }
46275 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
46276 if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
46277 fail:
46278 js_free_desc(ctx, &desc);
46279 JS_FreeValue(ctx, ret);
46280 return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
46281 }
46282 }
46283 js_free_desc(ctx, &desc);
46284 }
46285 return ret;
46286}
46287
46288static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
46289 JSValueConst value, JSValueConst receiver, int flags)
46290{
46291 JSProxyData *s;
46292 JSValue method, ret1, atom_val;
46293 int ret, res;
46294 JSValueConst args[4];
46295
46296 s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
46297 if (!s)
46298 return -1;
46299 if (JS_IsUndefined(method)) {
46300 return JS_SetPropertyInternal(ctx, s->target, atom,
46301 JS_DupValue(ctx, value), receiver,
46302 flags);
46303 }
46304 atom_val = JS_AtomToValue(ctx, atom);
46305 if (JS_IsException(atom_val)) {
46306 JS_FreeValue(ctx, method);
46307 return -1;
46308 }
46309 args[0] = s->target;
46310 args[1] = atom_val;
46311 args[2] = value;
46312 args[3] = receiver;
46313 ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
46314 JS_FreeValue(ctx, atom_val);
46315 if (JS_IsException(ret1))
46316 return -1;
46317 ret = JS_ToBoolFree(ctx, ret1);
46318 if (ret) {
46319 JSPropertyDescriptor desc;
46320 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
46321 if (res < 0)
46322 return -1;
46323 if (res) {
46324 if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
46325 if (!js_same_value(ctx, desc.value, value)) {
46326 goto fail;
46327 }
46328 } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
46329 fail:
46330 js_free_desc(ctx, &desc);
46331 JS_ThrowTypeError(ctx, "proxy: inconsistent set");
46332 return -1;
46333 }
46334 js_free_desc(ctx, &desc);
46335 }
46336 } else {
46337 if ((flags & JS_PROP_THROW) ||
46338 ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
46339 JS_ThrowTypeError(ctx, "proxy: cannot set property");
46340 return -1;
46341 }
46342 }
46343 return ret;
46344}
46345
46346static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
46347 JSValueConst getter, JSValueConst setter,
46348 int flags)
46349{
46350 JSValue ret;
46351 ret = JS_NewObject(ctx);
46352 if (JS_IsException(ret))
46353 return ret;
46354 if (flags & JS_PROP_HAS_GET) {
46355 JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
46356 JS_PROP_C_W_E);
46357 }
46358 if (flags & JS_PROP_HAS_SET) {
46359 JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
46360 JS_PROP_C_W_E);
46361 }
46362 if (flags & JS_PROP_HAS_VALUE) {
46363 JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
46364 JS_PROP_C_W_E);
46365 }
46366 if (flags & JS_PROP_HAS_WRITABLE) {
46367 JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
46368 JS_NewBool(ctx, flags & JS_PROP_WRITABLE),
46369 JS_PROP_C_W_E);
46370 }
46371 if (flags & JS_PROP_HAS_ENUMERABLE) {
46372 JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
46373 JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE),
46374 JS_PROP_C_W_E);
46375 }
46376 if (flags & JS_PROP_HAS_CONFIGURABLE) {
46377 JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
46378 JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE),
46379 JS_PROP_C_W_E);
46380 }
46381 return ret;
46382}
46383
46384static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
46385 JSValueConst obj, JSAtom prop)
46386{
46387 JSProxyData *s;
46388 JSValue method, trap_result_obj, prop_val;
46389 int res, target_desc_ret, ret;
46390 JSObject *p;
46391 JSValueConst args[2];
46392 JSPropertyDescriptor result_desc, target_desc;
46393
46394 s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
46395 if (!s)
46396 return -1;
46397 p = JS_VALUE_GET_OBJ(s->target);
46398 if (JS_IsUndefined(method)) {
46399 return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
46400 }
46401 prop_val = JS_AtomToValue(ctx, prop);
46402 if (JS_IsException(prop_val)) {
46403 JS_FreeValue(ctx, method);
46404 return -1;
46405 }
46406 args[0] = s->target;
46407 args[1] = prop_val;
46408 trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
46409 JS_FreeValue(ctx, prop_val);
46410 if (JS_IsException(trap_result_obj))
46411 return -1;
46412 if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
46413 JS_FreeValue(ctx, trap_result_obj);
46414 goto fail;
46415 }
46416 target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
46417 if (target_desc_ret < 0) {
46418 JS_FreeValue(ctx, trap_result_obj);
46419 return -1;
46420 }
46421 if (target_desc_ret)
46422 js_free_desc(ctx, &target_desc);
46423 if (JS_IsUndefined(trap_result_obj)) {
46424 if (target_desc_ret) {
46425 if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
46426 goto fail;
46427 }
46428 ret = FALSE;
46429 } else {
46430 int flags1, extensible_target;
46431 extensible_target = JS_IsExtensible(ctx, s->target);
46432 if (extensible_target < 0) {
46433 JS_FreeValue(ctx, trap_result_obj);
46434 return -1;
46435 }
46436 res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
46437 JS_FreeValue(ctx, trap_result_obj);
46438 if (res < 0)
46439 return -1;
46440
46441 /* convert the result_desc.flags to property flags */
46442 if (result_desc.flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
46443 result_desc.flags |= JS_PROP_GETSET;
46444 } else {
46445 result_desc.flags |= JS_PROP_NORMAL;
46446 }
46447 result_desc.flags &= (JS_PROP_C_W_E | JS_PROP_TMASK);
46448
46449 if (target_desc_ret) {
46450 /* convert result_desc.flags to defineProperty flags */
46451 flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
46452 if (result_desc.flags & JS_PROP_GETSET)
46453 flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
46454 else
46455 flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
46456 /* XXX: not complete check: need to compare value &
46457 getter/setter as in defineproperty */
46458 if (!check_define_prop_flags(target_desc.flags, flags1))
46459 goto fail1;
46460 } else {
46461 if (!extensible_target)
46462 goto fail1;
46463 }
46464 if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
46465 if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
46466 goto fail1;
46467 if ((result_desc.flags &
46468 (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
46469 target_desc_ret &&
46470 (target_desc.flags & JS_PROP_WRITABLE) != 0) {
46471 /* proxy-missing-checks */
46472 fail1:
46473 js_free_desc(ctx, &result_desc);
46474 fail:
46475 JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
46476 return -1;
46477 }
46478 }
46479 ret = TRUE;
46480 if (pdesc) {
46481 *pdesc = result_desc;
46482 } else {
46483 js_free_desc(ctx, &result_desc);
46484 }
46485 }
46486 return ret;
46487}
46488
46489static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
46490 JSAtom prop, JSValueConst val,
46491 JSValueConst getter, JSValueConst setter,
46492 int flags)
46493{
46494 JSProxyData *s;
46495 JSValue method, ret1, prop_val, desc_val;
46496 int res, ret;
46497 JSObject *p;
46498 JSValueConst args[3];
46499 JSPropertyDescriptor desc;
46500 BOOL setting_not_configurable;
46501
46502 s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
46503 if (!s)
46504 return -1;
46505 if (JS_IsUndefined(method)) {
46506 return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
46507 }
46508 prop_val = JS_AtomToValue(ctx, prop);
46509 if (JS_IsException(prop_val)) {
46510 JS_FreeValue(ctx, method);
46511 return -1;
46512 }
46513 desc_val = js_create_desc(ctx, val, getter, setter, flags);
46514 if (JS_IsException(desc_val)) {
46515 JS_FreeValue(ctx, prop_val);
46516 JS_FreeValue(ctx, method);
46517 return -1;
46518 }
46519 args[0] = s->target;
46520 args[1] = prop_val;
46521 args[2] = desc_val;
46522 ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
46523 JS_FreeValue(ctx, prop_val);
46524 JS_FreeValue(ctx, desc_val);
46525 if (JS_IsException(ret1))
46526 return -1;
46527 ret = JS_ToBoolFree(ctx, ret1);
46528 if (!ret) {
46529 if (flags & JS_PROP_THROW) {
46530 JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
46531 return -1;
46532 } else {
46533 return 0;
46534 }
46535 }
46536 p = JS_VALUE_GET_OBJ(s->target);
46537 res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
46538 if (res < 0)
46539 return -1;
46540 setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
46541 JS_PROP_CONFIGURABLE)) ==
46542 JS_PROP_HAS_CONFIGURABLE);
46543 if (!res) {
46544 if (!p->extensible || setting_not_configurable)
46545 goto fail;
46546 } else {
46547 if (!check_define_prop_flags(desc.flags, flags))
46548 goto fail1;
46549 /* do the missing check from check_define_prop_flags() */
46550 if (!(desc.flags & JS_PROP_CONFIGURABLE)) {
46551 if ((desc.flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
46552 if ((flags & JS_PROP_HAS_GET) &&
46553 !js_same_value(ctx, getter, desc.getter)) {
46554 goto fail1;
46555 }
46556 if ((flags & JS_PROP_HAS_SET) &&
46557 !js_same_value(ctx, setter, desc.setter)) {
46558 goto fail1;
46559 }
46560 } else if (!(desc.flags & JS_PROP_WRITABLE)) {
46561 if ((flags & JS_PROP_HAS_VALUE) &&
46562 !js_same_value(ctx, val, desc.value)) {
46563 goto fail1;
46564 }
46565 }
46566 }
46567
46568 /* additional checks */
46569 if ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)
46570 goto fail1;
46571
46572 if ((desc.flags & JS_PROP_TMASK) != JS_PROP_GETSET &&
46573 (desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == JS_PROP_WRITABLE &&
46574 (flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
46575 fail1:
46576 js_free_desc(ctx, &desc);
46577 fail:
46578 JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
46579 return -1;
46580 }
46581 js_free_desc(ctx, &desc);
46582 }
46583 return 1;
46584}
46585
46586static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
46587 JSAtom atom)
46588{
46589 JSProxyData *s;
46590 JSValue method, ret, atom_val;
46591 int res, res2, is_extensible;
46592 JSValueConst args[2];
46593
46594 s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
46595 if (!s)
46596 return -1;
46597 if (JS_IsUndefined(method)) {
46598 return JS_DeleteProperty(ctx, s->target, atom, 0);
46599 }
46600 atom_val = JS_AtomToValue(ctx, atom);;
46601 if (JS_IsException(atom_val)) {
46602 JS_FreeValue(ctx, method);
46603 return -1;
46604 }
46605 args[0] = s->target;
46606 args[1] = atom_val;
46607 ret = JS_CallFree(ctx, method, s->handler, 2, args);
46608 JS_FreeValue(ctx, atom_val);
46609 if (JS_IsException(ret))
46610 return -1;
46611 res = JS_ToBoolFree(ctx, ret);
46612 if (res) {
46613 JSPropertyDescriptor desc;
46614 res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
46615 if (res2 < 0)
46616 return -1;
46617 if (res2) {
46618 if (!(desc.flags & JS_PROP_CONFIGURABLE))
46619 goto fail;
46620 is_extensible = JS_IsExtensible(ctx, s->target);
46621 if (is_extensible < 0)
46622 goto fail1;
46623 if (!is_extensible) {
46624 /* proxy-missing-checks */
46625 fail:
46626 JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
46627 fail1:
46628 js_free_desc(ctx, &desc);
46629 return -1;
46630 }
46631 js_free_desc(ctx, &desc);
46632 }
46633 }
46634 return res;
46635}
46636
46637/* return the index of the property or -1 if not found */
46638static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
46639{
46640 int i;
46641 for(i = 0; i < n; i++) {
46642 if (tab[i].atom == atom)
46643 return i;
46644 }
46645 return -1;
46646}
46647
46648static int js_proxy_get_own_property_names(JSContext *ctx,
46649 JSPropertyEnum **ptab,
46650 uint32_t *plen,
46651 JSValueConst obj)
46652{
46653 JSProxyData *s;
46654 JSValue method, prop_array, val;
46655 uint32_t len, i, len2;
46656 JSPropertyEnum *tab, *tab2;
46657 JSAtom atom;
46658 JSPropertyDescriptor desc;
46659 int res, is_extensible, idx;
46660
46661 s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
46662 if (!s)
46663 return -1;
46664 if (JS_IsUndefined(method)) {
46665 return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
46666 JS_VALUE_GET_OBJ(s->target),
46667 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
46668 }
46669 prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
46670 if (JS_IsException(prop_array))
46671 return -1;
46672 tab = NULL;
46673 len = 0;
46674 tab2 = NULL;
46675 len2 = 0;
46676 if (js_get_length32(ctx, &len, prop_array))
46677 goto fail;
46678 if (len > 0) {
46679 tab = js_mallocz(ctx, sizeof(tab[0]) * len);
46680 if (!tab)
46681 goto fail;
46682 }
46683 for(i = 0; i < len; i++) {
46684 val = JS_GetPropertyUint32(ctx, prop_array, i);
46685 if (JS_IsException(val))
46686 goto fail;
46687 if (!JS_IsString(val) && !JS_IsSymbol(val)) {
46688 JS_FreeValue(ctx, val);
46689 JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
46690 goto fail;
46691 }
46692 atom = JS_ValueToAtom(ctx, val);
46693 JS_FreeValue(ctx, val);
46694 if (atom == JS_ATOM_NULL)
46695 goto fail;
46696 tab[i].atom = atom;
46697 tab[i].is_enumerable = FALSE; /* XXX: redundant? */
46698 }
46699
46700 /* check duplicate properties (XXX: inefficient, could store the
46701 * properties an a temporary object to use the hash) */
46702 for(i = 1; i < len; i++) {
46703 if (find_prop_key(tab, i, tab[i].atom) >= 0) {
46704 JS_ThrowTypeError(ctx, "proxy: duplicate property");
46705 goto fail;
46706 }
46707 }
46708
46709 is_extensible = JS_IsExtensible(ctx, s->target);
46710 if (is_extensible < 0)
46711 goto fail;
46712
46713 /* check if there are non configurable properties */
46714 if (s->is_revoked) {
46715 JS_ThrowTypeErrorRevokedProxy(ctx);
46716 goto fail;
46717 }
46718 if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
46719 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
46720 goto fail;
46721 for(i = 0; i < len2; i++) {
46722 if (s->is_revoked) {
46723 JS_ThrowTypeErrorRevokedProxy(ctx);
46724 goto fail;
46725 }
46726 res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
46727 tab2[i].atom);
46728 if (res < 0)
46729 goto fail;
46730 if (res) { /* safety, property should be found */
46731 js_free_desc(ctx, &desc);
46732 if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
46733 idx = find_prop_key(tab, len, tab2[i].atom);
46734 if (idx < 0) {
46735 JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
46736 goto fail;
46737 }
46738 /* mark the property as found */
46739 if (!is_extensible)
46740 tab[idx].is_enumerable = TRUE;
46741 }
46742 }
46743 }
46744 if (!is_extensible) {
46745 /* check that all property in 'tab' were checked */
46746 for(i = 0; i < len; i++) {
46747 if (!tab[i].is_enumerable) {
46748 JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
46749 goto fail;
46750 }
46751 }
46752 }
46753
46754 js_free_prop_enum(ctx, tab2, len2);
46755 JS_FreeValue(ctx, prop_array);
46756 *ptab = tab;
46757 *plen = len;
46758 return 0;
46759 fail:
46760 js_free_prop_enum(ctx, tab2, len2);
46761 js_free_prop_enum(ctx, tab, len);
46762 JS_FreeValue(ctx, prop_array);
46763 return -1;
46764}
46765
46766static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
46767 JSValueConst new_target,
46768 int argc, JSValueConst *argv)
46769{
46770 JSProxyData *s;
46771 JSValue method, arg_array, ret;
46772 JSValueConst args[3];
46773
46774 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
46775 if (!s)
46776 return JS_EXCEPTION;
46777 if (!JS_IsConstructor(ctx, s->target))
46778 return JS_ThrowTypeError(ctx, "not a constructor");
46779 if (JS_IsUndefined(method))
46780 return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
46781 arg_array = js_create_array(ctx, argc, argv);
46782 if (JS_IsException(arg_array)) {
46783 ret = JS_EXCEPTION;
46784 goto fail;
46785 }
46786 args[0] = s->target;
46787 args[1] = arg_array;
46788 args[2] = new_target;
46789 ret = JS_Call(ctx, method, s->handler, 3, args);
46790 if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
46791 JS_FreeValue(ctx, ret);
46792 ret = JS_ThrowTypeErrorNotAnObject(ctx);
46793 }
46794 fail:
46795 JS_FreeValue(ctx, method);
46796 JS_FreeValue(ctx, arg_array);
46797 return ret;
46798}
46799
46800static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
46801 JSValueConst this_obj,
46802 int argc, JSValueConst *argv, int flags)
46803{
46804 JSProxyData *s;
46805 JSValue method, arg_array, ret;
46806 JSValueConst args[3];
46807
46808 if (flags & JS_CALL_FLAG_CONSTRUCTOR)
46809 return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
46810
46811 s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
46812 if (!s)
46813 return JS_EXCEPTION;
46814 if (!s->is_func) {
46815 JS_FreeValue(ctx, method);
46816 return JS_ThrowTypeError(ctx, "not a function");
46817 }
46818 if (JS_IsUndefined(method))
46819 return JS_Call(ctx, s->target, this_obj, argc, argv);
46820 arg_array = js_create_array(ctx, argc, argv);
46821 if (JS_IsException(arg_array)) {
46822 ret = JS_EXCEPTION;
46823 goto fail;
46824 }
46825 args[0] = s->target;
46826 args[1] = this_obj;
46827 args[2] = arg_array;
46828 ret = JS_Call(ctx, method, s->handler, 3, args);
46829 fail:
46830 JS_FreeValue(ctx, method);
46831 JS_FreeValue(ctx, arg_array);
46832 return ret;
46833}
46834
46835/* `js_resolve_proxy`: resolve the proxy chain
46836 `*pval` is updated with to ultimate proxy target
46837 `throw_exception` controls whether exceptions are thown or not
46838 - return -1 in case of error
46839 - otherwise return 0
46840 */
46841static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) {
46842 int depth = 0;
46843 JSObject *p;
46844 JSProxyData *s;
46845
46846 while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) {
46847 p = JS_VALUE_GET_OBJ(*pval);
46848 if (p->class_id != JS_CLASS_PROXY)
46849 break;
46850 if (depth++ > 1000) {
46851 if (throw_exception)
46852 JS_ThrowStackOverflow(ctx);
46853 return -1;
46854 }
46855 s = p->u.opaque;
46856 if (s->is_revoked) {
46857 if (throw_exception)
46858 JS_ThrowTypeErrorRevokedProxy(ctx);
46859 return -1;
46860 }
46861 *pval = s->target;
46862 }
46863 return 0;
46864}
46865
46866static const JSClassExoticMethods js_proxy_exotic_methods = {
46867 .get_own_property = js_proxy_get_own_property,
46868 .define_own_property = js_proxy_define_own_property,
46869 .delete_property = js_proxy_delete_property,
46870 .get_own_property_names = js_proxy_get_own_property_names,
46871 .has_property = js_proxy_has,
46872 .get_property = js_proxy_get,
46873 .set_property = js_proxy_set,
46874 .get_prototype = js_proxy_get_prototype,
46875 .set_prototype = js_proxy_set_prototype,
46876 .is_extensible = js_proxy_is_extensible,
46877 .prevent_extensions = js_proxy_prevent_extensions,
46878};
46879
46880static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
46881 int argc, JSValueConst *argv)
46882{
46883 JSValueConst target, handler;
46884 JSValue obj;
46885 JSProxyData *s;
46886
46887 target = argv[0];
46888 handler = argv[1];
46889 if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
46890 JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
46891 return JS_ThrowTypeErrorNotAnObject(ctx);
46892
46893 obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
46894 if (JS_IsException(obj))
46895 return obj;
46896 s = js_malloc(ctx, sizeof(JSProxyData));
46897 if (!s) {
46898 JS_FreeValue(ctx, obj);
46899 return JS_EXCEPTION;
46900 }
46901 s->target = JS_DupValue(ctx, target);
46902 s->handler = JS_DupValue(ctx, handler);
46903 s->is_func = JS_IsFunction(ctx, target);
46904 s->is_revoked = FALSE;
46905 JS_SetOpaque(obj, s);
46906 JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
46907 return obj;
46908}
46909
46910static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
46911 int argc, JSValueConst *argv, int magic,
46912 JSValue *func_data)
46913{
46914 JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
46915 if (s) {
46916 /* We do not free the handler and target in case they are
46917 referenced as constants in the C call stack */
46918 s->is_revoked = TRUE;
46919 JS_FreeValue(ctx, func_data[0]);
46920 func_data[0] = JS_NULL;
46921 }
46922 return JS_UNDEFINED;
46923}
46924
46925static JSValue js_proxy_revoke_constructor(JSContext *ctx,
46926 JSValueConst proxy_obj)
46927{
46928 return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
46929}
46930
46931static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
46932 int argc, JSValueConst *argv)
46933{
46934 JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
46935
46936 proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
46937 if (JS_IsException(proxy_obj))
46938 goto fail;
46939 revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
46940 if (JS_IsException(revoke_obj))
46941 goto fail;
46942 obj = JS_NewObject(ctx);
46943 if (JS_IsException(obj))
46944 goto fail;
46945 // XXX: exceptions?
46946 JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
46947 JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
46948 return obj;
46949 fail:
46950 JS_FreeValue(ctx, proxy_obj);
46951 JS_FreeValue(ctx, revoke_obj);
46952 return JS_EXCEPTION;
46953}
46954
46955static const JSCFunctionListEntry js_proxy_funcs[] = {
46956 JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
46957};
46958
46959static const JSClassShortDef js_proxy_class_def[] = {
46960 { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
46961};
46962
46963void JS_AddIntrinsicProxy(JSContext *ctx)
46964{
46965 JSRuntime *rt = ctx->rt;
46966 JSValue obj1;
46967
46968 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
46969 init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
46970 countof(js_proxy_class_def));
46971 rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
46972 rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
46973 }
46974
46975 obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
46976 JS_CFUNC_constructor, 0);
46977 JS_SetConstructorBit(ctx, obj1, TRUE);
46978 JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
46979 countof(js_proxy_funcs));
46980 JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
46981 obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
46982}
46983
46984/* Symbol */
46985
46986static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
46987 int argc, JSValueConst *argv)
46988{
46989 JSValue str;
46990 JSString *p;
46991
46992 if (!JS_IsUndefined(new_target))
46993 return JS_ThrowTypeError(ctx, "not a constructor");
46994 if (argc == 0 || JS_IsUndefined(argv[0])) {
46995 p = NULL;
46996 } else {
46997 str = JS_ToString(ctx, argv[0]);
46998 if (JS_IsException(str))
46999 return JS_EXCEPTION;
47000 p = JS_VALUE_GET_STRING(str);
47001 }
47002 return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
47003}
47004
47005static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
47006{
47007 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
47008 return JS_DupValue(ctx, this_val);
47009
47010 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
47011 JSObject *p = JS_VALUE_GET_OBJ(this_val);
47012 if (p->class_id == JS_CLASS_SYMBOL) {
47013 if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
47014 return JS_DupValue(ctx, p->u.object_data);
47015 }
47016 }
47017 return JS_ThrowTypeError(ctx, "not a symbol");
47018}
47019
47020static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
47021 int argc, JSValueConst *argv)
47022{
47023 JSValue val, ret;
47024 val = js_thisSymbolValue(ctx, this_val);
47025 if (JS_IsException(val))
47026 return val;
47027 /* XXX: use JS_ToStringInternal() with a flags */
47028 ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
47029 JS_FreeValue(ctx, val);
47030 return ret;
47031}
47032
47033static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
47034 int argc, JSValueConst *argv)
47035{
47036 return js_thisSymbolValue(ctx, this_val);
47037}
47038
47039static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
47040{
47041 JSValue val, ret;
47042 JSAtomStruct *p;
47043
47044 val = js_thisSymbolValue(ctx, this_val);
47045 if (JS_IsException(val))
47046 return val;
47047 p = JS_VALUE_GET_PTR(val);
47048 if (p->len == 0 && p->is_wide_char != 0) {
47049 ret = JS_UNDEFINED;
47050 } else {
47051 ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
47052 }
47053 JS_FreeValue(ctx, val);
47054 return ret;
47055}
47056
47057static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
47058 JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
47059 JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
47060 // XXX: should have writable: false
47061 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
47062 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
47063 JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
47064};
47065
47066static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
47067 int argc, JSValueConst *argv)
47068{
47069 JSValue str;
47070
47071 str = JS_ToString(ctx, argv[0]);
47072 if (JS_IsException(str))
47073 return JS_EXCEPTION;
47074 return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
47075}
47076
47077static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
47078 int argc, JSValueConst *argv)
47079{
47080 JSAtomStruct *p;
47081
47082 if (!JS_IsSymbol(argv[0]))
47083 return JS_ThrowTypeError(ctx, "not a symbol");
47084 p = JS_VALUE_GET_PTR(argv[0]);
47085 if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
47086 return JS_UNDEFINED;
47087 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
47088}
47089
47090static const JSCFunctionListEntry js_symbol_funcs[] = {
47091 JS_CFUNC_DEF("for", 1, js_symbol_for ),
47092 JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
47093};
47094
47095/* Set/Map/WeakSet/WeakMap */
47096
47097typedef struct JSMapRecord {
47098 int ref_count; /* used during enumeration to avoid freeing the record */
47099 BOOL empty : 8; /* TRUE if the record is deleted */
47100 struct list_head link;
47101 struct JSMapRecord *hash_next;
47102 JSValue key;
47103 JSValue value;
47104} JSMapRecord;
47105
47106typedef struct JSMapState {
47107 BOOL is_weak; /* TRUE if WeakSet/WeakMap */
47108 struct list_head records; /* list of JSMapRecord.link */
47109 uint32_t record_count;
47110 JSMapRecord **hash_table;
47111 int hash_bits;
47112 uint32_t hash_size; /* = 2 ^ hash_bits */
47113 uint32_t record_count_threshold; /* count at which a hash table
47114 resize is needed */
47115 JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
47116} JSMapState;
47117
47118static BOOL js_weakref_is_target(JSValueConst val)
47119{
47120 switch (JS_VALUE_GET_TAG(val)) {
47121 case JS_TAG_OBJECT:
47122 return TRUE;
47123 case JS_TAG_SYMBOL:
47124 {
47125 JSAtomStruct *p = JS_VALUE_GET_PTR(val);
47126 if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
47127 p->hash != JS_ATOM_HASH_PRIVATE)
47128 return TRUE;
47129 }
47130 break;
47131 default:
47132 break;
47133 }
47134 return FALSE;
47135}
47136
47137/* JS_UNDEFINED is considered as a live weakref */
47138/* XXX: add a specific JSWeakRef value type ? */
47139static BOOL js_weakref_is_live(JSValueConst val)
47140{
47141 int *pref_count;
47142 if (JS_IsUndefined(val))
47143 return TRUE;
47144 pref_count = JS_VALUE_GET_PTR(val);
47145 return (*pref_count != 0);
47146}
47147
47148/* 'val' can be JS_UNDEFINED */
47149static void js_weakref_free(JSRuntime *rt, JSValue val)
47150{
47151 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
47152 JSObject *p = JS_VALUE_GET_OBJ(val);
47153 assert(p->weakref_count >= 1);
47154 p->weakref_count--;
47155 /* 'mark' is tested to avoid freeing the object structure when
47156 it is about to be freed in a cycle or in
47157 free_zero_refcount() */
47158 if (p->weakref_count == 0 && p->header.ref_count == 0 &&
47159 p->header.mark == 0) {
47160 js_free_rt(rt, p);
47161 }
47162 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
47163 JSString *p = JS_VALUE_GET_STRING(val);
47164 assert(p->hash >= 1);
47165 p->hash--;
47166 if (p->hash == 0 && p->header.ref_count == 0) {
47167 /* can remove the dummy structure */
47168 js_free_rt(rt, p);
47169 }
47170 }
47171}
47172
47173/* val must be an object, a symbol or undefined (see
47174 js_weakref_is_target). */
47175static JSValue js_weakref_new(JSContext *ctx, JSValueConst val)
47176{
47177 if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
47178 JSObject *p = JS_VALUE_GET_OBJ(val);
47179 p->weakref_count++;
47180 } else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
47181 JSString *p = JS_VALUE_GET_STRING(val);
47182 /* XXX: could return an exception if too many references */
47183 assert(p->hash < JS_ATOM_HASH_MASK - 2);
47184 p->hash++;
47185 } else {
47186 assert(JS_IsUndefined(val));
47187 }
47188 return (JSValue)val;
47189}
47190
47191#define MAGIC_SET (1 << 0)
47192#define MAGIC_WEAK (1 << 1)
47193
47194static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
47195 int argc, JSValueConst *argv, int magic)
47196{
47197 JSMapState *s;
47198 JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
47199 JSValueConst arr;
47200 BOOL is_set, is_weak;
47201
47202 is_set = magic & MAGIC_SET;
47203 is_weak = ((magic & MAGIC_WEAK) != 0);
47204 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
47205 if (JS_IsException(obj))
47206 return JS_EXCEPTION;
47207 s = js_mallocz(ctx, sizeof(*s));
47208 if (!s)
47209 goto fail;
47210 init_list_head(&s->records);
47211 s->is_weak = is_weak;
47212 if (is_weak) {
47213 s->weakref_header.weakref_type = JS_WEAKREF_TYPE_MAP;
47214 list_add_tail(&s->weakref_header.link, &ctx->rt->weakref_list);
47215 }
47216 JS_SetOpaque(obj, s);
47217 s->hash_bits = 1;
47218 s->hash_size = 1U << s->hash_bits;
47219 s->hash_table = js_mallocz(ctx, sizeof(s->hash_table[0]) * s->hash_size);
47220 if (!s->hash_table)
47221 goto fail;
47222 s->record_count_threshold = 4;
47223
47224 arr = JS_UNDEFINED;
47225 if (argc > 0)
47226 arr = argv[0];
47227 if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
47228 JSValue item, ret;
47229 BOOL done;
47230
47231 adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
47232 if (JS_IsException(adder))
47233 goto fail;
47234 if (!JS_IsFunction(ctx, adder)) {
47235 JS_ThrowTypeError(ctx, "set/add is not a function");
47236 goto fail;
47237 }
47238
47239 iter = JS_GetIterator(ctx, arr, FALSE);
47240 if (JS_IsException(iter))
47241 goto fail;
47242 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
47243 if (JS_IsException(next_method))
47244 goto fail;
47245
47246 for(;;) {
47247 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
47248 if (JS_IsException(item))
47249 goto fail;
47250 if (done)
47251 break;
47252 if (is_set) {
47253 ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
47254 if (JS_IsException(ret)) {
47255 JS_FreeValue(ctx, item);
47256 goto fail_close;
47257 }
47258 } else {
47259 JSValue key, value;
47260 JSValueConst args[2];
47261 key = JS_UNDEFINED;
47262 value = JS_UNDEFINED;
47263 if (!JS_IsObject(item)) {
47264 JS_ThrowTypeErrorNotAnObject(ctx);
47265 goto fail1;
47266 }
47267 key = JS_GetPropertyUint32(ctx, item, 0);
47268 if (JS_IsException(key))
47269 goto fail1;
47270 value = JS_GetPropertyUint32(ctx, item, 1);
47271 if (JS_IsException(value))
47272 goto fail1;
47273 args[0] = key;
47274 args[1] = value;
47275 ret = JS_Call(ctx, adder, obj, 2, args);
47276 if (JS_IsException(ret)) {
47277 fail1:
47278 JS_FreeValue(ctx, item);
47279 JS_FreeValue(ctx, key);
47280 JS_FreeValue(ctx, value);
47281 goto fail_close;
47282 }
47283 JS_FreeValue(ctx, key);
47284 JS_FreeValue(ctx, value);
47285 }
47286 JS_FreeValue(ctx, ret);
47287 JS_FreeValue(ctx, item);
47288 }
47289 JS_FreeValue(ctx, next_method);
47290 JS_FreeValue(ctx, iter);
47291 JS_FreeValue(ctx, adder);
47292 }
47293 return obj;
47294 fail_close:
47295 /* close the iterator object, preserving pending exception */
47296 JS_IteratorClose(ctx, iter, TRUE);
47297 fail:
47298 JS_FreeValue(ctx, next_method);
47299 JS_FreeValue(ctx, iter);
47300 JS_FreeValue(ctx, adder);
47301 JS_FreeValue(ctx, obj);
47302 return JS_EXCEPTION;
47303}
47304
47305/* XXX: could normalize strings to speed up comparison */
47306static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
47307{
47308 uint32_t tag = JS_VALUE_GET_TAG(key);
47309 /* convert -0.0 to +0.0 */
47310 if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
47311 key = JS_NewInt32(ctx, 0);
47312 }
47313 return key;
47314}
47315
47316/* hash multipliers, same as the Linux kernel (see Knuth vol 3,
47317 section 6.4, exercise 9) */
47318#define HASH_MUL32 0x61C88647
47319#define HASH_MUL64 UINT64_C(0x61C8864680B583EB)
47320
47321static uint32_t map_hash32(uint32_t a, int hash_bits)
47322{
47323 return (a * HASH_MUL32) >> (32 - hash_bits);
47324}
47325
47326static uint32_t map_hash64(uint64_t a, int hash_bits)
47327{
47328 return (a * HASH_MUL64) >> (64 - hash_bits);
47329}
47330
47331static uint32_t map_hash_pointer(uintptr_t a, int hash_bits)
47332{
47333#ifdef JS_PTR64
47334 return map_hash64(a, hash_bits);
47335#else
47336 return map_hash32(a, hash_bits);
47337#endif
47338}
47339
47340/* XXX: better hash ? */
47341/* precondition: 1 <= hash_bits <= 32 */
47342static uint32_t map_hash_key(JSValueConst key, int hash_bits)
47343{
47344 uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
47345 uint32_t h;
47346 double d;
47347 JSBigInt *p;
47348 JSBigIntBuf buf;
47349
47350 switch(tag) {
47351 case JS_TAG_BOOL:
47352 h = map_hash32(JS_VALUE_GET_INT(key) ^ JS_TAG_BOOL, hash_bits);
47353 break;
47354 case JS_TAG_STRING:
47355 h = map_hash32(hash_string(JS_VALUE_GET_STRING(key), 0) ^ JS_TAG_STRING, hash_bits);
47356 break;
47357 case JS_TAG_STRING_ROPE:
47358 h = map_hash32(hash_string_rope(key, 0) ^ JS_TAG_STRING, hash_bits);
47359 break;
47360 case JS_TAG_OBJECT:
47361 case JS_TAG_SYMBOL:
47362 h = map_hash_pointer((uintptr_t)JS_VALUE_GET_PTR(key) ^ tag, hash_bits);
47363 break;
47364 case JS_TAG_INT:
47365 d = JS_VALUE_GET_INT(key);
47366 goto hash_float64;
47367 case JS_TAG_FLOAT64:
47368 d = JS_VALUE_GET_FLOAT64(key);
47369 /* normalize the NaN */
47370 if (isnan(d))
47371 d = JS_FLOAT64_NAN;
47372 hash_float64:
47373 h = map_hash64(float64_as_uint64(d) ^ JS_TAG_FLOAT64, hash_bits);
47374 break;
47375 case JS_TAG_SHORT_BIG_INT:
47376 p = js_bigint_set_short(&buf, key);
47377 goto hash_bigint;
47378 case JS_TAG_BIG_INT:
47379 p = JS_VALUE_GET_PTR(key);
47380 hash_bigint:
47381 {
47382 int i;
47383 h = 1;
47384 for(i = p->len - 1; i >= 0; i--) {
47385 h = h * 263 + p->tab[i];
47386 }
47387 /* the final step is necessary otherwise h mod n only
47388 depends of p->tab[i] mod n */
47389 h = map_hash32(h ^ JS_TAG_BIG_INT, hash_bits);
47390 }
47391 break;
47392 default:
47393 h = 0;
47394 break;
47395 }
47396 return h;
47397}
47398
47399static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
47400 JSValueConst key)
47401{
47402 JSMapRecord *mr;
47403 uint32_t h;
47404 h = map_hash_key(key, s->hash_bits);
47405 for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) {
47406 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
47407 /* cannot match */
47408 } else {
47409 if (js_same_value_zero(ctx, mr->key, key))
47410 return mr;
47411 }
47412 }
47413 return NULL;
47414}
47415
47416static void map_hash_resize(JSContext *ctx, JSMapState *s)
47417{
47418 uint32_t new_hash_size, h;
47419 int new_hash_bits;
47420 struct list_head *el;
47421 JSMapRecord *mr, **new_hash_table;
47422
47423 /* XXX: no reporting of memory allocation failure */
47424 new_hash_bits = min_int(s->hash_bits + 1, 31);
47425 new_hash_size = 1U << new_hash_bits;
47426 new_hash_table = js_realloc(ctx, s->hash_table,
47427 sizeof(new_hash_table[0]) * new_hash_size);
47428 if (!new_hash_table)
47429 return;
47430
47431 memset(new_hash_table, 0, sizeof(new_hash_table[0]) * new_hash_size);
47432
47433 list_for_each(el, &s->records) {
47434 mr = list_entry(el, JSMapRecord, link);
47435 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
47436 } else {
47437 h = map_hash_key(mr->key, new_hash_bits);
47438 mr->hash_next = new_hash_table[h];
47439 new_hash_table[h] = mr;
47440 }
47441 }
47442 s->hash_table = new_hash_table;
47443 s->hash_bits = new_hash_bits;
47444 s->hash_size = new_hash_size;
47445 s->record_count_threshold = new_hash_size * 2;
47446}
47447
47448static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
47449 JSValueConst key)
47450{
47451 uint32_t h;
47452 JSMapRecord *mr;
47453
47454 mr = js_malloc(ctx, sizeof(*mr));
47455 if (!mr)
47456 return NULL;
47457 mr->ref_count = 1;
47458 mr->empty = FALSE;
47459 if (s->is_weak) {
47460 mr->key = js_weakref_new(ctx, key);
47461 } else {
47462 mr->key = JS_DupValue(ctx, key);
47463 }
47464 h = map_hash_key(key, s->hash_bits);
47465 mr->hash_next = s->hash_table[h];
47466 s->hash_table[h] = mr;
47467 list_add_tail(&mr->link, &s->records);
47468 s->record_count++;
47469 if (s->record_count >= s->record_count_threshold) {
47470 map_hash_resize(ctx, s);
47471 }
47472 return mr;
47473}
47474
47475/* warning: the record must be removed from the hash table before */
47476static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
47477{
47478 if (mr->empty)
47479 return;
47480
47481 if (s->is_weak) {
47482 js_weakref_free(rt, mr->key);
47483 } else {
47484 JS_FreeValueRT(rt, mr->key);
47485 }
47486 JS_FreeValueRT(rt, mr->value);
47487 if (--mr->ref_count == 0) {
47488 list_del(&mr->link);
47489 js_free_rt(rt, mr);
47490 } else {
47491 /* keep a zombie record for iterators */
47492 mr->empty = TRUE;
47493 mr->key = JS_UNDEFINED;
47494 mr->value = JS_UNDEFINED;
47495 }
47496 s->record_count--;
47497}
47498
47499static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
47500{
47501 if (--mr->ref_count == 0) {
47502 /* the record can be safely removed */
47503 assert(mr->empty);
47504 list_del(&mr->link);
47505 js_free_rt(rt, mr);
47506 }
47507}
47508
47509static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh)
47510{
47511 JSMapState *s = container_of(wh, JSMapState, weakref_header);
47512 struct list_head *el, *el1;
47513 JSMapRecord *mr1, **pmr;
47514 uint32_t h;
47515
47516 list_for_each_safe(el, el1, &s->records) {
47517 JSMapRecord *mr = list_entry(el, JSMapRecord, link);
47518 if (!js_weakref_is_live(mr->key)) {
47519
47520 /* even if key is not live it can be hashed as a pointer */
47521 h = map_hash_key(mr->key, s->hash_bits);
47522 pmr = &s->hash_table[h];
47523 for(;;) {
47524 mr1 = *pmr;
47525 /* the entry may already be removed from the hash
47526 table if the map was resized */
47527 if (mr1 == NULL)
47528 goto done;
47529 if (mr1 == mr)
47530 break;
47531 pmr = &mr1->hash_next;
47532 }
47533 /* remove from the hash table */
47534 *pmr = mr1->hash_next;
47535 done:
47536 map_delete_record(rt, s, mr);
47537 }
47538 }
47539}
47540
47541static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
47542 int argc, JSValueConst *argv, int magic)
47543{
47544 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47545 JSMapRecord *mr;
47546 JSValueConst key, value;
47547
47548 if (!s)
47549 return JS_EXCEPTION;
47550 key = map_normalize_key(ctx, argv[0]);
47551 if (s->is_weak && !js_weakref_is_target(key))
47552 return JS_ThrowTypeError(ctx, "invalid value used as %s key", (magic & MAGIC_SET) ? "WeakSet" : "WeakMap");
47553 if (magic & MAGIC_SET)
47554 value = JS_UNDEFINED;
47555 else
47556 value = argv[1];
47557 mr = map_find_record(ctx, s, key);
47558 if (mr) {
47559 JS_FreeValue(ctx, mr->value);
47560 } else {
47561 mr = map_add_record(ctx, s, key);
47562 if (!mr)
47563 return JS_EXCEPTION;
47564 }
47565 mr->value = JS_DupValue(ctx, value);
47566 return JS_DupValue(ctx, this_val);
47567}
47568
47569static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
47570 int argc, JSValueConst *argv, int magic)
47571{
47572 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47573 JSMapRecord *mr;
47574 JSValueConst key;
47575
47576 if (!s)
47577 return JS_EXCEPTION;
47578 key = map_normalize_key(ctx, argv[0]);
47579 mr = map_find_record(ctx, s, key);
47580 if (!mr)
47581 return JS_UNDEFINED;
47582 else
47583 return JS_DupValue(ctx, mr->value);
47584}
47585
47586static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
47587 int argc, JSValueConst *argv, int magic)
47588{
47589 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47590 JSMapRecord *mr;
47591 JSValueConst key;
47592
47593 if (!s)
47594 return JS_EXCEPTION;
47595 key = map_normalize_key(ctx, argv[0]);
47596 mr = map_find_record(ctx, s, key);
47597 return JS_NewBool(ctx, mr != NULL);
47598}
47599
47600static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
47601 int argc, JSValueConst *argv, int magic)
47602{
47603 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47604 JSMapRecord *mr, **pmr;
47605 JSValueConst key;
47606 uint32_t h;
47607
47608 if (!s)
47609 return JS_EXCEPTION;
47610 key = map_normalize_key(ctx, argv[0]);
47611
47612 h = map_hash_key(key, s->hash_bits);
47613 pmr = &s->hash_table[h];
47614 for(;;) {
47615 mr = *pmr;
47616 if (mr == NULL)
47617 return JS_FALSE;
47618 if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) {
47619 /* not valid */
47620 } else {
47621 if (js_same_value_zero(ctx, mr->key, key))
47622 break;
47623 }
47624 pmr = &mr->hash_next;
47625 }
47626
47627 /* remove from the hash table */
47628 *pmr = mr->hash_next;
47629
47630 map_delete_record(ctx->rt, s, mr);
47631 return JS_TRUE;
47632}
47633
47634static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
47635 int argc, JSValueConst *argv, int magic)
47636{
47637 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47638 struct list_head *el, *el1;
47639 JSMapRecord *mr;
47640
47641 if (!s)
47642 return JS_EXCEPTION;
47643
47644 /* remove from the hash table */
47645 memset(s->hash_table, 0, sizeof(s->hash_table[0]) * s->hash_size);
47646
47647 list_for_each_safe(el, el1, &s->records) {
47648 mr = list_entry(el, JSMapRecord, link);
47649 map_delete_record(ctx->rt, s, mr);
47650 }
47651 return JS_UNDEFINED;
47652}
47653
47654static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
47655{
47656 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47657 if (!s)
47658 return JS_EXCEPTION;
47659 return JS_NewUint32(ctx, s->record_count);
47660}
47661
47662static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
47663 int argc, JSValueConst *argv, int magic)
47664{
47665 JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47666 JSValueConst func, this_arg;
47667 JSValue ret, args[3];
47668 struct list_head *el;
47669 JSMapRecord *mr;
47670
47671 if (!s)
47672 return JS_EXCEPTION;
47673 func = argv[0];
47674 if (argc > 1)
47675 this_arg = argv[1];
47676 else
47677 this_arg = JS_UNDEFINED;
47678 if (check_function(ctx, func))
47679 return JS_EXCEPTION;
47680 /* Note: the list can be modified while traversing it, but the
47681 current element is locked */
47682 el = s->records.next;
47683 while (el != &s->records) {
47684 mr = list_entry(el, JSMapRecord, link);
47685 if (!mr->empty) {
47686 mr->ref_count++;
47687 /* must duplicate in case the record is deleted */
47688 args[1] = JS_DupValue(ctx, mr->key);
47689 if (magic)
47690 args[0] = args[1];
47691 else
47692 args[0] = JS_DupValue(ctx, mr->value);
47693 args[2] = (JSValue)this_val;
47694 ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
47695 JS_FreeValue(ctx, args[0]);
47696 if (!magic)
47697 JS_FreeValue(ctx, args[1]);
47698 el = el->next;
47699 map_decref_record(ctx->rt, mr);
47700 if (JS_IsException(ret))
47701 return ret;
47702 JS_FreeValue(ctx, ret);
47703 } else {
47704 el = el->next;
47705 }
47706 }
47707 return JS_UNDEFINED;
47708}
47709
47710static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
47711 int argc, JSValueConst *argv, int is_map)
47712{
47713 JSValueConst cb, args[2];
47714 JSValue res, iter, next, groups, key, v, prop;
47715 JSAtom key_atom = JS_ATOM_NULL;
47716 int64_t idx;
47717 BOOL done;
47718
47719 // "is function?" check must be observed before argv[0] is accessed
47720 cb = argv[1];
47721 if (check_function(ctx, cb))
47722 return JS_EXCEPTION;
47723
47724 iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE);
47725 if (JS_IsException(iter))
47726 return JS_EXCEPTION;
47727
47728 key = JS_UNDEFINED;
47729 key_atom = JS_ATOM_NULL;
47730 v = JS_UNDEFINED;
47731 prop = JS_UNDEFINED;
47732 groups = JS_UNDEFINED;
47733
47734 next = JS_GetProperty(ctx, iter, JS_ATOM_next);
47735 if (JS_IsException(next))
47736 goto exception;
47737
47738 if (is_map) {
47739 groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0);
47740 } else {
47741 groups = JS_NewObjectProto(ctx, JS_NULL);
47742 }
47743 if (JS_IsException(groups))
47744 goto exception;
47745
47746 for (idx = 0; ; idx++) {
47747 if (idx >= MAX_SAFE_INTEGER) {
47748 JS_ThrowTypeError(ctx, "too many elements");
47749 goto iterator_close_exception;
47750 }
47751 v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done);
47752 if (JS_IsException(v))
47753 goto exception;
47754 if (done)
47755 break; // v is JS_UNDEFINED
47756
47757 args[0] = v;
47758 args[1] = JS_NewInt64(ctx, idx);
47759 key = JS_Call(ctx, cb, ctx->global_obj, 2, args);
47760 if (JS_IsException(key))
47761 goto iterator_close_exception;
47762
47763 if (is_map) {
47764 prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0);
47765 } else {
47766 key_atom = JS_ValueToAtom(ctx, key);
47767 JS_FreeValue(ctx, key);
47768 key = JS_UNDEFINED;
47769 if (key_atom == JS_ATOM_NULL)
47770 goto iterator_close_exception;
47771 prop = JS_GetProperty(ctx, groups, key_atom);
47772 }
47773 if (JS_IsException(prop))
47774 goto exception;
47775
47776 if (JS_IsUndefined(prop)) {
47777 prop = JS_NewArray(ctx);
47778 if (JS_IsException(prop))
47779 goto exception;
47780 if (is_map) {
47781 args[0] = key;
47782 args[1] = prop;
47783 res = js_map_set(ctx, groups, 2, args, 0);
47784 if (JS_IsException(res))
47785 goto exception;
47786 JS_FreeValue(ctx, res);
47787 } else {
47788 prop = JS_DupValue(ctx, prop);
47789 if (JS_DefinePropertyValue(ctx, groups, key_atom, prop,
47790 JS_PROP_C_W_E) < 0) {
47791 goto exception;
47792 }
47793 }
47794 }
47795 res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0);
47796 if (JS_IsException(res))
47797 goto exception;
47798 // res is an int64
47799
47800 JS_FreeValue(ctx, prop);
47801 JS_FreeValue(ctx, key);
47802 JS_FreeAtom(ctx, key_atom);
47803 JS_FreeValue(ctx, v);
47804 prop = JS_UNDEFINED;
47805 key = JS_UNDEFINED;
47806 key_atom = JS_ATOM_NULL;
47807 v = JS_UNDEFINED;
47808 }
47809
47810 JS_FreeValue(ctx, iter);
47811 JS_FreeValue(ctx, next);
47812 return groups;
47813
47814 iterator_close_exception:
47815 JS_IteratorClose(ctx, iter, TRUE);
47816 exception:
47817 JS_FreeAtom(ctx, key_atom);
47818 JS_FreeValue(ctx, prop);
47819 JS_FreeValue(ctx, key);
47820 JS_FreeValue(ctx, v);
47821 JS_FreeValue(ctx, groups);
47822 JS_FreeValue(ctx, iter);
47823 JS_FreeValue(ctx, next);
47824 return JS_EXCEPTION;
47825}
47826
47827static void js_map_finalizer(JSRuntime *rt, JSValue val)
47828{
47829 JSObject *p;
47830 JSMapState *s;
47831 struct list_head *el, *el1;
47832 JSMapRecord *mr;
47833
47834 p = JS_VALUE_GET_OBJ(val);
47835 s = p->u.map_state;
47836 if (s) {
47837 /* if the object is deleted we are sure that no iterator is
47838 using it */
47839 list_for_each_safe(el, el1, &s->records) {
47840 mr = list_entry(el, JSMapRecord, link);
47841 if (!mr->empty) {
47842 if (s->is_weak)
47843 js_weakref_free(rt, mr->key);
47844 else
47845 JS_FreeValueRT(rt, mr->key);
47846 JS_FreeValueRT(rt, mr->value);
47847 }
47848 js_free_rt(rt, mr);
47849 }
47850 js_free_rt(rt, s->hash_table);
47851 if (s->is_weak) {
47852 list_del(&s->weakref_header.link);
47853 }
47854 js_free_rt(rt, s);
47855 }
47856}
47857
47858static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
47859{
47860 JSObject *p = JS_VALUE_GET_OBJ(val);
47861 JSMapState *s;
47862 struct list_head *el;
47863 JSMapRecord *mr;
47864
47865 s = p->u.map_state;
47866 if (s) {
47867 list_for_each(el, &s->records) {
47868 mr = list_entry(el, JSMapRecord, link);
47869 if (!s->is_weak)
47870 JS_MarkValue(rt, mr->key, mark_func);
47871 JS_MarkValue(rt, mr->value, mark_func);
47872 }
47873 }
47874}
47875
47876/* Map Iterator */
47877
47878typedef struct JSMapIteratorData {
47879 JSValue obj;
47880 JSIteratorKindEnum kind;
47881 JSMapRecord *cur_record;
47882} JSMapIteratorData;
47883
47884static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
47885{
47886 JSObject *p;
47887 JSMapIteratorData *it;
47888
47889 p = JS_VALUE_GET_OBJ(val);
47890 it = p->u.map_iterator_data;
47891 if (it) {
47892 /* During the GC sweep phase the Map finalizer may be
47893 called before the Map iterator finalizer */
47894 if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
47895 map_decref_record(rt, it->cur_record);
47896 }
47897 JS_FreeValueRT(rt, it->obj);
47898 js_free_rt(rt, it);
47899 }
47900}
47901
47902static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
47903 JS_MarkFunc *mark_func)
47904{
47905 JSObject *p = JS_VALUE_GET_OBJ(val);
47906 JSMapIteratorData *it;
47907 it = p->u.map_iterator_data;
47908 if (it) {
47909 /* the record is already marked by the object */
47910 JS_MarkValue(rt, it->obj, mark_func);
47911 }
47912}
47913
47914static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
47915 int argc, JSValueConst *argv, int magic)
47916{
47917 JSIteratorKindEnum kind;
47918 JSMapState *s;
47919 JSMapIteratorData *it;
47920 JSValue enum_obj;
47921
47922 kind = magic >> 2;
47923 magic &= 3;
47924 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
47925 if (!s)
47926 return JS_EXCEPTION;
47927 enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
47928 if (JS_IsException(enum_obj))
47929 goto fail;
47930 it = js_malloc(ctx, sizeof(*it));
47931 if (!it) {
47932 JS_FreeValue(ctx, enum_obj);
47933 goto fail;
47934 }
47935 it->obj = JS_DupValue(ctx, this_val);
47936 it->kind = kind;
47937 it->cur_record = NULL;
47938 JS_SetOpaque(enum_obj, it);
47939 return enum_obj;
47940 fail:
47941 return JS_EXCEPTION;
47942}
47943
47944static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
47945 int argc, JSValueConst *argv,
47946 BOOL *pdone, int magic)
47947{
47948 JSMapIteratorData *it;
47949 JSMapState *s;
47950 JSMapRecord *mr;
47951 struct list_head *el;
47952
47953 it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
47954 if (!it) {
47955 *pdone = FALSE;
47956 return JS_EXCEPTION;
47957 }
47958 if (JS_IsUndefined(it->obj))
47959 goto done;
47960 s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
47961 assert(s != NULL);
47962 if (!it->cur_record) {
47963 el = s->records.next;
47964 } else {
47965 mr = it->cur_record;
47966 el = mr->link.next;
47967 map_decref_record(ctx->rt, mr); /* the record can be freed here */
47968 }
47969 for(;;) {
47970 if (el == &s->records) {
47971 /* no more record */
47972 it->cur_record = NULL;
47973 JS_FreeValue(ctx, it->obj);
47974 it->obj = JS_UNDEFINED;
47975 done:
47976 /* end of enumeration */
47977 *pdone = TRUE;
47978 return JS_UNDEFINED;
47979 }
47980 mr = list_entry(el, JSMapRecord, link);
47981 if (!mr->empty)
47982 break;
47983 /* get the next record */
47984 el = mr->link.next;
47985 }
47986
47987 /* lock the record so that it won't be freed */
47988 mr->ref_count++;
47989 it->cur_record = mr;
47990 *pdone = FALSE;
47991
47992 if (it->kind == JS_ITERATOR_KIND_KEY) {
47993 return JS_DupValue(ctx, mr->key);
47994 } else {
47995 JSValueConst args[2];
47996 args[0] = mr->key;
47997 if (magic)
47998 args[1] = mr->key;
47999 else
48000 args[1] = mr->value;
48001 if (it->kind == JS_ITERATOR_KIND_VALUE) {
48002 return JS_DupValue(ctx, args[1]);
48003 } else {
48004 return js_create_array(ctx, 2, args);
48005 }
48006 }
48007}
48008
48009static const JSCFunctionListEntry js_map_funcs[] = {
48010 JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ),
48011 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
48012};
48013
48014static const JSCFunctionListEntry js_map_proto_funcs[] = {
48015 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
48016 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
48017 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
48018 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
48019 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
48020 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
48021 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
48022 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
48023 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
48024 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
48025 JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
48026 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
48027};
48028
48029static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
48030 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
48031 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
48032};
48033
48034static const JSCFunctionListEntry js_set_proto_funcs[] = {
48035 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
48036 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
48037 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
48038 JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
48039 JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
48040 JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
48041 JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
48042 JS_ALIAS_DEF("keys", "values" ),
48043 JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
48044 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
48045 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
48046};
48047
48048static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
48049 JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
48050 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
48051};
48052
48053static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
48054 JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
48055 JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
48056 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
48057 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
48058 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
48059};
48060
48061static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
48062 JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
48063 JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
48064 JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
48065 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
48066};
48067
48068static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
48069 js_map_proto_funcs,
48070 js_set_proto_funcs,
48071 js_weak_map_proto_funcs,
48072 js_weak_set_proto_funcs,
48073 js_map_iterator_proto_funcs,
48074 js_set_iterator_proto_funcs,
48075};
48076
48077static const uint8_t js_map_proto_funcs_count[6] = {
48078 countof(js_map_proto_funcs),
48079 countof(js_set_proto_funcs),
48080 countof(js_weak_map_proto_funcs),
48081 countof(js_weak_set_proto_funcs),
48082 countof(js_map_iterator_proto_funcs),
48083 countof(js_set_iterator_proto_funcs),
48084};
48085
48086void JS_AddIntrinsicMapSet(JSContext *ctx)
48087{
48088 int i;
48089 JSValue obj1;
48090 char buf[ATOM_GET_STR_BUF_SIZE];
48091
48092 for(i = 0; i < 4; i++) {
48093 const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
48094 JS_ATOM_Map + i);
48095 ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
48096 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
48097 js_map_proto_funcs_ptr[i],
48098 js_map_proto_funcs_count[i]);
48099 obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
48100 JS_CFUNC_constructor_magic, i);
48101 if (i < 2) {
48102 JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
48103 countof(js_map_funcs));
48104 }
48105 JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
48106 }
48107
48108 for(i = 0; i < 2; i++) {
48109 ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
48110 JS_NewObjectProto(ctx, ctx->iterator_proto);
48111 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
48112 js_map_proto_funcs_ptr[i + 4],
48113 js_map_proto_funcs_count[i + 4]);
48114 }
48115}
48116
48117/* Generator */
48118static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
48119 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
48120};
48121
48122static const JSCFunctionListEntry js_generator_proto_funcs[] = {
48123 JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
48124 JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
48125 JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
48126 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
48127};
48128
48129/* Promise */
48130
48131typedef struct JSPromiseData {
48132 JSPromiseStateEnum promise_state;
48133 /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
48134 struct list_head promise_reactions[2];
48135 BOOL is_handled; /* Note: only useful to debug */
48136 JSValue promise_result;
48137} JSPromiseData;
48138
48139typedef struct JSPromiseFunctionDataResolved {
48140 int ref_count;
48141 BOOL already_resolved;
48142} JSPromiseFunctionDataResolved;
48143
48144typedef struct JSPromiseFunctionData {
48145 JSValue promise;
48146 JSPromiseFunctionDataResolved *presolved;
48147} JSPromiseFunctionData;
48148
48149typedef struct JSPromiseReactionData {
48150 struct list_head link; /* not used in promise_reaction_job */
48151 JSValue resolving_funcs[2];
48152 JSValue handler;
48153} JSPromiseReactionData;
48154
48155JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise)
48156{
48157 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
48158 if (!s)
48159 return -1;
48160 return s->promise_state;
48161}
48162
48163JSValue JS_PromiseResult(JSContext *ctx, JSValue promise)
48164{
48165 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
48166 if (!s)
48167 return JS_UNDEFINED;
48168 return JS_DupValue(ctx, s->promise_result);
48169}
48170
48171static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
48172 JSValueConst promise);
48173
48174static void promise_reaction_data_free(JSRuntime *rt,
48175 JSPromiseReactionData *rd)
48176{
48177 JS_FreeValueRT(rt, rd->resolving_funcs[0]);
48178 JS_FreeValueRT(rt, rd->resolving_funcs[1]);
48179 JS_FreeValueRT(rt, rd->handler);
48180 js_free_rt(rt, rd);
48181}
48182
48183static JSValue promise_reaction_job(JSContext *ctx, int argc,
48184 JSValueConst *argv)
48185{
48186 JSValueConst handler, arg, func;
48187 JSValue res, res2;
48188 BOOL is_reject;
48189
48190 assert(argc == 5);
48191 handler = argv[2];
48192 is_reject = JS_ToBool(ctx, argv[3]);
48193 arg = argv[4];
48194#ifdef DUMP_PROMISE
48195 printf("promise_reaction_job: is_reject=%d\n", is_reject);
48196#endif
48197
48198 if (JS_IsUndefined(handler)) {
48199 if (is_reject) {
48200 res = JS_Throw(ctx, JS_DupValue(ctx, arg));
48201 } else {
48202 res = JS_DupValue(ctx, arg);
48203 }
48204 } else {
48205 res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
48206 }
48207 is_reject = JS_IsException(res);
48208 if (is_reject)
48209 res = JS_GetException(ctx);
48210 func = argv[is_reject];
48211 /* as an extension, we support undefined as value to avoid
48212 creating a dummy promise in the 'await' implementation of async
48213 functions */
48214 if (!JS_IsUndefined(func)) {
48215 res2 = JS_Call(ctx, func, JS_UNDEFINED,
48216 1, (JSValueConst *)&res);
48217 } else {
48218 res2 = JS_UNDEFINED;
48219 }
48220 JS_FreeValue(ctx, res);
48221
48222 return res2;
48223}
48224
48225void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
48226 JSHostPromiseRejectionTracker *cb,
48227 void *opaque)
48228{
48229 rt->host_promise_rejection_tracker = cb;
48230 rt->host_promise_rejection_tracker_opaque = opaque;
48231}
48232
48233static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
48234 JSValueConst value, BOOL is_reject)
48235{
48236 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
48237 struct list_head *el, *el1;
48238 JSPromiseReactionData *rd;
48239 JSValueConst args[5];
48240
48241 if (!s || s->promise_state != JS_PROMISE_PENDING)
48242 return; /* should never happen */
48243 set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
48244 s->promise_state = JS_PROMISE_FULFILLED + is_reject;
48245#ifdef DUMP_PROMISE
48246 printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
48247#endif
48248 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
48249 JSRuntime *rt = ctx->rt;
48250 if (rt->host_promise_rejection_tracker) {
48251 rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
48252 rt->host_promise_rejection_tracker_opaque);
48253 }
48254 }
48255
48256 list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
48257 rd = list_entry(el, JSPromiseReactionData, link);
48258 args[0] = rd->resolving_funcs[0];
48259 args[1] = rd->resolving_funcs[1];
48260 args[2] = rd->handler;
48261 args[3] = JS_NewBool(ctx, is_reject);
48262 args[4] = value;
48263 JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
48264 list_del(&rd->link);
48265 promise_reaction_data_free(ctx->rt, rd);
48266 }
48267
48268 list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
48269 rd = list_entry(el, JSPromiseReactionData, link);
48270 list_del(&rd->link);
48271 promise_reaction_data_free(ctx->rt, rd);
48272 }
48273}
48274
48275static void reject_promise(JSContext *ctx, JSValueConst promise,
48276 JSValueConst value)
48277{
48278 fulfill_or_reject_promise(ctx, promise, value, TRUE);
48279}
48280
48281static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
48282 int argc, JSValueConst *argv)
48283{
48284 JSValueConst promise, thenable, then;
48285 JSValue args[2], res;
48286
48287#ifdef DUMP_PROMISE
48288 printf("js_promise_resolve_thenable_job\n");
48289#endif
48290 assert(argc == 3);
48291 promise = argv[0];
48292 thenable = argv[1];
48293 then = argv[2];
48294 if (js_create_resolving_functions(ctx, args, promise) < 0)
48295 return JS_EXCEPTION;
48296 res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
48297 if (JS_IsException(res)) {
48298 JSValue error = JS_GetException(ctx);
48299 res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
48300 JS_FreeValue(ctx, error);
48301 }
48302 JS_FreeValue(ctx, args[0]);
48303 JS_FreeValue(ctx, args[1]);
48304 return res;
48305}
48306
48307static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
48308 JSPromiseFunctionDataResolved *sr)
48309{
48310 if (--sr->ref_count == 0) {
48311 js_free_rt(rt, sr);
48312 }
48313}
48314
48315static int js_create_resolving_functions(JSContext *ctx,
48316 JSValue *resolving_funcs,
48317 JSValueConst promise)
48318
48319{
48320 JSValue obj;
48321 JSPromiseFunctionData *s;
48322 JSPromiseFunctionDataResolved *sr;
48323 int i, ret;
48324
48325 sr = js_malloc(ctx, sizeof(*sr));
48326 if (!sr)
48327 return -1;
48328 sr->ref_count = 1;
48329 sr->already_resolved = FALSE; /* must be shared between the two functions */
48330 ret = 0;
48331 for(i = 0; i < 2; i++) {
48332 obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
48333 JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
48334 if (JS_IsException(obj))
48335 goto fail;
48336 s = js_malloc(ctx, sizeof(*s));
48337 if (!s) {
48338 JS_FreeValue(ctx, obj);
48339 fail:
48340
48341 if (i != 0)
48342 JS_FreeValue(ctx, resolving_funcs[0]);
48343 ret = -1;
48344 break;
48345 }
48346 sr->ref_count++;
48347 s->presolved = sr;
48348 s->promise = JS_DupValue(ctx, promise);
48349 JS_SetOpaque(obj, s);
48350 js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
48351 resolving_funcs[i] = obj;
48352 }
48353 js_promise_resolve_function_free_resolved(ctx->rt, sr);
48354 return ret;
48355}
48356
48357static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
48358{
48359 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
48360 if (s) {
48361 js_promise_resolve_function_free_resolved(rt, s->presolved);
48362 JS_FreeValueRT(rt, s->promise);
48363 js_free_rt(rt, s);
48364 }
48365}
48366
48367static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
48368 JS_MarkFunc *mark_func)
48369{
48370 JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
48371 if (s) {
48372 JS_MarkValue(rt, s->promise, mark_func);
48373 }
48374}
48375
48376static JSValue js_promise_resolve_function_call(JSContext *ctx,
48377 JSValueConst func_obj,
48378 JSValueConst this_val,
48379 int argc, JSValueConst *argv,
48380 int flags)
48381{
48382 JSObject *p = JS_VALUE_GET_OBJ(func_obj);
48383 JSPromiseFunctionData *s;
48384 JSValueConst resolution, args[3];
48385 JSValue then;
48386 BOOL is_reject;
48387
48388 s = p->u.promise_function_data;
48389 if (!s || s->presolved->already_resolved)
48390 return JS_UNDEFINED;
48391 s->presolved->already_resolved = TRUE;
48392 is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
48393 if (argc > 0)
48394 resolution = argv[0];
48395 else
48396 resolution = JS_UNDEFINED;
48397#ifdef DUMP_PROMISE
48398 printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
48399 JS_DumpValue(ctx, resolution);
48400 printf("\n");
48401#endif
48402 if (is_reject || !JS_IsObject(resolution)) {
48403 goto done;
48404 } else if (js_same_value(ctx, resolution, s->promise)) {
48405 JS_ThrowTypeError(ctx, "promise self resolution");
48406 goto fail_reject;
48407 }
48408 then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
48409 if (JS_IsException(then)) {
48410 JSValue error;
48411 fail_reject:
48412 error = JS_GetException(ctx);
48413 reject_promise(ctx, s->promise, error);
48414 JS_FreeValue(ctx, error);
48415 } else if (!JS_IsFunction(ctx, then)) {
48416 JS_FreeValue(ctx, then);
48417 done:
48418 fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
48419 } else {
48420 args[0] = s->promise;
48421 args[1] = resolution;
48422 args[2] = then;
48423 JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
48424 JS_FreeValue(ctx, then);
48425 }
48426 return JS_UNDEFINED;
48427}
48428
48429static void js_promise_finalizer(JSRuntime *rt, JSValue val)
48430{
48431 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
48432 struct list_head *el, *el1;
48433 int i;
48434
48435 if (!s)
48436 return;
48437 for(i = 0; i < 2; i++) {
48438 list_for_each_safe(el, el1, &s->promise_reactions[i]) {
48439 JSPromiseReactionData *rd =
48440 list_entry(el, JSPromiseReactionData, link);
48441 promise_reaction_data_free(rt, rd);
48442 }
48443 }
48444 JS_FreeValueRT(rt, s->promise_result);
48445 js_free_rt(rt, s);
48446}
48447
48448static void js_promise_mark(JSRuntime *rt, JSValueConst val,
48449 JS_MarkFunc *mark_func)
48450{
48451 JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
48452 struct list_head *el;
48453 int i;
48454
48455 if (!s)
48456 return;
48457 for(i = 0; i < 2; i++) {
48458 list_for_each(el, &s->promise_reactions[i]) {
48459 JSPromiseReactionData *rd =
48460 list_entry(el, JSPromiseReactionData, link);
48461 JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
48462 JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
48463 JS_MarkValue(rt, rd->handler, mark_func);
48464 }
48465 }
48466 JS_MarkValue(rt, s->promise_result, mark_func);
48467}
48468
48469static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
48470 int argc, JSValueConst *argv)
48471{
48472 JSValueConst executor;
48473 JSValue obj;
48474 JSPromiseData *s;
48475 JSValue args[2], ret;
48476 int i;
48477
48478 executor = argv[0];
48479 if (check_function(ctx, executor))
48480 return JS_EXCEPTION;
48481 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
48482 if (JS_IsException(obj))
48483 return JS_EXCEPTION;
48484 s = js_mallocz(ctx, sizeof(*s));
48485 if (!s)
48486 goto fail;
48487 s->promise_state = JS_PROMISE_PENDING;
48488 s->is_handled = FALSE;
48489 for(i = 0; i < 2; i++)
48490 init_list_head(&s->promise_reactions[i]);
48491 s->promise_result = JS_UNDEFINED;
48492 JS_SetOpaque(obj, s);
48493 if (js_create_resolving_functions(ctx, args, obj))
48494 goto fail;
48495 ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
48496 if (JS_IsException(ret)) {
48497 JSValue ret2, error;
48498 error = JS_GetException(ctx);
48499 ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
48500 JS_FreeValue(ctx, error);
48501 if (JS_IsException(ret2))
48502 goto fail1;
48503 JS_FreeValue(ctx, ret2);
48504 }
48505 JS_FreeValue(ctx, ret);
48506 JS_FreeValue(ctx, args[0]);
48507 JS_FreeValue(ctx, args[1]);
48508 return obj;
48509 fail1:
48510 JS_FreeValue(ctx, args[0]);
48511 JS_FreeValue(ctx, args[1]);
48512 fail:
48513 JS_FreeValue(ctx, obj);
48514 return JS_EXCEPTION;
48515}
48516
48517static JSValue js_promise_executor(JSContext *ctx,
48518 JSValueConst this_val,
48519 int argc, JSValueConst *argv,
48520 int magic, JSValue *func_data)
48521{
48522 int i;
48523
48524 for(i = 0; i < 2; i++) {
48525 if (!JS_IsUndefined(func_data[i]))
48526 return JS_ThrowTypeError(ctx, "resolving function already set");
48527 func_data[i] = JS_DupValue(ctx, argv[i]);
48528 }
48529 return JS_UNDEFINED;
48530}
48531
48532static JSValue js_promise_executor_new(JSContext *ctx)
48533{
48534 JSValueConst func_data[2];
48535
48536 func_data[0] = JS_UNDEFINED;
48537 func_data[1] = JS_UNDEFINED;
48538 return JS_NewCFunctionData(ctx, js_promise_executor, 2,
48539 0, 2, func_data);
48540}
48541
48542static JSValue js_new_promise_capability(JSContext *ctx,
48543 JSValue *resolving_funcs,
48544 JSValueConst ctor)
48545{
48546 JSValue executor, result_promise;
48547 JSCFunctionDataRecord *s;
48548 int i;
48549
48550 executor = js_promise_executor_new(ctx);
48551 if (JS_IsException(executor))
48552 return executor;
48553
48554 if (JS_IsUndefined(ctor)) {
48555 result_promise = js_promise_constructor(ctx, ctor, 1,
48556 (JSValueConst *)&executor);
48557 } else {
48558 result_promise = JS_CallConstructor(ctx, ctor, 1,
48559 (JSValueConst *)&executor);
48560 }
48561 if (JS_IsException(result_promise))
48562 goto fail;
48563 s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
48564 for(i = 0; i < 2; i++) {
48565 if (check_function(ctx, s->data[i]))
48566 goto fail;
48567 }
48568 for(i = 0; i < 2; i++)
48569 resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
48570 JS_FreeValue(ctx, executor);
48571 return result_promise;
48572 fail:
48573 JS_FreeValue(ctx, executor);
48574 JS_FreeValue(ctx, result_promise);
48575 return JS_EXCEPTION;
48576}
48577
48578JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
48579{
48580 return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
48581}
48582
48583static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
48584 int argc, JSValueConst *argv, int magic)
48585{
48586 JSValue result_promise, resolving_funcs[2], ret;
48587 BOOL is_reject = magic;
48588
48589 if (!JS_IsObject(this_val))
48590 return JS_ThrowTypeErrorNotAnObject(ctx);
48591 if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
48592 JSValue ctor;
48593 BOOL is_same;
48594 ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
48595 if (JS_IsException(ctor))
48596 return ctor;
48597 is_same = js_same_value(ctx, ctor, this_val);
48598 JS_FreeValue(ctx, ctor);
48599 if (is_same)
48600 return JS_DupValue(ctx, argv[0]);
48601 }
48602 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
48603 if (JS_IsException(result_promise))
48604 return result_promise;
48605 ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
48606 JS_FreeValue(ctx, resolving_funcs[0]);
48607 JS_FreeValue(ctx, resolving_funcs[1]);
48608 if (JS_IsException(ret)) {
48609 JS_FreeValue(ctx, result_promise);
48610 return ret;
48611 }
48612 JS_FreeValue(ctx, ret);
48613 return result_promise;
48614}
48615
48616static JSValue js_promise_withResolvers(JSContext *ctx,
48617 JSValueConst this_val,
48618 int argc, JSValueConst *argv)
48619{
48620 JSValue result_promise, resolving_funcs[2], obj;
48621 if (!JS_IsObject(this_val))
48622 return JS_ThrowTypeErrorNotAnObject(ctx);
48623 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
48624 if (JS_IsException(result_promise))
48625 return result_promise;
48626 obj = JS_NewObject(ctx);
48627 if (JS_IsException(obj)) {
48628 JS_FreeValue(ctx, resolving_funcs[0]);
48629 JS_FreeValue(ctx, resolving_funcs[1]);
48630 JS_FreeValue(ctx, result_promise);
48631 return JS_EXCEPTION;
48632 }
48633 JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
48634 JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
48635 JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
48636 return obj;
48637}
48638
48639static __exception int remainingElementsCount_add(JSContext *ctx,
48640 JSValueConst resolve_element_env,
48641 int addend)
48642{
48643 JSValue val;
48644 int remainingElementsCount;
48645
48646 val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
48647 if (JS_IsException(val))
48648 return -1;
48649 if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
48650 return -1;
48651 remainingElementsCount += addend;
48652 if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
48653 JS_NewInt32(ctx, remainingElementsCount)) < 0)
48654 return -1;
48655 return (remainingElementsCount == 0);
48656}
48657
48658#define PROMISE_MAGIC_all 0
48659#define PROMISE_MAGIC_allSettled 1
48660#define PROMISE_MAGIC_any 2
48661
48662static JSValue js_promise_all_resolve_element(JSContext *ctx,
48663 JSValueConst this_val,
48664 int argc, JSValueConst *argv,
48665 int magic,
48666 JSValue *func_data)
48667{
48668 int resolve_type = magic & 3;
48669 int is_reject = magic & 4;
48670 BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
48671 JSValueConst values = func_data[2];
48672 JSValueConst resolve = func_data[3];
48673 JSValueConst resolve_element_env = func_data[4];
48674 JSValue ret, obj;
48675 int is_zero, index;
48676
48677 if (JS_ToInt32(ctx, &index, func_data[1]))
48678 return JS_EXCEPTION;
48679 if (alreadyCalled)
48680 return JS_UNDEFINED;
48681 func_data[0] = JS_NewBool(ctx, TRUE);
48682
48683 if (resolve_type == PROMISE_MAGIC_allSettled) {
48684 JSValue str;
48685
48686 obj = JS_NewObject(ctx);
48687 if (JS_IsException(obj))
48688 return JS_EXCEPTION;
48689 str = js_new_string8(ctx, is_reject ? "rejected" : "fulfilled");
48690 if (JS_IsException(str))
48691 goto fail1;
48692 if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
48693 str,
48694 JS_PROP_C_W_E) < 0)
48695 goto fail1;
48696 if (JS_DefinePropertyValue(ctx, obj,
48697 is_reject ? JS_ATOM_reason : JS_ATOM_value,
48698 JS_DupValue(ctx, argv[0]),
48699 JS_PROP_C_W_E) < 0) {
48700 fail1:
48701 JS_FreeValue(ctx, obj);
48702 return JS_EXCEPTION;
48703 }
48704 } else {
48705 obj = JS_DupValue(ctx, argv[0]);
48706 }
48707 if (JS_DefinePropertyValueUint32(ctx, values, index,
48708 obj, JS_PROP_C_W_E) < 0)
48709 return JS_EXCEPTION;
48710
48711 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
48712 if (is_zero < 0)
48713 return JS_EXCEPTION;
48714 if (is_zero) {
48715 if (resolve_type == PROMISE_MAGIC_any) {
48716 JSValue error;
48717 error = js_aggregate_error_constructor(ctx, values);
48718 if (JS_IsException(error))
48719 return JS_EXCEPTION;
48720 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
48721 JS_FreeValue(ctx, error);
48722 } else {
48723 ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
48724 }
48725 if (JS_IsException(ret))
48726 return ret;
48727 JS_FreeValue(ctx, ret);
48728 }
48729 return JS_UNDEFINED;
48730}
48731
48732/* magic = 0: Promise.all 1: Promise.allSettled */
48733static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
48734 int argc, JSValueConst *argv, int magic)
48735{
48736 JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
48737 JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
48738 JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
48739 JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
48740 JSValueConst then_args[2], resolve_element_data[5];
48741 BOOL done;
48742 int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
48743
48744 if (!JS_IsObject(this_val))
48745 return JS_ThrowTypeErrorNotAnObject(ctx);
48746 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
48747 if (JS_IsException(result_promise))
48748 return result_promise;
48749 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
48750 if (JS_IsException(promise_resolve) ||
48751 check_function(ctx, promise_resolve))
48752 goto fail_reject;
48753 iter = JS_GetIterator(ctx, argv[0], FALSE);
48754 if (JS_IsException(iter)) {
48755 JSValue error;
48756 fail_reject:
48757 error = JS_GetException(ctx);
48758 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
48759 (JSValueConst *)&error);
48760 JS_FreeValue(ctx, error);
48761 if (JS_IsException(ret))
48762 goto fail;
48763 JS_FreeValue(ctx, ret);
48764 } else {
48765 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
48766 if (JS_IsException(next_method))
48767 goto fail_reject;
48768 values = JS_NewArray(ctx);
48769 if (JS_IsException(values))
48770 goto fail_reject;
48771 resolve_element_env = JS_NewArray(ctx);
48772 if (JS_IsException(resolve_element_env))
48773 goto fail_reject;
48774 /* remainingElementsCount field */
48775 if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
48776 JS_NewInt32(ctx, 1),
48777 JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
48778 goto fail_reject;
48779
48780 index = 0;
48781 for(;;) {
48782 /* XXX: conformance: should close the iterator if error on 'done'
48783 access, but not on 'value' access */
48784 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
48785 if (JS_IsException(item))
48786 goto fail_reject;
48787 if (done)
48788 break;
48789 next_promise = JS_Call(ctx, promise_resolve,
48790 this_val, 1, (JSValueConst *)&item);
48791 JS_FreeValue(ctx, item);
48792 if (JS_IsException(next_promise)) {
48793 fail_reject1:
48794 JS_IteratorClose(ctx, iter, TRUE);
48795 goto fail_reject;
48796 }
48797 resolve_element_data[0] = JS_NewBool(ctx, FALSE);
48798 resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
48799 resolve_element_data[2] = values;
48800 resolve_element_data[3] = resolving_funcs[is_promise_any];
48801 resolve_element_data[4] = resolve_element_env;
48802 resolve_element =
48803 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
48804 magic, 5, resolve_element_data);
48805 if (JS_IsException(resolve_element)) {
48806 JS_FreeValue(ctx, next_promise);
48807 goto fail_reject1;
48808 }
48809
48810 if (magic == PROMISE_MAGIC_allSettled) {
48811 reject_element =
48812 JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
48813 magic | 4, 5, resolve_element_data);
48814 if (JS_IsException(reject_element)) {
48815 JS_FreeValue(ctx, next_promise);
48816 goto fail_reject1;
48817 }
48818 } else if (magic == PROMISE_MAGIC_any) {
48819 if (JS_DefinePropertyValueUint32(ctx, values, index,
48820 JS_UNDEFINED, JS_PROP_C_W_E) < 0)
48821 goto fail_reject1;
48822 reject_element = resolve_element;
48823 resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
48824 } else {
48825 reject_element = JS_DupValue(ctx, resolving_funcs[1]);
48826 }
48827
48828 if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
48829 JS_FreeValue(ctx, next_promise);
48830 JS_FreeValue(ctx, resolve_element);
48831 JS_FreeValue(ctx, reject_element);
48832 goto fail_reject1;
48833 }
48834
48835 then_args[0] = resolve_element;
48836 then_args[1] = reject_element;
48837 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
48838 JS_FreeValue(ctx, resolve_element);
48839 JS_FreeValue(ctx, reject_element);
48840 if (check_exception_free(ctx, ret))
48841 goto fail_reject1;
48842 index++;
48843 }
48844
48845 is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
48846 if (is_zero < 0)
48847 goto fail_reject;
48848 if (is_zero) {
48849 if (magic == PROMISE_MAGIC_any) {
48850 JSValue error;
48851 error = js_aggregate_error_constructor(ctx, values);
48852 if (JS_IsException(error))
48853 goto fail_reject;
48854 JS_FreeValue(ctx, values);
48855 values = error;
48856 }
48857 ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
48858 1, (JSValueConst *)&values);
48859 if (check_exception_free(ctx, ret))
48860 goto fail_reject;
48861 }
48862 }
48863 done:
48864 JS_FreeValue(ctx, promise_resolve);
48865 JS_FreeValue(ctx, resolve_element_env);
48866 JS_FreeValue(ctx, values);
48867 JS_FreeValue(ctx, next_method);
48868 JS_FreeValue(ctx, iter);
48869 JS_FreeValue(ctx, resolving_funcs[0]);
48870 JS_FreeValue(ctx, resolving_funcs[1]);
48871 return result_promise;
48872 fail:
48873 JS_FreeValue(ctx, result_promise);
48874 result_promise = JS_EXCEPTION;
48875 goto done;
48876}
48877
48878static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
48879 int argc, JSValueConst *argv)
48880{
48881 JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
48882 JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
48883 JSValue promise_resolve = JS_UNDEFINED;
48884 BOOL done;
48885
48886 if (!JS_IsObject(this_val))
48887 return JS_ThrowTypeErrorNotAnObject(ctx);
48888 result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
48889 if (JS_IsException(result_promise))
48890 return result_promise;
48891 promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
48892 if (JS_IsException(promise_resolve) ||
48893 check_function(ctx, promise_resolve))
48894 goto fail_reject;
48895 iter = JS_GetIterator(ctx, argv[0], FALSE);
48896 if (JS_IsException(iter)) {
48897 JSValue error;
48898 fail_reject:
48899 error = JS_GetException(ctx);
48900 ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
48901 (JSValueConst *)&error);
48902 JS_FreeValue(ctx, error);
48903 if (JS_IsException(ret))
48904 goto fail;
48905 JS_FreeValue(ctx, ret);
48906 } else {
48907 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
48908 if (JS_IsException(next_method))
48909 goto fail_reject;
48910
48911 for(;;) {
48912 /* XXX: conformance: should close the iterator if error on 'done'
48913 access, but not on 'value' access */
48914 item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
48915 if (JS_IsException(item))
48916 goto fail_reject;
48917 if (done)
48918 break;
48919 next_promise = JS_Call(ctx, promise_resolve,
48920 this_val, 1, (JSValueConst *)&item);
48921 JS_FreeValue(ctx, item);
48922 if (JS_IsException(next_promise)) {
48923 fail_reject1:
48924 JS_IteratorClose(ctx, iter, TRUE);
48925 goto fail_reject;
48926 }
48927 ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
48928 (JSValueConst *)resolving_funcs);
48929 if (check_exception_free(ctx, ret))
48930 goto fail_reject1;
48931 }
48932 }
48933 done:
48934 JS_FreeValue(ctx, promise_resolve);
48935 JS_FreeValue(ctx, next_method);
48936 JS_FreeValue(ctx, iter);
48937 JS_FreeValue(ctx, resolving_funcs[0]);
48938 JS_FreeValue(ctx, resolving_funcs[1]);
48939 return result_promise;
48940 fail:
48941 //JS_FreeValue(ctx, next_method); // why not???
48942 JS_FreeValue(ctx, result_promise);
48943 result_promise = JS_EXCEPTION;
48944 goto done;
48945}
48946
48947static __exception int perform_promise_then(JSContext *ctx,
48948 JSValueConst promise,
48949 JSValueConst *resolve_reject,
48950 JSValueConst *cap_resolving_funcs)
48951{
48952 JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
48953 JSPromiseReactionData *rd_array[2], *rd;
48954 int i, j;
48955
48956 rd_array[0] = NULL;
48957 rd_array[1] = NULL;
48958 for(i = 0; i < 2; i++) {
48959 JSValueConst handler;
48960 rd = js_mallocz(ctx, sizeof(*rd));
48961 if (!rd) {
48962 if (i == 1)
48963 promise_reaction_data_free(ctx->rt, rd_array[0]);
48964 return -1;
48965 }
48966 for(j = 0; j < 2; j++)
48967 rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
48968 handler = resolve_reject[i];
48969 if (!JS_IsFunction(ctx, handler))
48970 handler = JS_UNDEFINED;
48971 rd->handler = JS_DupValue(ctx, handler);
48972 rd_array[i] = rd;
48973 }
48974
48975 if (s->promise_state == JS_PROMISE_PENDING) {
48976 for(i = 0; i < 2; i++)
48977 list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
48978 } else {
48979 JSValueConst args[5];
48980 if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
48981 JSRuntime *rt = ctx->rt;
48982 if (rt->host_promise_rejection_tracker) {
48983 rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
48984 TRUE, rt->host_promise_rejection_tracker_opaque);
48985 }
48986 }
48987 i = s->promise_state - JS_PROMISE_FULFILLED;
48988 rd = rd_array[i];
48989 args[0] = rd->resolving_funcs[0];
48990 args[1] = rd->resolving_funcs[1];
48991 args[2] = rd->handler;
48992 args[3] = JS_NewBool(ctx, i);
48993 args[4] = s->promise_result;
48994 JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
48995 for(i = 0; i < 2; i++)
48996 promise_reaction_data_free(ctx->rt, rd_array[i]);
48997 }
48998 s->is_handled = TRUE;
48999 return 0;
49000}
49001
49002static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
49003 int argc, JSValueConst *argv)
49004{
49005 JSValue ctor, result_promise, resolving_funcs[2];
49006 JSPromiseData *s;
49007 int i, ret;
49008
49009 s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
49010 if (!s)
49011 return JS_EXCEPTION;
49012
49013 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
49014 if (JS_IsException(ctor))
49015 return ctor;
49016 result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
49017 JS_FreeValue(ctx, ctor);
49018 if (JS_IsException(result_promise))
49019 return result_promise;
49020 ret = perform_promise_then(ctx, this_val, argv,
49021 (JSValueConst *)resolving_funcs);
49022 for(i = 0; i < 2; i++)
49023 JS_FreeValue(ctx, resolving_funcs[i]);
49024 if (ret) {
49025 JS_FreeValue(ctx, result_promise);
49026 return JS_EXCEPTION;
49027 }
49028 return result_promise;
49029}
49030
49031static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
49032 int argc, JSValueConst *argv)
49033{
49034 JSValueConst args[2];
49035 args[0] = JS_UNDEFINED;
49036 args[1] = argv[0];
49037 return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
49038}
49039
49040static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
49041 int argc, JSValueConst *argv,
49042 int magic, JSValue *func_data)
49043{
49044 return JS_DupValue(ctx, func_data[0]);
49045}
49046
49047static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
49048 int argc, JSValueConst *argv,
49049 int magic, JSValue *func_data)
49050{
49051 return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
49052}
49053
49054static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
49055 int argc, JSValueConst *argv,
49056 int magic, JSValue *func_data)
49057{
49058 JSValueConst ctor = func_data[0];
49059 JSValueConst onFinally = func_data[1];
49060 JSValue res, promise, ret, then_func;
49061
49062 res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
49063 if (JS_IsException(res))
49064 return res;
49065 promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
49066 JS_FreeValue(ctx, res);
49067 if (JS_IsException(promise))
49068 return promise;
49069 if (magic == 0) {
49070 then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
49071 0, 1, argv);
49072 } else {
49073 then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
49074 0, 1, argv);
49075 }
49076 if (JS_IsException(then_func)) {
49077 JS_FreeValue(ctx, promise);
49078 return then_func;
49079 }
49080 ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
49081 JS_FreeValue(ctx, then_func);
49082 return ret;
49083}
49084
49085static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
49086 int argc, JSValueConst *argv)
49087{
49088 JSValueConst onFinally = argv[0];
49089 JSValue ctor, ret;
49090 JSValue then_funcs[2];
49091 JSValueConst func_data[2];
49092 int i;
49093
49094 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
49095 if (JS_IsException(ctor))
49096 return ctor;
49097 if (!JS_IsFunction(ctx, onFinally)) {
49098 then_funcs[0] = JS_DupValue(ctx, onFinally);
49099 then_funcs[1] = JS_DupValue(ctx, onFinally);
49100 } else {
49101 func_data[0] = ctor;
49102 func_data[1] = onFinally;
49103 for(i = 0; i < 2; i++) {
49104 then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
49105 if (JS_IsException(then_funcs[i])) {
49106 if (i == 1)
49107 JS_FreeValue(ctx, then_funcs[0]);
49108 JS_FreeValue(ctx, ctor);
49109 return JS_EXCEPTION;
49110 }
49111 }
49112 }
49113 JS_FreeValue(ctx, ctor);
49114 ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
49115 JS_FreeValue(ctx, then_funcs[0]);
49116 JS_FreeValue(ctx, then_funcs[1]);
49117 return ret;
49118}
49119
49120static const JSCFunctionListEntry js_promise_funcs[] = {
49121 JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
49122 JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
49123 JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
49124 JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
49125 JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
49126 JS_CFUNC_DEF("race", 1, js_promise_race ),
49127 JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ),
49128 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
49129};
49130
49131static const JSCFunctionListEntry js_promise_proto_funcs[] = {
49132 JS_CFUNC_DEF("then", 2, js_promise_then ),
49133 JS_CFUNC_DEF("catch", 1, js_promise_catch ),
49134 JS_CFUNC_DEF("finally", 1, js_promise_finally ),
49135 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
49136};
49137
49138/* AsyncFunction */
49139static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
49140 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
49141};
49142
49143/* AsyncIteratorPrototype */
49144
49145static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
49146 JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
49147};
49148
49149/* AsyncFromSyncIteratorPrototype */
49150
49151typedef struct JSAsyncFromSyncIteratorData {
49152 JSValue sync_iter;
49153 JSValue next_method;
49154} JSAsyncFromSyncIteratorData;
49155
49156static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
49157{
49158 JSAsyncFromSyncIteratorData *s =
49159 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
49160 if (s) {
49161 JS_FreeValueRT(rt, s->sync_iter);
49162 JS_FreeValueRT(rt, s->next_method);
49163 js_free_rt(rt, s);
49164 }
49165}
49166
49167static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
49168 JS_MarkFunc *mark_func)
49169{
49170 JSAsyncFromSyncIteratorData *s =
49171 JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
49172 if (s) {
49173 JS_MarkValue(rt, s->sync_iter, mark_func);
49174 JS_MarkValue(rt, s->next_method, mark_func);
49175 }
49176}
49177
49178static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
49179 JSValueConst sync_iter)
49180{
49181 JSValue async_iter, next_method;
49182 JSAsyncFromSyncIteratorData *s;
49183
49184 next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
49185 if (JS_IsException(next_method))
49186 return JS_EXCEPTION;
49187 async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
49188 if (JS_IsException(async_iter)) {
49189 JS_FreeValue(ctx, next_method);
49190 return async_iter;
49191 }
49192 s = js_mallocz(ctx, sizeof(*s));
49193 if (!s) {
49194 JS_FreeValue(ctx, async_iter);
49195 JS_FreeValue(ctx, next_method);
49196 return JS_EXCEPTION;
49197 }
49198 s->sync_iter = JS_DupValue(ctx, sync_iter);
49199 s->next_method = next_method;
49200 JS_SetOpaque(async_iter, s);
49201 return async_iter;
49202}
49203
49204static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
49205 JSValueConst this_val,
49206 int argc, JSValueConst *argv,
49207 int magic, JSValue *func_data)
49208{
49209 return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
49210 JS_ToBool(ctx, func_data[0]));
49211}
49212
49213static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
49214 BOOL done)
49215{
49216 JSValueConst func_data[1];
49217
49218 func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
49219 return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
49220 1, 0, 1, func_data);
49221}
49222
49223static JSValue js_async_from_sync_iterator_close_wrap(JSContext *ctx,
49224 JSValueConst this_val,
49225 int argc, JSValueConst *argv,
49226 int magic, JSValue *func_data)
49227{
49228 JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
49229 JS_IteratorClose(ctx, func_data[0], TRUE);
49230 return JS_EXCEPTION;
49231}
49232
49233static JSValue js_async_from_sync_iterator_close_wrap_func_create(JSContext *ctx, JSValueConst sync_iter)
49234{
49235 return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_close_wrap,
49236 1, 0, 1, &sync_iter);
49237}
49238
49239static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
49240 int argc, JSValueConst *argv,
49241 int magic)
49242{
49243 JSValue promise, resolving_funcs[2], value, err, method;
49244 JSAsyncFromSyncIteratorData *s;
49245 int done;
49246 int is_reject;
49247
49248 promise = JS_NewPromiseCapability(ctx, resolving_funcs);
49249 if (JS_IsException(promise))
49250 return JS_EXCEPTION;
49251 s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
49252 if (!s) {
49253 JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
49254 goto reject;
49255 }
49256
49257 if (magic == GEN_MAGIC_NEXT) {
49258 method = JS_DupValue(ctx, s->next_method);
49259 } else {
49260 method = JS_GetProperty(ctx, s->sync_iter,
49261 magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
49262 JS_ATOM_throw);
49263 if (JS_IsException(method))
49264 goto reject;
49265 if (JS_IsUndefined(method) || JS_IsNull(method)) {
49266 if (magic == GEN_MAGIC_RETURN) {
49267 err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
49268 is_reject = 0;
49269 goto done_resolve;
49270 } else {
49271 if (JS_IteratorClose(ctx, s->sync_iter, FALSE))
49272 goto reject;
49273 JS_ThrowTypeError(ctx, "throw is not a method");
49274 goto reject;
49275 }
49276 }
49277 }
49278 value = JS_IteratorNext2(ctx, s->sync_iter, method,
49279 argc >= 1 ? 1 : 0, argv, &done);
49280 JS_FreeValue(ctx, method);
49281 if (JS_IsException(value))
49282 goto reject;
49283 if (done == 2) {
49284 JSValue obj = value;
49285 value = JS_IteratorGetCompleteValue(ctx, obj, &done);
49286 JS_FreeValue(ctx, obj);
49287 if (JS_IsException(value))
49288 goto reject;
49289 }
49290
49291 if (JS_IsException(value))
49292 goto reject;
49293 {
49294 JSValue value_wrapper_promise, resolve_reject[2];
49295 int res;
49296
49297 value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
49298 1, (JSValueConst *)&value, 0);
49299 if (JS_IsException(value_wrapper_promise)) {
49300 JSValue res2;
49301 JS_FreeValue(ctx, value);
49302 if (magic != GEN_MAGIC_RETURN && !done) {
49303 JS_IteratorClose(ctx, s->sync_iter, TRUE);
49304 }
49305 reject:
49306 err = JS_GetException(ctx);
49307 is_reject = 1;
49308 done_resolve:
49309 res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
49310 1, (JSValueConst *)&err);
49311 JS_FreeValue(ctx, err);
49312 JS_FreeValue(ctx, res2);
49313 JS_FreeValue(ctx, resolving_funcs[0]);
49314 JS_FreeValue(ctx, resolving_funcs[1]);
49315 return promise;
49316 }
49317
49318 resolve_reject[0] =
49319 js_async_from_sync_iterator_unwrap_func_create(ctx, done);
49320 if (JS_IsException(resolve_reject[0])) {
49321 JS_FreeValue(ctx, value_wrapper_promise);
49322 goto fail;
49323 }
49324 if (done || magic == GEN_MAGIC_RETURN) {
49325 resolve_reject[1] = JS_UNDEFINED;
49326 } else {
49327 resolve_reject[1] =
49328 js_async_from_sync_iterator_close_wrap_func_create(ctx, s->sync_iter);
49329 if (JS_IsException(resolve_reject[1])) {
49330 JS_FreeValue(ctx, value_wrapper_promise);
49331 JS_FreeValue(ctx, resolve_reject[0]);
49332 goto fail;
49333 }
49334 }
49335 JS_FreeValue(ctx, value);
49336 res = perform_promise_then(ctx, value_wrapper_promise,
49337 (JSValueConst *)resolve_reject,
49338 (JSValueConst *)resolving_funcs);
49339 JS_FreeValue(ctx, resolve_reject[0]);
49340 JS_FreeValue(ctx, resolve_reject[1]);
49341 JS_FreeValue(ctx, value_wrapper_promise);
49342 JS_FreeValue(ctx, resolving_funcs[0]);
49343 JS_FreeValue(ctx, resolving_funcs[1]);
49344 if (res) {
49345 JS_FreeValue(ctx, promise);
49346 return JS_EXCEPTION;
49347 }
49348 }
49349 return promise;
49350 fail:
49351 JS_FreeValue(ctx, value);
49352 JS_FreeValue(ctx, resolving_funcs[0]);
49353 JS_FreeValue(ctx, resolving_funcs[1]);
49354 JS_FreeValue(ctx, promise);
49355 return JS_EXCEPTION;
49356}
49357
49358static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
49359 JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
49360 JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
49361 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
49362};
49363
49364/* AsyncGeneratorFunction */
49365
49366static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
49367 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
49368};
49369
49370/* AsyncGenerator prototype */
49371
49372static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
49373 JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
49374 JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
49375 JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
49376 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
49377};
49378
49379static JSClassShortDef const js_async_class_def[] = {
49380 { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */
49381 { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
49382 { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
49383 { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */
49384 { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
49385 { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
49386 { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
49387 { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
49388 { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
49389};
49390
49391void JS_AddIntrinsicPromise(JSContext *ctx)
49392{
49393 JSRuntime *rt = ctx->rt;
49394 JSValue obj1;
49395
49396 if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
49397 init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
49398 countof(js_async_class_def));
49399 rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
49400 rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
49401 rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
49402 rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
49403 rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
49404 rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
49405 }
49406
49407 /* Promise */
49408 ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
49409 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
49410 js_promise_proto_funcs,
49411 countof(js_promise_proto_funcs));
49412 obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
49413 JS_CFUNC_constructor, 0);
49414 ctx->promise_ctor = JS_DupValue(ctx, obj1);
49415 JS_SetPropertyFunctionList(ctx, obj1,
49416 js_promise_funcs,
49417 countof(js_promise_funcs));
49418 JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
49419 ctx->class_proto[JS_CLASS_PROMISE]);
49420
49421 /* AsyncFunction */
49422 ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
49423 obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
49424 "AsyncFunction", 1,
49425 JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
49426 ctx->function_ctor);
49427 JS_SetPropertyFunctionList(ctx,
49428 ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
49429 js_async_function_proto_funcs,
49430 countof(js_async_function_proto_funcs));
49431 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
49432 0, JS_PROP_CONFIGURABLE);
49433 JS_FreeValue(ctx, obj1);
49434
49435 /* AsyncIteratorPrototype */
49436 ctx->async_iterator_proto = JS_NewObject(ctx);
49437 JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
49438 js_async_iterator_proto_funcs,
49439 countof(js_async_iterator_proto_funcs));
49440
49441 /* AsyncFromSyncIteratorPrototype */
49442 ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
49443 JS_NewObjectProto(ctx, ctx->async_iterator_proto);
49444 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
49445 js_async_from_sync_iterator_proto_funcs,
49446 countof(js_async_from_sync_iterator_proto_funcs));
49447
49448 /* AsyncGeneratorPrototype */
49449 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
49450 JS_NewObjectProto(ctx, ctx->async_iterator_proto);
49451 JS_SetPropertyFunctionList(ctx,
49452 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
49453 js_async_generator_proto_funcs,
49454 countof(js_async_generator_proto_funcs));
49455
49456 /* AsyncGeneratorFunction */
49457 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
49458 JS_NewObjectProto(ctx, ctx->function_proto);
49459 obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
49460 "AsyncGeneratorFunction", 1,
49461 JS_CFUNC_constructor_or_func_magic,
49462 JS_FUNC_ASYNC_GENERATOR,
49463 ctx->function_ctor);
49464 JS_SetPropertyFunctionList(ctx,
49465 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
49466 js_async_generator_function_proto_funcs,
49467 countof(js_async_generator_function_proto_funcs));
49468 JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
49469 ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
49470 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
49471 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
49472 0, JS_PROP_CONFIGURABLE);
49473 JS_FreeValue(ctx, obj1);
49474}
49475
49476/* URI handling */
49477
49478static int string_get_hex(JSString *p, int k, int n) {
49479 int c = 0, h;
49480 while (n-- > 0) {
49481 if ((h = from_hex(string_get(p, k++))) < 0)
49482 return -1;
49483 c = (c << 4) | h;
49484 }
49485 return c;
49486}
49487
49488static int isURIReserved(int c) {
49489 return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
49490}
49491
49492static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
49493{
49494 va_list ap;
49495
49496 va_start(ap, fmt);
49497 JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
49498 va_end(ap);
49499 return -1;
49500}
49501
49502static int hex_decode(JSContext *ctx, JSString *p, int k) {
49503 int c;
49504
49505 if (k >= p->len || string_get(p, k) != '%')
49506 return js_throw_URIError(ctx, "expecting %%");
49507 if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
49508 return js_throw_URIError(ctx, "expecting hex digit");
49509
49510 return c;
49511}
49512
49513static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
49514 int argc, JSValueConst *argv, int isComponent)
49515{
49516 JSValue str;
49517 StringBuffer b_s, *b = &b_s;
49518 JSString *p;
49519 int k, c, c1, n, c_min;
49520
49521 str = JS_ToString(ctx, argv[0]);
49522 if (JS_IsException(str))
49523 return str;
49524
49525 string_buffer_init(ctx, b, 0);
49526
49527 p = JS_VALUE_GET_STRING(str);
49528 for (k = 0; k < p->len;) {
49529 c = string_get(p, k);
49530 if (c == '%') {
49531 c = hex_decode(ctx, p, k);
49532 if (c < 0)
49533 goto fail;
49534 k += 3;
49535 if (c < 0x80) {
49536 if (!isComponent && isURIReserved(c)) {
49537 c = '%';
49538 k -= 2;
49539 }
49540 } else {
49541 /* Decode URI-encoded UTF-8 sequence */
49542 if (c >= 0xc0 && c <= 0xdf) {
49543 n = 1;
49544 c_min = 0x80;
49545 c &= 0x1f;
49546 } else if (c >= 0xe0 && c <= 0xef) {
49547 n = 2;
49548 c_min = 0x800;
49549 c &= 0xf;
49550 } else if (c >= 0xf0 && c <= 0xf7) {
49551 n = 3;
49552 c_min = 0x10000;
49553 c &= 0x7;
49554 } else {
49555 n = 0;
49556 c_min = 1;
49557 c = 0;
49558 }
49559 while (n-- > 0) {
49560 c1 = hex_decode(ctx, p, k);
49561 if (c1 < 0)
49562 goto fail;
49563 k += 3;
49564 if ((c1 & 0xc0) != 0x80) {
49565 c = 0;
49566 break;
49567 }
49568 c = (c << 6) | (c1 & 0x3f);
49569 }
49570 if (c < c_min || c > 0x10FFFF || is_surrogate(c)) {
49571 js_throw_URIError(ctx, "malformed UTF-8");
49572 goto fail;
49573 }
49574 }
49575 } else {
49576 k++;
49577 }
49578 string_buffer_putc(b, c);
49579 }
49580 JS_FreeValue(ctx, str);
49581 return string_buffer_end(b);
49582
49583fail:
49584 JS_FreeValue(ctx, str);
49585 string_buffer_free(b);
49586 return JS_EXCEPTION;
49587}
49588
49589static int isUnescaped(int c) {
49590 static char const unescaped_chars[] =
49591 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
49592 "abcdefghijklmnopqrstuvwxyz"
49593 "0123456789"
49594 "@*_+-./";
49595 return c < 0x100 &&
49596 memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
49597}
49598
49599static int isURIUnescaped(int c, int isComponent) {
49600 return c < 0x100 &&
49601 ((c >= 0x61 && c <= 0x7a) ||
49602 (c >= 0x41 && c <= 0x5a) ||
49603 (c >= 0x30 && c <= 0x39) ||
49604 memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
49605 (!isComponent && isURIReserved(c)));
49606}
49607
49608static int encodeURI_hex(StringBuffer *b, int c) {
49609 uint8_t buf[6];
49610 int n = 0;
49611 const char *hex = "0123456789ABCDEF";
49612
49613 buf[n++] = '%';
49614 if (c >= 256) {
49615 buf[n++] = 'u';
49616 buf[n++] = hex[(c >> 12) & 15];
49617 buf[n++] = hex[(c >> 8) & 15];
49618 }
49619 buf[n++] = hex[(c >> 4) & 15];
49620 buf[n++] = hex[(c >> 0) & 15];
49621 return string_buffer_write8(b, buf, n);
49622}
49623
49624static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
49625 int argc, JSValueConst *argv,
49626 int isComponent)
49627{
49628 JSValue str;
49629 StringBuffer b_s, *b = &b_s;
49630 JSString *p;
49631 int k, c, c1;
49632
49633 str = JS_ToString(ctx, argv[0]);
49634 if (JS_IsException(str))
49635 return str;
49636
49637 p = JS_VALUE_GET_STRING(str);
49638 string_buffer_init(ctx, b, p->len);
49639 for (k = 0; k < p->len;) {
49640 c = string_get(p, k);
49641 k++;
49642 if (isURIUnescaped(c, isComponent)) {
49643 string_buffer_putc16(b, c);
49644 } else {
49645 if (is_lo_surrogate(c)) {
49646 js_throw_URIError(ctx, "invalid character");
49647 goto fail;
49648 } else if (is_hi_surrogate(c)) {
49649 if (k >= p->len) {
49650 js_throw_URIError(ctx, "expecting surrogate pair");
49651 goto fail;
49652 }
49653 c1 = string_get(p, k);
49654 k++;
49655 if (!is_lo_surrogate(c1)) {
49656 js_throw_URIError(ctx, "expecting surrogate pair");
49657 goto fail;
49658 }
49659 c = from_surrogate(c, c1);
49660 }
49661 if (c < 0x80) {
49662 encodeURI_hex(b, c);
49663 } else {
49664 /* XXX: use C UTF-8 conversion ? */
49665 if (c < 0x800) {
49666 encodeURI_hex(b, (c >> 6) | 0xc0);
49667 } else {
49668 if (c < 0x10000) {
49669 encodeURI_hex(b, (c >> 12) | 0xe0);
49670 } else {
49671 encodeURI_hex(b, (c >> 18) | 0xf0);
49672 encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
49673 }
49674 encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
49675 }
49676 encodeURI_hex(b, (c & 0x3f) | 0x80);
49677 }
49678 }
49679 }
49680 JS_FreeValue(ctx, str);
49681 return string_buffer_end(b);
49682
49683fail:
49684 JS_FreeValue(ctx, str);
49685 string_buffer_free(b);
49686 return JS_EXCEPTION;
49687}
49688
49689static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
49690 int argc, JSValueConst *argv)
49691{
49692 JSValue str;
49693 StringBuffer b_s, *b = &b_s;
49694 JSString *p;
49695 int i, len, c;
49696
49697 str = JS_ToString(ctx, argv[0]);
49698 if (JS_IsException(str))
49699 return str;
49700
49701 p = JS_VALUE_GET_STRING(str);
49702 string_buffer_init(ctx, b, p->len);
49703 for (i = 0, len = p->len; i < len; i++) {
49704 c = string_get(p, i);
49705 if (isUnescaped(c)) {
49706 string_buffer_putc16(b, c);
49707 } else {
49708 encodeURI_hex(b, c);
49709 }
49710 }
49711 JS_FreeValue(ctx, str);
49712 return string_buffer_end(b);
49713}
49714
49715static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
49716 int argc, JSValueConst *argv)
49717{
49718 JSValue str;
49719 StringBuffer b_s, *b = &b_s;
49720 JSString *p;
49721 int i, len, c, n;
49722
49723 str = JS_ToString(ctx, argv[0]);
49724 if (JS_IsException(str))
49725 return str;
49726
49727 string_buffer_init(ctx, b, 0);
49728 p = JS_VALUE_GET_STRING(str);
49729 for (i = 0, len = p->len; i < len; i++) {
49730 c = string_get(p, i);
49731 if (c == '%') {
49732 if (i + 6 <= len
49733 && string_get(p, i + 1) == 'u'
49734 && (n = string_get_hex(p, i + 2, 4)) >= 0) {
49735 c = n;
49736 i += 6 - 1;
49737 } else
49738 if (i + 3 <= len
49739 && (n = string_get_hex(p, i + 1, 2)) >= 0) {
49740 c = n;
49741 i += 3 - 1;
49742 }
49743 }
49744 string_buffer_putc16(b, c);
49745 }
49746 JS_FreeValue(ctx, str);
49747 return string_buffer_end(b);
49748}
49749
49750/* global object */
49751
49752static const JSCFunctionListEntry js_global_funcs[] = {
49753 JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
49754 JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
49755 JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
49756 JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
49757
49758 JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
49759 JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
49760 JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
49761 JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
49762 JS_CFUNC_DEF("escape", 1, js_global_escape ),
49763 JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
49764 JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
49765 JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
49766 JS_PROP_UNDEFINED_DEF("undefined", 0 ),
49767 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ),
49768};
49769
49770/* Date */
49771
49772static int64_t math_mod(int64_t a, int64_t b) {
49773 /* return positive modulo */
49774 int64_t m = a % b;
49775 return m + (m < 0) * b;
49776}
49777
49778static int64_t floor_div(int64_t a, int64_t b) {
49779 /* integer division rounding toward -Infinity */
49780 int64_t m = a % b;
49781 return (a - (m + (m < 0) * b)) / b;
49782}
49783
49784static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
49785 int argc, JSValueConst *argv);
49786
49787static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
49788{
49789 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49790 JSObject *p = JS_VALUE_GET_OBJ(this_val);
49791 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
49792 return JS_ToFloat64(ctx, valp, p->u.object_data);
49793 }
49794 JS_ThrowTypeError(ctx, "not a Date object");
49795 return -1;
49796}
49797
49798static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
49799{
49800 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
49801 JSObject *p = JS_VALUE_GET_OBJ(this_val);
49802 if (p->class_id == JS_CLASS_DATE) {
49803 JS_FreeValue(ctx, p->u.object_data);
49804 p->u.object_data = JS_NewFloat64(ctx, v);
49805 return JS_DupValue(ctx, p->u.object_data);
49806 }
49807 }
49808 return JS_ThrowTypeError(ctx, "not a Date object");
49809}
49810
49811static int64_t days_from_year(int64_t y) {
49812 return 365 * (y - 1970) + floor_div(y - 1969, 4) -
49813 floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
49814}
49815
49816static int64_t days_in_year(int64_t y) {
49817 return 365 + !(y % 4) - !(y % 100) + !(y % 400);
49818}
49819
49820/* return the year, update days */
49821static int64_t year_from_days(int64_t *days) {
49822 int64_t y, d1, nd, d = *days;
49823 y = floor_div(d * 10000, 3652425) + 1970;
49824 /* the initial approximation is very good, so only a few
49825 iterations are necessary */
49826 for(;;) {
49827 d1 = d - days_from_year(y);
49828 if (d1 < 0) {
49829 y--;
49830 d1 += days_in_year(y);
49831 } else {
49832 nd = days_in_year(y);
49833 if (d1 < nd)
49834 break;
49835 d1 -= nd;
49836 y++;
49837 }
49838 }
49839 *days = d1;
49840 return y;
49841}
49842
49843static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
49844static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
49845static char const day_names[] = "SunMonTueWedThuFriSat";
49846
49847static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
49848 double fields[minimum_length(9)], int is_local, int force)
49849{
49850 double dval;
49851 int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
49852
49853 if (JS_ThisTimeValue(ctx, &dval, obj))
49854 return -1;
49855
49856 if (isnan(dval)) {
49857 if (!force)
49858 return FALSE; /* NaN */
49859 d = 0; /* initialize all fields to 0 */
49860 } else {
49861 d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */
49862 if (is_local) {
49863 tz = -getTimezoneOffset(d);
49864 d += tz * 60000;
49865 }
49866 }
49867
49868 /* result is >= 0, we can use % */
49869 h = math_mod(d, 86400000);
49870 days = (d - h) / 86400000;
49871 ms = h % 1000;
49872 h = (h - ms) / 1000;
49873 s = h % 60;
49874 h = (h - s) / 60;
49875 m = h % 60;
49876 h = (h - m) / 60;
49877 wd = math_mod(days + 4, 7); /* week day */
49878 y = year_from_days(&days);
49879
49880 for(i = 0; i < 11; i++) {
49881 md = month_days[i];
49882 if (i == 1)
49883 md += days_in_year(y) - 365;
49884 if (days < md)
49885 break;
49886 days -= md;
49887 }
49888 fields[0] = y;
49889 fields[1] = i;
49890 fields[2] = days + 1;
49891 fields[3] = h;
49892 fields[4] = m;
49893 fields[5] = s;
49894 fields[6] = ms;
49895 fields[7] = wd;
49896 fields[8] = tz;
49897 return TRUE;
49898}
49899
49900static double time_clip(double t) {
49901 if (t >= -8.64e15 && t <= 8.64e15)
49902 return trunc(t) + 0.0; /* convert -0 to +0 */
49903 else
49904 return NAN;
49905}
49906
49907/* The spec mandates the use of 'double' and it specifies the order
49908 of the operations */
49909static double set_date_fields(double fields[minimum_length(7)], int is_local) {
49910 double y, m, dt, ym, mn, day, h, s, milli, time, tv;
49911 int yi, mi, i;
49912 int64_t days;
49913 volatile double temp; /* enforce evaluation order */
49914
49915 /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
49916 y = fields[0];
49917 m = fields[1];
49918 dt = fields[2];
49919 ym = y + floor(m / 12);
49920 mn = fmod(m, 12);
49921 if (mn < 0)
49922 mn += 12;
49923 if (ym < -271821 || ym > 275760)
49924 return NAN;
49925
49926 yi = ym;
49927 mi = mn;
49928 days = days_from_year(yi);
49929 for(i = 0; i < mi; i++) {
49930 days += month_days[i];
49931 if (i == 1)
49932 days += days_in_year(yi) - 365;
49933 }
49934 day = days + dt - 1;
49935
49936 /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */
49937 h = fields[3];
49938 m = fields[4];
49939 s = fields[5];
49940 milli = fields[6];
49941 /* Use a volatile intermediary variable to ensure order of evaluation
49942 * as specified in ECMA. This fixes a test262 error on
49943 * test262/test/built-ins/Date/UTC/fp-evaluation-order.js.
49944 * Without the volatile qualifier, the compile can generate code
49945 * that performs the computation in a different order or with instructions
49946 * that produce a different result such as FMA (float multiply and add).
49947 */
49948 time = h * 3600000;
49949 time += (temp = m * 60000);
49950 time += (temp = s * 1000);
49951 time += milli;
49952
49953 /* emulate 21.4.1.16 MakeDate ( day, time ) */
49954 tv = (temp = day * 86400000) + time; /* prevent generation of FMA */
49955 if (!isfinite(tv))
49956 return NAN;
49957
49958 /* adjust for local time and clip */
49959 if (is_local) {
49960 int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
49961 tv += getTimezoneOffset(ti) * 60000;
49962 }
49963 return time_clip(tv);
49964}
49965
49966static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
49967 int argc, JSValueConst *argv, int magic)
49968{
49969 // get_date_field(obj, n, is_local)
49970 double fields[9];
49971 int res, n, is_local;
49972
49973 is_local = magic & 0x0F;
49974 n = (magic >> 4) & 0x0F;
49975 res = get_date_fields(ctx, this_val, fields, is_local, 0);
49976 if (res < 0)
49977 return JS_EXCEPTION;
49978 if (!res)
49979 return JS_NAN;
49980
49981 if (magic & 0x100) { // getYear
49982 fields[0] -= 1900;
49983 }
49984 return JS_NewFloat64(ctx, fields[n]);
49985}
49986
49987static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
49988 int argc, JSValueConst *argv, int magic)
49989{
49990 // _field(obj, first_field, end_field, args, is_local)
49991 double fields[9];
49992 int res, first_field, end_field, is_local, i, n, res1;
49993 double d, a;
49994
49995 d = NAN;
49996 first_field = (magic >> 8) & 0x0F;
49997 end_field = (magic >> 4) & 0x0F;
49998 is_local = magic & 0x0F;
49999
50000 res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
50001 if (res < 0)
50002 return JS_EXCEPTION;
50003 res1 = res;
50004
50005 // Argument coercion is observable and must be done unconditionally.
50006 n = min_int(argc, end_field - first_field);
50007 for(i = 0; i < n; i++) {
50008 if (JS_ToFloat64(ctx, &a, argv[i]))
50009 return JS_EXCEPTION;
50010 if (!isfinite(a))
50011 res = FALSE;
50012 fields[first_field + i] = trunc(a);
50013 }
50014
50015 if (!res1)
50016 return JS_NAN; /* thisTimeValue is NaN */
50017
50018 if (res && argc > 0)
50019 d = set_date_fields(fields, is_local);
50020
50021 return JS_SetThisTimeValue(ctx, this_val, d);
50022}
50023
50024/* fmt:
50025 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
50026 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
50027 2: toISOString: "2018-01-02T23:02:56.927Z"
50028 3: toLocaleString: "1/2/2018, 11:40:40 PM"
50029 part: 1=date, 2=time 3=all
50030 XXX: should use a variant of strftime().
50031 */
50032static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
50033 int argc, JSValueConst *argv, int magic)
50034{
50035 // _string(obj, fmt, part)
50036 char buf[64];
50037 double fields[9];
50038 int res, fmt, part, pos;
50039 int y, mon, d, h, m, s, ms, wd, tz;
50040
50041 fmt = (magic >> 4) & 0x0F;
50042 part = magic & 0x0F;
50043
50044 res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
50045 if (res < 0)
50046 return JS_EXCEPTION;
50047 if (!res) {
50048 if (fmt == 2)
50049 return JS_ThrowRangeError(ctx, "Date value is NaN");
50050 else
50051 return js_new_string8(ctx, "Invalid Date");
50052 }
50053
50054 y = fields[0];
50055 mon = fields[1];
50056 d = fields[2];
50057 h = fields[3];
50058 m = fields[4];
50059 s = fields[5];
50060 ms = fields[6];
50061 wd = fields[7];
50062 tz = fields[8];
50063
50064 pos = 0;
50065
50066 if (part & 1) { /* date part */
50067 switch(fmt) {
50068 case 0:
50069 pos += snprintf(buf + pos, sizeof(buf) - pos,
50070 "%.3s, %02d %.3s %0*d ",
50071 day_names + wd * 3, d,
50072 month_names + mon * 3, 4 + (y < 0), y);
50073 break;
50074 case 1:
50075 pos += snprintf(buf + pos, sizeof(buf) - pos,
50076 "%.3s %.3s %02d %0*d",
50077 day_names + wd * 3,
50078 month_names + mon * 3, d, 4 + (y < 0), y);
50079 if (part == 3) {
50080 buf[pos++] = ' ';
50081 }
50082 break;
50083 case 2:
50084 if (y >= 0 && y <= 9999) {
50085 pos += snprintf(buf + pos, sizeof(buf) - pos,
50086 "%04d", y);
50087 } else {
50088 pos += snprintf(buf + pos, sizeof(buf) - pos,
50089 "%+07d", y);
50090 }
50091 pos += snprintf(buf + pos, sizeof(buf) - pos,
50092 "-%02d-%02dT", mon + 1, d);
50093 break;
50094 case 3:
50095 pos += snprintf(buf + pos, sizeof(buf) - pos,
50096 "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
50097 if (part == 3) {
50098 buf[pos++] = ',';
50099 buf[pos++] = ' ';
50100 }
50101 break;
50102 }
50103 }
50104 if (part & 2) { /* time part */
50105 switch(fmt) {
50106 case 0:
50107 pos += snprintf(buf + pos, sizeof(buf) - pos,
50108 "%02d:%02d:%02d GMT", h, m, s);
50109 break;
50110 case 1:
50111 pos += snprintf(buf + pos, sizeof(buf) - pos,
50112 "%02d:%02d:%02d GMT", h, m, s);
50113 if (tz < 0) {
50114 buf[pos++] = '-';
50115 tz = -tz;
50116 } else {
50117 buf[pos++] = '+';
50118 }
50119 /* tz is >= 0, can use % */
50120 pos += snprintf(buf + pos, sizeof(buf) - pos,
50121 "%02d%02d", tz / 60, tz % 60);
50122 /* XXX: tack the time zone code? */
50123 break;
50124 case 2:
50125 pos += snprintf(buf + pos, sizeof(buf) - pos,
50126 "%02d:%02d:%02d.%03dZ", h, m, s, ms);
50127 break;
50128 case 3:
50129 pos += snprintf(buf + pos, sizeof(buf) - pos,
50130 "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s,
50131 (h < 12) ? 'A' : 'P');
50132 break;
50133 }
50134 }
50135 return JS_NewStringLen(ctx, buf, pos);
50136}
50137
50138/* OS dependent: return the UTC time in ms since 1970. */
50139static int64_t date_now(void) {
50140 struct timeval tv;
50141 gettimeofday(&tv, NULL);
50142 return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
50143}
50144
50145static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
50146 int argc, JSValueConst *argv)
50147{
50148 // Date(y, mon, d, h, m, s, ms)
50149 JSValue rv;
50150 int i, n;
50151 double a, val;
50152
50153 if (JS_IsUndefined(new_target)) {
50154 /* invoked as function */
50155 argc = 0;
50156 }
50157 n = argc;
50158 if (n == 0) {
50159 val = date_now();
50160 } else if (n == 1) {
50161 JSValue v, dv;
50162 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
50163 JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
50164 if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
50165 if (JS_ToFloat64(ctx, &val, p->u.object_data))
50166 return JS_EXCEPTION;
50167 val = time_clip(val);
50168 goto has_val;
50169 }
50170 }
50171 v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
50172 if (JS_IsString(v)) {
50173 dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
50174 JS_FreeValue(ctx, v);
50175 if (JS_IsException(dv))
50176 return JS_EXCEPTION;
50177 if (JS_ToFloat64Free(ctx, &val, dv))
50178 return JS_EXCEPTION;
50179 } else {
50180 if (JS_ToFloat64Free(ctx, &val, v))
50181 return JS_EXCEPTION;
50182 }
50183 val = time_clip(val);
50184 } else {
50185 double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
50186 if (n > 7)
50187 n = 7;
50188 for(i = 0; i < n; i++) {
50189 if (JS_ToFloat64(ctx, &a, argv[i]))
50190 return JS_EXCEPTION;
50191 if (!isfinite(a))
50192 break;
50193 fields[i] = trunc(a);
50194 if (i == 0 && fields[0] >= 0 && fields[0] < 100)
50195 fields[0] += 1900;
50196 }
50197 val = (i == n) ? set_date_fields(fields, 1) : NAN;
50198 }
50199has_val:
50200#if 0
50201 JSValueConst args[3];
50202 args[0] = new_target;
50203 args[1] = ctx->class_proto[JS_CLASS_DATE];
50204 args[2] = JS_NewFloat64(ctx, val);
50205 rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
50206#else
50207 rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
50208 if (!JS_IsException(rv))
50209 JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
50210#endif
50211 if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
50212 /* invoked as a function, return (new Date()).toString(); */
50213 JSValue s;
50214 s = get_date_string(ctx, rv, 0, NULL, 0x13);
50215 JS_FreeValue(ctx, rv);
50216 rv = s;
50217 }
50218 return rv;
50219}
50220
50221static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
50222 int argc, JSValueConst *argv)
50223{
50224 // UTC(y, mon, d, h, m, s, ms)
50225 double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
50226 int i, n;
50227 double a;
50228
50229 n = argc;
50230 if (n == 0)
50231 return JS_NAN;
50232 if (n > 7)
50233 n = 7;
50234 for(i = 0; i < n; i++) {
50235 if (JS_ToFloat64(ctx, &a, argv[i]))
50236 return JS_EXCEPTION;
50237 if (!isfinite(a))
50238 return JS_NAN;
50239 fields[i] = trunc(a);
50240 if (i == 0 && fields[0] >= 0 && fields[0] < 100)
50241 fields[0] += 1900;
50242 }
50243 return JS_NewFloat64(ctx, set_date_fields(fields, 0));
50244}
50245
50246/* Date string parsing */
50247
50248static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) {
50249 if (sp[*pp] == c) {
50250 *pp += 1;
50251 return TRUE;
50252 } else {
50253 return FALSE;
50254 }
50255}
50256
50257/* skip spaces, update offset, return next char */
50258static int string_skip_spaces(const uint8_t *sp, int *pp) {
50259 int c;
50260 while ((c = sp[*pp]) == ' ')
50261 *pp += 1;
50262 return c;
50263}
50264
50265/* skip dashes dots and commas */
50266static int string_skip_separators(const uint8_t *sp, int *pp) {
50267 int c;
50268 while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',')
50269 *pp += 1;
50270 return c;
50271}
50272
50273/* skip a word, stop on spaces, digits and separators, update offset */
50274static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) {
50275 int c;
50276 while (!strchr(stoplist, c = sp[*pp]))
50277 *pp += 1;
50278 return c;
50279}
50280
50281/* parse a numeric field (max_digits = 0 -> no maximum) */
50282static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval,
50283 int min_digits, int max_digits)
50284{
50285 int v = 0;
50286 int c, p = *pp, p_start;
50287
50288 p_start = p;
50289 while ((c = sp[p]) >= '0' && c <= '9') {
50290 /* arbitrary limit to 9 digits */
50291 if (v >= 100000000)
50292 return FALSE;
50293 v = v * 10 + c - '0';
50294 p++;
50295 if (p - p_start == max_digits)
50296 break;
50297 }
50298 if (p - p_start < min_digits)
50299 return FALSE;
50300 *pval = v;
50301 *pp = p;
50302 return TRUE;
50303}
50304
50305static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) {
50306 /* parse optional fractional part as milliseconds and truncate. */
50307 /* spec does not indicate which rounding should be used */
50308 int mul = 100, ms = 0, c, p_start, p = *pp;
50309
50310 c = sp[p];
50311 if (c == '.' || c == ',') {
50312 p++;
50313 p_start = p;
50314 while ((c = sp[p]) >= '0' && c <= '9') {
50315 ms += (c - '0') * mul;
50316 mul /= 10;
50317 p++;
50318 if (p - p_start == 9)
50319 break;
50320 }
50321 if (p > p_start) {
50322 /* only consume the separator if digits are present */
50323 *pval = ms;
50324 *pp = p;
50325 }
50326 }
50327 return TRUE;
50328}
50329
50330static uint8_t upper_ascii(uint8_t c) {
50331 return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c;
50332}
50333
50334static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) {
50335 int tz = 0, sgn, hh, mm, p = *pp;
50336
50337 sgn = sp[p++];
50338 if (sgn == '+' || sgn == '-') {
50339 int n = p;
50340 if (!string_get_digits(sp, &p, &hh, 1, 0))
50341 return FALSE;
50342 n = p - n;
50343 if (strict && n != 2 && n != 4)
50344 return FALSE;
50345 while (n > 4) {
50346 n -= 2;
50347 hh /= 100;
50348 }
50349 if (n > 2) {
50350 mm = hh % 100;
50351 hh = hh / 100;
50352 } else {
50353 mm = 0;
50354 if (string_skip_char(sp, &p, ':') /* optional separator */
50355 && !string_get_digits(sp, &p, &mm, 2, 2))
50356 return FALSE;
50357 }
50358 if (hh > 23 || mm > 59)
50359 return FALSE;
50360 tz = hh * 60 + mm;
50361 if (sgn != '+')
50362 tz = -tz;
50363 } else
50364 if (sgn != 'Z') {
50365 return FALSE;
50366 }
50367 *pp = p;
50368 *tzp = tz;
50369 return TRUE;
50370}
50371
50372static BOOL string_match(const uint8_t *sp, int *pp, const char *s) {
50373 int p = *pp;
50374 while (*s != '\0') {
50375 if (upper_ascii(sp[p]) != upper_ascii(*s++))
50376 return FALSE;
50377 p++;
50378 }
50379 *pp = p;
50380 return TRUE;
50381}
50382
50383static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) {
50384 int n, i;
50385
50386 for (n = 0; n < count; n++) {
50387 for (i = 0;; i++) {
50388 if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i]))
50389 break;
50390 if (i == 2)
50391 return n;
50392 }
50393 }
50394 return -1;
50395}
50396
50397static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) {
50398 int n;
50399
50400 n = find_abbrev(sp, *pp, month_names, 12);
50401 if (n < 0)
50402 return FALSE;
50403
50404 *pval = n + 1;
50405 *pp += 3;
50406 return TRUE;
50407}
50408
50409/* parse toISOString format */
50410static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) {
50411 int sgn, i, p = 0;
50412
50413 /* initialize fields to the beginning of the Epoch */
50414 for (i = 0; i < 9; i++) {
50415 fields[i] = (i == 2);
50416 }
50417 *is_local = FALSE;
50418
50419 /* year is either yyyy digits or [+-]yyyyyy */
50420 sgn = sp[p];
50421 if (sgn == '-' || sgn == '+') {
50422 p++;
50423 if (!string_get_digits(sp, &p, &fields[0], 6, 6))
50424 return FALSE;
50425 if (sgn == '-') {
50426 if (fields[0] == 0)
50427 return FALSE; // reject -000000
50428 fields[0] = -fields[0];
50429 }
50430 } else {
50431 if (!string_get_digits(sp, &p, &fields[0], 4, 4))
50432 return FALSE;
50433 }
50434 if (string_skip_char(sp, &p, '-')) {
50435 if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */
50436 return FALSE;
50437 if (fields[1] < 1)
50438 return FALSE;
50439 fields[1] -= 1;
50440 if (string_skip_char(sp, &p, '-')) {
50441 if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */
50442 return FALSE;
50443 if (fields[2] < 1)
50444 return FALSE;
50445 }
50446 }
50447 if (string_skip_char(sp, &p, 'T')) {
50448 *is_local = TRUE;
50449 if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */
50450 || !string_skip_char(sp, &p, ':')
50451 || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */
50452 fields[3] = 100; // reject unconditionally
50453 return TRUE;
50454 }
50455 if (string_skip_char(sp, &p, ':')) {
50456 if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */
50457 return FALSE;
50458 string_get_milliseconds(sp, &p, &fields[6]);
50459 }
50460 }
50461 /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
50462 if (sp[p]) {
50463 *is_local = FALSE;
50464 if (!string_get_tzoffset(sp, &p, &fields[8], TRUE))
50465 return FALSE;
50466 }
50467 /* error if extraneous characters */
50468 return sp[p] == '\0';
50469}
50470
50471static struct {
50472 char name[6];
50473 int16_t offset;
50474} const js_tzabbr[] = {
50475 { "GMT", 0 }, // Greenwich Mean Time
50476 { "UTC", 0 }, // Coordinated Universal Time
50477 { "UT", 0 }, // Universal Time
50478 { "Z", 0 }, // Zulu Time
50479 { "EDT", -4 * 60 }, // Eastern Daylight Time
50480 { "EST", -5 * 60 }, // Eastern Standard Time
50481 { "CDT", -5 * 60 }, // Central Daylight Time
50482 { "CST", -6 * 60 }, // Central Standard Time
50483 { "MDT", -6 * 60 }, // Mountain Daylight Time
50484 { "MST", -7 * 60 }, // Mountain Standard Time
50485 { "PDT", -7 * 60 }, // Pacific Daylight Time
50486 { "PST", -8 * 60 }, // Pacific Standard Time
50487 { "WET", +0 * 60 }, // Western European Time
50488 { "WEST", +1 * 60 }, // Western European Summer Time
50489 { "CET", +1 * 60 }, // Central European Time
50490 { "CEST", +2 * 60 }, // Central European Summer Time
50491 { "EET", +2 * 60 }, // Eastern European Time
50492 { "EEST", +3 * 60 }, // Eastern European Summer Time
50493};
50494
50495static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) {
50496 for (size_t i = 0; i < countof(js_tzabbr); i++) {
50497 if (string_match(sp, pp, js_tzabbr[i].name)) {
50498 *offset = js_tzabbr[i].offset;
50499 return TRUE;
50500 }
50501 }
50502 return FALSE;
50503}
50504
50505/* parse toString, toUTCString and other formats */
50506static BOOL js_date_parse_otherstring(const uint8_t *sp,
50507 int fields[minimum_length(9)],
50508 BOOL *is_local) {
50509 int c, i, val, p = 0, p_start;
50510 int num[3];
50511 BOOL has_year = FALSE;
50512 BOOL has_mon = FALSE;
50513 BOOL has_time = FALSE;
50514 int num_index = 0;
50515
50516 /* initialize fields to the beginning of 2001-01-01 */
50517 fields[0] = 2001;
50518 fields[1] = 1;
50519 fields[2] = 1;
50520 for (i = 3; i < 9; i++) {
50521 fields[i] = 0;
50522 }
50523 *is_local = TRUE;
50524
50525 while (string_skip_spaces(sp, &p)) {
50526 p_start = p;
50527 if ((c = sp[p]) == '+' || c == '-') {
50528 if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) {
50529 *is_local = FALSE;
50530 } else {
50531 p++;
50532 if (string_get_digits(sp, &p, &val, 1, 0)) {
50533 if (c == '-') {
50534 if (val == 0)
50535 return FALSE;
50536 val = -val;
50537 }
50538 fields[0] = val;
50539 has_year = TRUE;
50540 }
50541 }
50542 } else
50543 if (string_get_digits(sp, &p, &val, 1, 0)) {
50544 if (string_skip_char(sp, &p, ':')) {
50545 /* time part */
50546 fields[3] = val;
50547 if (!string_get_digits(sp, &p, &fields[4], 1, 2))
50548 return FALSE;
50549 if (string_skip_char(sp, &p, ':')) {
50550 if (!string_get_digits(sp, &p, &fields[5], 1, 2))
50551 return FALSE;
50552 string_get_milliseconds(sp, &p, &fields[6]);
50553 }
50554 has_time = TRUE;
50555 } else {
50556 if (p - p_start > 2) {
50557 fields[0] = val;
50558 has_year = TRUE;
50559 } else
50560 if (val < 1 || val > 31) {
50561 fields[0] = val + (val < 100) * 1900 + (val < 50) * 100;
50562 has_year = TRUE;
50563 } else {
50564 if (num_index == 3)
50565 return FALSE;
50566 num[num_index++] = val;
50567 }
50568 }
50569 } else
50570 if (string_get_month(sp, &p, &fields[1])) {
50571 has_mon = TRUE;
50572 string_skip_until(sp, &p, "0123456789 -/(");
50573 } else
50574 if (has_time && string_match(sp, &p, "PM")) {
50575 if (fields[3] < 12)
50576 fields[3] += 12;
50577 continue;
50578 } else
50579 if (has_time && string_match(sp, &p, "AM")) {
50580 if (fields[3] == 12)
50581 fields[3] -= 12;
50582 continue;
50583 } else
50584 if (string_get_tzabbr(sp, &p, &fields[8])) {
50585 *is_local = FALSE;
50586 continue;
50587 } else
50588 if (c == '(') { /* skip parenthesized phrase */
50589 int level = 0;
50590 while ((c = sp[p]) != '\0') {
50591 p++;
50592 level += (c == '(');
50593 level -= (c == ')');
50594 if (!level)
50595 break;
50596 }
50597 if (level > 0)
50598 return FALSE;
50599 } else
50600 if (c == ')') {
50601 return FALSE;
50602 } else {
50603 if (has_year + has_mon + has_time + num_index)
50604 return FALSE;
50605 /* skip a word */
50606 string_skip_until(sp, &p, " -/(");
50607 }
50608 string_skip_separators(sp, &p);
50609 }
50610 if (num_index + has_year + has_mon > 3)
50611 return FALSE;
50612
50613 switch (num_index) {
50614 case 0:
50615 if (!has_year)
50616 return FALSE;
50617 break;
50618 case 1:
50619 if (has_mon)
50620 fields[2] = num[0];
50621 else
50622 fields[1] = num[0];
50623 break;
50624 case 2:
50625 if (has_year) {
50626 fields[1] = num[0];
50627 fields[2] = num[1];
50628 } else
50629 if (has_mon) {
50630 fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100;
50631 fields[2] = num[0];
50632 } else {
50633 fields[1] = num[0];
50634 fields[2] = num[1];
50635 }
50636 break;
50637 case 3:
50638 fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100;
50639 fields[1] = num[0];
50640 fields[2] = num[1];
50641 break;
50642 default:
50643 return FALSE;
50644 }
50645 if (fields[1] < 1 || fields[2] < 1)
50646 return FALSE;
50647 fields[1] -= 1;
50648 return TRUE;
50649}
50650
50651static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
50652 int argc, JSValueConst *argv)
50653{
50654 JSValue s, rv;
50655 int fields[9];
50656 double fields1[9];
50657 double d;
50658 int i, c;
50659 JSString *sp;
50660 uint8_t buf[128];
50661 BOOL is_local;
50662
50663 rv = JS_NAN;
50664
50665 s = JS_ToString(ctx, argv[0]);
50666 if (JS_IsException(s))
50667 return JS_EXCEPTION;
50668
50669 sp = JS_VALUE_GET_STRING(s);
50670 /* convert the string as a byte array */
50671 for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) {
50672 c = string_get(sp, i);
50673 if (c > 255)
50674 c = (c == 0x2212) ? '-' : 'x';
50675 buf[i] = c;
50676 }
50677 buf[i] = '\0';
50678 if (js_date_parse_isostring(buf, fields, &is_local)
50679 || js_date_parse_otherstring(buf, fields, &is_local)) {
50680 static int const field_max[6] = { 0, 11, 31, 24, 59, 59 };
50681 BOOL valid = TRUE;
50682 /* check field maximum values */
50683 for (i = 1; i < 6; i++) {
50684 if (fields[i] > field_max[i])
50685 valid = FALSE;
50686 }
50687 /* special case 24:00:00.000 */
50688 if (fields[3] == 24 && (fields[4] | fields[5] | fields[6]))
50689 valid = FALSE;
50690 if (valid) {
50691 for(i = 0; i < 7; i++)
50692 fields1[i] = fields[i];
50693 d = set_date_fields(fields1, is_local) - fields[8] * 60000;
50694 rv = JS_NewFloat64(ctx, d);
50695 }
50696 }
50697 JS_FreeValue(ctx, s);
50698 return rv;
50699}
50700
50701static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
50702 int argc, JSValueConst *argv)
50703{
50704 // now()
50705 return JS_NewInt64(ctx, date_now());
50706}
50707
50708static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
50709 int argc, JSValueConst *argv)
50710{
50711 // Symbol_toPrimitive(hint)
50712 JSValueConst obj = this_val;
50713 JSAtom hint = JS_ATOM_NULL;
50714 int hint_num;
50715
50716 if (!JS_IsObject(obj))
50717 return JS_ThrowTypeErrorNotAnObject(ctx);
50718
50719 if (JS_IsString(argv[0])) {
50720 hint = JS_ValueToAtom(ctx, argv[0]);
50721 if (hint == JS_ATOM_NULL)
50722 return JS_EXCEPTION;
50723 JS_FreeAtom(ctx, hint);
50724 }
50725 switch (hint) {
50726 case JS_ATOM_number:
50727 case JS_ATOM_integer:
50728 hint_num = HINT_NUMBER;
50729 break;
50730 case JS_ATOM_string:
50731 case JS_ATOM_default:
50732 hint_num = HINT_STRING;
50733 break;
50734 default:
50735 return JS_ThrowTypeError(ctx, "invalid hint");
50736 }
50737 return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
50738}
50739
50740static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
50741 int argc, JSValueConst *argv)
50742{
50743 // getTimezoneOffset()
50744 double v;
50745
50746 if (JS_ThisTimeValue(ctx, &v, this_val))
50747 return JS_EXCEPTION;
50748 if (isnan(v))
50749 return JS_NAN;
50750 else
50751 /* assuming -8.64e15 <= v <= -8.64e15 */
50752 return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
50753}
50754
50755static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
50756 int argc, JSValueConst *argv)
50757{
50758 // getTime()
50759 double v;
50760
50761 if (JS_ThisTimeValue(ctx, &v, this_val))
50762 return JS_EXCEPTION;
50763 return JS_NewFloat64(ctx, v);
50764}
50765
50766static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
50767 int argc, JSValueConst *argv)
50768{
50769 // setTime(v)
50770 double v;
50771
50772 if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
50773 return JS_EXCEPTION;
50774 return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
50775}
50776
50777static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
50778 int argc, JSValueConst *argv)
50779{
50780 // setYear(y)
50781 double y;
50782 JSValueConst args[1];
50783
50784 if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
50785 return JS_EXCEPTION;
50786 y = +y;
50787 if (isfinite(y)) {
50788 y = trunc(y);
50789 if (y >= 0 && y < 100)
50790 y += 1900;
50791 }
50792 args[0] = JS_NewFloat64(ctx, y);
50793 return set_date_field(ctx, this_val, 1, args, 0x011);
50794}
50795
50796static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
50797 int argc, JSValueConst *argv)
50798{
50799 // toJSON(key)
50800 JSValue obj, tv, method, rv;
50801 double d;
50802
50803 rv = JS_EXCEPTION;
50804 tv = JS_UNDEFINED;
50805
50806 obj = JS_ToObject(ctx, this_val);
50807 tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
50808 if (JS_IsException(tv))
50809 goto exception;
50810 if (JS_IsNumber(tv)) {
50811 if (JS_ToFloat64(ctx, &d, tv) < 0)
50812 goto exception;
50813 if (!isfinite(d)) {
50814 rv = JS_NULL;
50815 goto done;
50816 }
50817 }
50818 method = JS_GetPropertyStr(ctx, obj, "toISOString");
50819 if (JS_IsException(method))
50820 goto exception;
50821 if (!JS_IsFunction(ctx, method)) {
50822 JS_ThrowTypeError(ctx, "object needs toISOString method");
50823 JS_FreeValue(ctx, method);
50824 goto exception;
50825 }
50826 rv = JS_CallFree(ctx, method, obj, 0, NULL);
50827exception:
50828done:
50829 JS_FreeValue(ctx, obj);
50830 JS_FreeValue(ctx, tv);
50831 return rv;
50832}
50833
50834static const JSCFunctionListEntry js_date_funcs[] = {
50835 JS_CFUNC_DEF("now", 0, js_Date_now ),
50836 JS_CFUNC_DEF("parse", 1, js_Date_parse ),
50837 JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
50838};
50839
50840static const JSCFunctionListEntry js_date_proto_funcs[] = {
50841 JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
50842 JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
50843 JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
50844 JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
50845 JS_ALIAS_DEF("toGMTString", "toUTCString" ),
50846 JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
50847 JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
50848 JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
50849 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
50850 JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
50851 JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
50852 JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
50853 JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
50854 JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
50855 JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
50856 JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
50857 JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
50858 JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
50859 JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
50860 JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
50861 JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
50862 JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
50863 JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
50864 JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
50865 JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
50866 JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
50867 JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
50868 JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
50869 JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
50870 JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
50871 JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
50872 JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
50873 JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
50874 JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
50875 JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
50876 JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
50877 JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
50878 JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
50879 JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
50880 JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
50881 JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
50882 JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
50883 JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
50884 JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
50885 JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
50886 JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
50887 JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
50888};
50889
50890JSValue JS_NewDate(JSContext *ctx, double epoch_ms)
50891{
50892 JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE);
50893 if (JS_IsException(obj))
50894 return JS_EXCEPTION;
50895 JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms)));
50896 return obj;
50897}
50898
50899void JS_AddIntrinsicDate(JSContext *ctx)
50900{
50901 JSValueConst obj;
50902
50903 /* Date */
50904 ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
50905 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
50906 countof(js_date_proto_funcs));
50907 obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
50908 ctx->class_proto[JS_CLASS_DATE]);
50909 JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
50910}
50911
50912/* eval */
50913
50914void JS_AddIntrinsicEval(JSContext *ctx)
50915{
50916 ctx->eval_internal = __JS_EvalInternal;
50917}
50918
50919/* BigInt */
50920
50921static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
50922{
50923 uint32_t tag;
50924
50925 redo:
50926 tag = JS_VALUE_GET_NORM_TAG(val);
50927 switch(tag) {
50928 case JS_TAG_INT:
50929 case JS_TAG_BOOL:
50930 val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
50931 break;
50932 case JS_TAG_SHORT_BIG_INT:
50933 case JS_TAG_BIG_INT:
50934 break;
50935 case JS_TAG_FLOAT64:
50936 {
50937 double d = JS_VALUE_GET_FLOAT64(val);
50938 JSBigInt *r;
50939 int res;
50940 r = js_bigint_from_float64(ctx, &res, d);
50941 if (!r) {
50942 if (res == 0) {
50943 val = JS_EXCEPTION;
50944 } else if (res == 1) {
50945 val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer");
50946 } else {
50947 val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); }
50948 } else {
50949 val = JS_CompactBigInt(ctx, r);
50950 }
50951 }
50952 break;
50953 case JS_TAG_STRING:
50954 case JS_TAG_STRING_ROPE:
50955 val = JS_StringToBigIntErr(ctx, val);
50956 break;
50957 case JS_TAG_OBJECT:
50958 val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
50959 if (JS_IsException(val))
50960 break;
50961 goto redo;
50962 case JS_TAG_NULL:
50963 case JS_TAG_UNDEFINED:
50964 default:
50965 JS_FreeValue(ctx, val);
50966 return JS_ThrowTypeError(ctx, "cannot convert to BigInt");
50967 }
50968 return val;
50969}
50970
50971static JSValue js_bigint_constructor(JSContext *ctx,
50972 JSValueConst new_target,
50973 int argc, JSValueConst *argv)
50974{
50975 if (!JS_IsUndefined(new_target))
50976 return JS_ThrowTypeError(ctx, "not a constructor");
50977 return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
50978}
50979
50980static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
50981{
50982 if (JS_IsBigInt(ctx, this_val))
50983 return JS_DupValue(ctx, this_val);
50984
50985 if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
50986 JSObject *p = JS_VALUE_GET_OBJ(this_val);
50987 if (p->class_id == JS_CLASS_BIG_INT) {
50988 if (JS_IsBigInt(ctx, p->u.object_data))
50989 return JS_DupValue(ctx, p->u.object_data);
50990 }
50991 }
50992 return JS_ThrowTypeError(ctx, "not a BigInt");
50993}
50994
50995static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
50996 int argc, JSValueConst *argv)
50997{
50998 JSValue val;
50999 int base;
51000 JSValue ret;
51001
51002 val = js_thisBigIntValue(ctx, this_val);
51003 if (JS_IsException(val))
51004 return val;
51005 if (argc == 0 || JS_IsUndefined(argv[0])) {
51006 base = 10;
51007 } else {
51008 base = js_get_radix(ctx, argv[0]);
51009 if (base < 0)
51010 goto fail;
51011 }
51012 ret = js_bigint_to_string1(ctx, val, base);
51013 JS_FreeValue(ctx, val);
51014 return ret;
51015 fail:
51016 JS_FreeValue(ctx, val);
51017 return JS_EXCEPTION;
51018}
51019
51020static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
51021 int argc, JSValueConst *argv)
51022{
51023 return js_thisBigIntValue(ctx, this_val);
51024}
51025
51026static JSValue js_bigint_asUintN(JSContext *ctx,
51027 JSValueConst this_val,
51028 int argc, JSValueConst *argv, int asIntN)
51029{
51030 uint64_t bits;
51031 JSValue res, a;
51032
51033 if (JS_ToIndex(ctx, &bits, argv[0]))
51034 return JS_EXCEPTION;
51035 a = JS_ToBigInt(ctx, argv[1]);
51036 if (JS_IsException(a))
51037 return JS_EXCEPTION;
51038 if (bits == 0) {
51039 JS_FreeValue(ctx, a);
51040 res = __JS_NewShortBigInt(ctx, 0);
51041 } else if (JS_VALUE_GET_TAG(a) == JS_TAG_SHORT_BIG_INT) {
51042 /* fast case */
51043 if (bits >= JS_SHORT_BIG_INT_BITS) {
51044 res = a;
51045 } else {
51046 uint64_t v;
51047 int shift;
51048 shift = 64 - bits;
51049 v = JS_VALUE_GET_SHORT_BIG_INT(a);
51050 v = v << shift;
51051 if (asIntN)
51052 v = (int64_t)v >> shift;
51053 else
51054 v = v >> shift;
51055 res = __JS_NewShortBigInt(ctx, v);
51056 }
51057 } else {
51058 JSBigInt *r, *p = JS_VALUE_GET_PTR(a);
51059 if (bits >= p->len * JS_LIMB_BITS) {
51060 res = a;
51061 } else {
51062 int len, shift, i;
51063 js_limb_t v;
51064 len = (bits + JS_LIMB_BITS - 1) / JS_LIMB_BITS;
51065 r = js_bigint_new(ctx, len);
51066 if (!r) {
51067 JS_FreeValue(ctx, a);
51068 return JS_EXCEPTION;
51069 }
51070 r->len = len;
51071 for(i = 0; i < len - 1; i++)
51072 r->tab[i] = p->tab[i];
51073 shift = (-bits) & (JS_LIMB_BITS - 1);
51074 /* 0 <= shift <= JS_LIMB_BITS - 1 */
51075 v = p->tab[len - 1] << shift;
51076 if (asIntN)
51077 v = (js_slimb_t)v >> shift;
51078 else
51079 v = v >> shift;
51080 r->tab[len - 1] = v;
51081 r = js_bigint_normalize(ctx, r);
51082 JS_FreeValue(ctx, a);
51083 res = JS_CompactBigInt(ctx, r);
51084 }
51085 }
51086 return res;
51087}
51088
51089static const JSCFunctionListEntry js_bigint_funcs[] = {
51090 JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
51091 JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
51092};
51093
51094static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
51095 JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
51096 JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
51097 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
51098};
51099
51100static void JS_AddIntrinsicBigInt(JSContext *ctx)
51101{
51102 JSValueConst obj1;
51103
51104 ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
51105 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
51106 js_bigint_proto_funcs,
51107 countof(js_bigint_proto_funcs));
51108 obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
51109 ctx->class_proto[JS_CLASS_BIG_INT]);
51110 JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
51111 countof(js_bigint_funcs));
51112}
51113
51114static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
51115 "EvalError", "RangeError", "ReferenceError",
51116 "SyntaxError", "TypeError", "URIError",
51117 "InternalError", "AggregateError",
51118};
51119
51120/* Minimum amount of objects to be able to compile code and display
51121 error messages. No JSAtom should be allocated by this function. */
51122static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
51123{
51124 JSValue proto;
51125 int i;
51126
51127 ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
51128 ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
51129 JS_CFUNC_generic, 0,
51130 ctx->class_proto[JS_CLASS_OBJECT]);
51131 ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
51132 ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
51133#if 0
51134 /* these are auto-initialized from js_error_proto_funcs,
51135 but delaying might be a problem */
51136 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
51137 JS_AtomToString(ctx, JS_ATOM_Error),
51138 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51139 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
51140 JS_AtomToString(ctx, JS_ATOM_empty_string),
51141 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51142#endif
51143 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
51144 js_error_proto_funcs,
51145 countof(js_error_proto_funcs));
51146
51147 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
51148 proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
51149 JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
51150 JS_NewAtomString(ctx, native_error_name[i]),
51151 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51152 JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
51153 JS_AtomToString(ctx, JS_ATOM_empty_string),
51154 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51155 ctx->native_error_proto[i] = proto;
51156 }
51157
51158 /* the array prototype is an array */
51159 ctx->class_proto[JS_CLASS_ARRAY] =
51160 JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51161 JS_CLASS_ARRAY);
51162
51163 ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
51164 JS_PROP_INITIAL_HASH_SIZE, 1);
51165 add_shape_property(ctx, &ctx->array_shape, NULL,
51166 JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
51167
51168 /* XXX: could test it on first context creation to ensure that no
51169 new atoms are created in JS_AddIntrinsicBasicObjects(). It is
51170 necessary to avoid useless renumbering of atoms after
51171 JS_EvalBinary() if it is done just after
51172 JS_AddIntrinsicBasicObjects(). */
51173 // assert(ctx->rt->atom_count == JS_ATOM_END);
51174}
51175
51176void JS_AddIntrinsicBaseObjects(JSContext *ctx)
51177{
51178 int i;
51179 JSValueConst obj, number_obj;
51180 JSValue obj1;
51181
51182 ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
51183
51184 /* add caller and arguments properties to throw a TypeError */
51185 JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
51186 ctx->throw_type_error, ctx->throw_type_error,
51187 JS_PROP_HAS_GET | JS_PROP_HAS_SET |
51188 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
51189 JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
51190 ctx->throw_type_error, ctx->throw_type_error,
51191 JS_PROP_HAS_GET | JS_PROP_HAS_SET |
51192 JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
51193 JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
51194
51195 ctx->global_obj = JS_NewObject(ctx);
51196 ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
51197
51198 /* Object */
51199 obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
51200 ctx->class_proto[JS_CLASS_OBJECT]);
51201 JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
51202 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51203 js_object_proto_funcs, countof(js_object_proto_funcs));
51204
51205 /* Function */
51206 JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
51207 ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
51208 "Function", 1, JS_CFUNC_constructor_or_func_magic,
51209 JS_FUNC_NORMAL);
51210 JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
51211 ctx->function_proto);
51212
51213 /* Error */
51214 obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
51215 "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
51216 JS_NewGlobalCConstructor2(ctx, obj1,
51217 "Error", ctx->class_proto[JS_CLASS_ERROR]);
51218
51219 /* Used to squelch a -Wcast-function-type warning. */
51220 JSCFunctionType ft = { .generic_magic = js_error_constructor };
51221 for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
51222 JSValue func_obj;
51223 int n_args;
51224 n_args = 1 + (i == JS_AGGREGATE_ERROR);
51225 func_obj = JS_NewCFunction3(ctx, ft.generic,
51226 native_error_name[i], n_args,
51227 JS_CFUNC_constructor_or_func_magic, i, obj1);
51228 JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
51229 ctx->native_error_proto[i]);
51230 }
51231
51232 /* Iterator prototype */
51233 ctx->iterator_proto = JS_NewObject(ctx);
51234 JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
51235 js_iterator_proto_funcs,
51236 countof(js_iterator_proto_funcs));
51237
51238 /* Array */
51239 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
51240 js_array_proto_funcs,
51241 countof(js_array_proto_funcs));
51242
51243 obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
51244 ctx->class_proto[JS_CLASS_ARRAY]);
51245 ctx->array_ctor = JS_DupValue(ctx, obj);
51246 JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
51247 countof(js_array_funcs));
51248
51249 /* XXX: create auto_initializer */
51250 {
51251 /* initialize Array.prototype[Symbol.unscopables] */
51252 static const char unscopables[] =
51253 "at" "\0"
51254 "copyWithin" "\0"
51255 "entries" "\0"
51256 "fill" "\0"
51257 "find" "\0"
51258 "findIndex" "\0"
51259 "findLast" "\0"
51260 "findLastIndex" "\0"
51261 "flat" "\0"
51262 "flatMap" "\0"
51263 "includes" "\0"
51264 "keys" "\0"
51265 "toReversed" "\0"
51266 "toSorted" "\0"
51267 "toSpliced" "\0"
51268 "values" "\0";
51269 const char *p = unscopables;
51270 obj1 = JS_NewObjectProto(ctx, JS_NULL);
51271 for(p = unscopables; *p; p += strlen(p) + 1) {
51272 JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
51273 }
51274 JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
51275 JS_ATOM_Symbol_unscopables, obj1,
51276 JS_PROP_CONFIGURABLE);
51277 }
51278
51279 /* needed to initialize arguments[Symbol.iterator] */
51280 ctx->array_proto_values =
51281 JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
51282
51283 ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51284 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
51285 js_array_iterator_proto_funcs,
51286 countof(js_array_iterator_proto_funcs));
51287
51288 /* parseFloat and parseInteger must be defined before Number
51289 because of the Number.parseFloat and Number.parseInteger
51290 aliases */
51291 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
51292 countof(js_global_funcs));
51293
51294 /* Number */
51295 ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51296 JS_CLASS_NUMBER);
51297 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
51298 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
51299 js_number_proto_funcs,
51300 countof(js_number_proto_funcs));
51301 number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
51302 ctx->class_proto[JS_CLASS_NUMBER]);
51303 JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
51304
51305 /* Boolean */
51306 ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51307 JS_CLASS_BOOLEAN);
51308 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
51309 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
51310 countof(js_boolean_proto_funcs));
51311 JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
51312 ctx->class_proto[JS_CLASS_BOOLEAN]);
51313
51314 /* String */
51315 ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
51316 JS_CLASS_STRING);
51317 JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
51318 obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
51319 ctx->class_proto[JS_CLASS_STRING]);
51320 JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
51321 countof(js_string_funcs));
51322 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
51323 countof(js_string_proto_funcs));
51324
51325 ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51326 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
51327 js_string_iterator_proto_funcs,
51328 countof(js_string_iterator_proto_funcs));
51329
51330 /* Math: create as autoinit object */
51331 js_random_init(ctx);
51332 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
51333
51334 /* ES6 Reflect: create as autoinit object */
51335 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
51336
51337 /* ES6 Symbol */
51338 ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
51339 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
51340 countof(js_symbol_proto_funcs));
51341 obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
51342 ctx->class_proto[JS_CLASS_SYMBOL]);
51343 JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
51344 countof(js_symbol_funcs));
51345 for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
51346 char buf[ATOM_GET_STR_BUF_SIZE];
51347 const char *str, *p;
51348 str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
51349 /* skip "Symbol." */
51350 p = strchr(str, '.');
51351 if (p)
51352 str = p + 1;
51353 JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
51354 }
51355
51356 /* ES6 Generator */
51357 ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
51358 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
51359 js_generator_proto_funcs,
51360 countof(js_generator_proto_funcs));
51361
51362 ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
51363 obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
51364 "GeneratorFunction", 1,
51365 JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR,
51366 ctx->function_ctor);
51367 JS_SetPropertyFunctionList(ctx,
51368 ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51369 js_generator_function_proto_funcs,
51370 countof(js_generator_function_proto_funcs));
51371 JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51372 ctx->class_proto[JS_CLASS_GENERATOR],
51373 JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
51374 JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
51375 0, JS_PROP_CONFIGURABLE);
51376 JS_FreeValue(ctx, obj1);
51377
51378 /* global properties */
51379 ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
51380 JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
51381 JS_DupValue(ctx, ctx->eval_obj),
51382 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
51383
51384 JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
51385 JS_DupValue(ctx, ctx->global_obj),
51386 JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
51387
51388 /* BigInt */
51389 JS_AddIntrinsicBigInt(ctx);
51390}
51391
51392/* Typed Arrays */
51393
51394static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
51395 0, 0, 0, 1, 1, 2, 2,
51396 3, 3, /* BigInt64Array, BigUint64Array */
51397 2, 3
51398};
51399
51400static JSValue js_array_buffer_constructor3(JSContext *ctx,
51401 JSValueConst new_target,
51402 uint64_t len, JSClassID class_id,
51403 uint8_t *buf,
51404 JSFreeArrayBufferDataFunc *free_func,
51405 void *opaque, BOOL alloc_flag)
51406{
51407 JSRuntime *rt = ctx->rt;
51408 JSValue obj;
51409 JSArrayBuffer *abuf = NULL;
51410
51411 obj = js_create_from_ctor(ctx, new_target, class_id);
51412 if (JS_IsException(obj))
51413 return obj;
51414 /* XXX: we are currently limited to 2 GB */
51415 if (len > INT32_MAX) {
51416 JS_ThrowRangeError(ctx, "invalid array buffer length");
51417 goto fail;
51418 }
51419 abuf = js_malloc(ctx, sizeof(*abuf));
51420 if (!abuf)
51421 goto fail;
51422 abuf->byte_length = len;
51423 if (alloc_flag) {
51424 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
51425 rt->sab_funcs.sab_alloc) {
51426 abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
51427 max_int(len, 1));
51428 if (!abuf->data)
51429 goto fail;
51430 memset(abuf->data, 0, len);
51431 } else {
51432 /* the allocation must be done after the object creation */
51433 abuf->data = js_mallocz(ctx, max_int(len, 1));
51434 if (!abuf->data)
51435 goto fail;
51436 }
51437 } else {
51438 if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
51439 rt->sab_funcs.sab_dup) {
51440 rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
51441 }
51442 abuf->data = buf;
51443 }
51444 init_list_head(&abuf->array_list);
51445 abuf->detached = FALSE;
51446 abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
51447 abuf->opaque = opaque;
51448 abuf->free_func = free_func;
51449 if (alloc_flag && buf)
51450 memcpy(abuf->data, buf, len);
51451 JS_SetOpaque(obj, abuf);
51452 return obj;
51453 fail:
51454 JS_FreeValue(ctx, obj);
51455 js_free(ctx, abuf);
51456 return JS_EXCEPTION;
51457}
51458
51459static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
51460{
51461 js_free_rt(rt, ptr);
51462}
51463
51464static JSValue js_array_buffer_constructor2(JSContext *ctx,
51465 JSValueConst new_target,
51466 uint64_t len, JSClassID class_id)
51467{
51468 return js_array_buffer_constructor3(ctx, new_target, len, class_id,
51469 NULL, js_array_buffer_free, NULL,
51470 TRUE);
51471}
51472
51473static JSValue js_array_buffer_constructor1(JSContext *ctx,
51474 JSValueConst new_target,
51475 uint64_t len)
51476{
51477 return js_array_buffer_constructor2(ctx, new_target, len,
51478 JS_CLASS_ARRAY_BUFFER);
51479}
51480
51481JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
51482 JSFreeArrayBufferDataFunc *free_func, void *opaque,
51483 BOOL is_shared)
51484{
51485 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51486 is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
51487 buf, free_func, opaque, FALSE);
51488}
51489
51490/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
51491JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
51492{
51493 return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
51494 JS_CLASS_ARRAY_BUFFER,
51495 (uint8_t *)buf,
51496 js_array_buffer_free, NULL,
51497 TRUE);
51498}
51499
51500static JSValue js_array_buffer_constructor(JSContext *ctx,
51501 JSValueConst new_target,
51502 int argc, JSValueConst *argv)
51503{
51504 uint64_t len;
51505 if (JS_ToIndex(ctx, &len, argv[0]))
51506 return JS_EXCEPTION;
51507 return js_array_buffer_constructor1(ctx, new_target, len);
51508}
51509
51510static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
51511 JSValueConst new_target,
51512 int argc, JSValueConst *argv)
51513{
51514 uint64_t len;
51515 if (JS_ToIndex(ctx, &len, argv[0]))
51516 return JS_EXCEPTION;
51517 return js_array_buffer_constructor2(ctx, new_target, len,
51518 JS_CLASS_SHARED_ARRAY_BUFFER);
51519}
51520
51521/* also used for SharedArrayBuffer */
51522static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
51523{
51524 JSObject *p = JS_VALUE_GET_OBJ(val);
51525 JSArrayBuffer *abuf = p->u.array_buffer;
51526 struct list_head *el, *el1;
51527
51528 if (abuf) {
51529 /* The ArrayBuffer finalizer may be called before the typed
51530 array finalizers using it, so abuf->array_list is not
51531 necessarily empty. */
51532 list_for_each_safe(el, el1, &abuf->array_list) {
51533 JSTypedArray *ta;
51534 JSObject *p1;
51535
51536 ta = list_entry(el, JSTypedArray, link);
51537 ta->link.prev = NULL;
51538 ta->link.next = NULL;
51539 p1 = ta->obj;
51540 /* Note: the typed array length and offset fields are not modified */
51541 if (p1->class_id != JS_CLASS_DATAVIEW) {
51542 p1->u.array.count = 0;
51543 p1->u.array.u.ptr = NULL;
51544 }
51545 }
51546 if (abuf->shared && rt->sab_funcs.sab_free) {
51547 rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
51548 } else {
51549 if (abuf->free_func)
51550 abuf->free_func(rt, abuf->opaque, abuf->data);
51551 }
51552 js_free_rt(rt, abuf);
51553 }
51554}
51555
51556static JSValue js_array_buffer_isView(JSContext *ctx,
51557 JSValueConst this_val,
51558 int argc, JSValueConst *argv)
51559{
51560 JSObject *p;
51561 BOOL res;
51562 res = FALSE;
51563 if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
51564 p = JS_VALUE_GET_OBJ(argv[0]);
51565 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51566 p->class_id <= JS_CLASS_DATAVIEW) {
51567 res = TRUE;
51568 }
51569 }
51570 return JS_NewBool(ctx, res);
51571}
51572
51573static const JSCFunctionListEntry js_array_buffer_funcs[] = {
51574 JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
51575 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51576};
51577
51578static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
51579{
51580 return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
51581}
51582
51583static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
51584 JSValueConst this_val,
51585 int class_id)
51586{
51587 JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
51588 if (!abuf)
51589 return JS_EXCEPTION;
51590 /* return 0 if detached */
51591 return JS_NewUint32(ctx, abuf->byte_length);
51592}
51593
51594void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
51595{
51596 JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
51597 struct list_head *el;
51598
51599 if (!abuf || abuf->detached)
51600 return;
51601 if (abuf->free_func)
51602 abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
51603 abuf->data = NULL;
51604 abuf->byte_length = 0;
51605 abuf->detached = TRUE;
51606
51607 list_for_each(el, &abuf->array_list) {
51608 JSTypedArray *ta;
51609 JSObject *p;
51610
51611 ta = list_entry(el, JSTypedArray, link);
51612 p = ta->obj;
51613 /* Note: the typed array length and offset fields are not modified */
51614 if (p->class_id != JS_CLASS_DATAVIEW) {
51615 p->u.array.count = 0;
51616 p->u.array.u.ptr = NULL;
51617 }
51618 }
51619}
51620
51621/* get an ArrayBuffer or SharedArrayBuffer */
51622static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
51623{
51624 JSObject *p;
51625 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
51626 goto fail;
51627 p = JS_VALUE_GET_OBJ(obj);
51628 if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
51629 p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
51630 fail:
51631 JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
51632 return NULL;
51633 }
51634 return p->u.array_buffer;
51635}
51636
51637/* return NULL if exception. WARNING: any JS call can detach the
51638 buffer and render the returned pointer invalid */
51639uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
51640{
51641 JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
51642 if (!abuf)
51643 goto fail;
51644 if (abuf->detached) {
51645 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51646 goto fail;
51647 }
51648 *psize = abuf->byte_length;
51649 return abuf->data;
51650 fail:
51651 *psize = 0;
51652 return NULL;
51653}
51654
51655static JSValue js_array_buffer_slice(JSContext *ctx,
51656 JSValueConst this_val,
51657 int argc, JSValueConst *argv, int class_id)
51658{
51659 JSArrayBuffer *abuf, *new_abuf;
51660 int64_t len, start, end, new_len;
51661 JSValue ctor, new_obj;
51662
51663 abuf = JS_GetOpaque2(ctx, this_val, class_id);
51664 if (!abuf)
51665 return JS_EXCEPTION;
51666 if (abuf->detached)
51667 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51668 len = abuf->byte_length;
51669
51670 if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
51671 return JS_EXCEPTION;
51672
51673 end = len;
51674 if (!JS_IsUndefined(argv[1])) {
51675 if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
51676 return JS_EXCEPTION;
51677 }
51678 new_len = max_int64(end - start, 0);
51679 ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
51680 if (JS_IsException(ctor))
51681 return ctor;
51682 if (JS_IsUndefined(ctor)) {
51683 new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
51684 class_id);
51685 } else {
51686 JSValue args[1];
51687 args[0] = JS_NewInt64(ctx, new_len);
51688 new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
51689 JS_FreeValue(ctx, ctor);
51690 JS_FreeValue(ctx, args[0]);
51691 }
51692 if (JS_IsException(new_obj))
51693 return new_obj;
51694 new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
51695 if (!new_abuf)
51696 goto fail;
51697 if (js_same_value(ctx, new_obj, this_val)) {
51698 JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
51699 goto fail;
51700 }
51701 if (new_abuf->detached) {
51702 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51703 goto fail;
51704 }
51705 if (new_abuf->byte_length < new_len) {
51706 JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
51707 goto fail;
51708 }
51709 /* must test again because of side effects */
51710 if (abuf->detached) {
51711 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51712 goto fail;
51713 }
51714 memcpy(new_abuf->data, abuf->data + start, new_len);
51715 return new_obj;
51716 fail:
51717 JS_FreeValue(ctx, new_obj);
51718 return JS_EXCEPTION;
51719}
51720
51721static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
51722 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
51723 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
51724 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
51725};
51726
51727/* SharedArrayBuffer */
51728
51729static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
51730 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
51731};
51732
51733static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
51734 JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
51735 JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
51736 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
51737};
51738
51739static JSObject *get_typed_array(JSContext *ctx,
51740 JSValueConst this_val,
51741 int is_dataview)
51742{
51743 JSObject *p;
51744 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51745 goto fail;
51746 p = JS_VALUE_GET_OBJ(this_val);
51747 if (is_dataview) {
51748 if (p->class_id != JS_CLASS_DATAVIEW)
51749 goto fail;
51750 } else {
51751 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51752 p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
51753 fail:
51754 JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
51755 return NULL;
51756 }
51757 }
51758 return p;
51759}
51760
51761/* WARNING: 'p' must be a typed array */
51762static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
51763{
51764 JSTypedArray *ta = p->u.typed_array;
51765 JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
51766 /* XXX: could simplify test by ensuring that
51767 p->u.array.u.ptr is NULL iff it is detached */
51768 return abuf->detached;
51769}
51770
51771/* WARNING: 'p' must be a typed array. Works even if the array buffer
51772 is detached */
51773static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
51774{
51775 JSTypedArray *ta = p->u.typed_array;
51776 int size_log2 = typed_array_size_log2(p->class_id);
51777 return ta->length >> size_log2;
51778}
51779
51780static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
51781{
51782 JSObject *p;
51783 p = get_typed_array(ctx, this_val, 0);
51784 if (!p)
51785 return -1;
51786 if (typed_array_is_detached(ctx, p)) {
51787 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51788 return -1;
51789 }
51790 return 0;
51791}
51792
51793static JSValue js_typed_array_get_length(JSContext *ctx,
51794 JSValueConst this_val)
51795{
51796 JSObject *p;
51797 p = get_typed_array(ctx, this_val, 0);
51798 if (!p)
51799 return JS_EXCEPTION;
51800 return JS_NewInt32(ctx, p->u.array.count);
51801}
51802
51803static JSValue js_typed_array_get_buffer(JSContext *ctx,
51804 JSValueConst this_val, int is_dataview)
51805{
51806 JSObject *p;
51807 JSTypedArray *ta;
51808 p = get_typed_array(ctx, this_val, is_dataview);
51809 if (!p)
51810 return JS_EXCEPTION;
51811 ta = p->u.typed_array;
51812 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51813}
51814
51815static JSValue js_typed_array_get_byteLength(JSContext *ctx,
51816 JSValueConst this_val,
51817 int is_dataview)
51818{
51819 JSObject *p;
51820 JSTypedArray *ta;
51821 p = get_typed_array(ctx, this_val, is_dataview);
51822 if (!p)
51823 return JS_EXCEPTION;
51824 if (typed_array_is_detached(ctx, p)) {
51825 if (is_dataview) {
51826 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51827 } else {
51828 return JS_NewInt32(ctx, 0);
51829 }
51830 }
51831 ta = p->u.typed_array;
51832 return JS_NewInt32(ctx, ta->length);
51833}
51834
51835static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
51836 JSValueConst this_val,
51837 int is_dataview)
51838{
51839 JSObject *p;
51840 JSTypedArray *ta;
51841 p = get_typed_array(ctx, this_val, is_dataview);
51842 if (!p)
51843 return JS_EXCEPTION;
51844 if (typed_array_is_detached(ctx, p)) {
51845 if (is_dataview) {
51846 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51847 } else {
51848 return JS_NewInt32(ctx, 0);
51849 }
51850 }
51851 ta = p->u.typed_array;
51852 return JS_NewInt32(ctx, ta->offset);
51853}
51854
51855JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv,
51856 JSTypedArrayEnum type)
51857{
51858 if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64)
51859 return JS_ThrowRangeError(ctx, "invalid typed array type");
51860
51861 return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv,
51862 JS_CLASS_UINT8C_ARRAY + type);
51863}
51864
51865/* Return the buffer associated to the typed array or an exception if
51866 it is not a typed array or if the buffer is detached. pbyte_offset,
51867 pbyte_length or pbytes_per_element can be NULL. */
51868JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
51869 size_t *pbyte_offset,
51870 size_t *pbyte_length,
51871 size_t *pbytes_per_element)
51872{
51873 JSObject *p;
51874 JSTypedArray *ta;
51875 p = get_typed_array(ctx, obj, FALSE);
51876 if (!p)
51877 return JS_EXCEPTION;
51878 if (typed_array_is_detached(ctx, p))
51879 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51880 ta = p->u.typed_array;
51881 if (pbyte_offset)
51882 *pbyte_offset = ta->offset;
51883 if (pbyte_length)
51884 *pbyte_length = ta->length;
51885 if (pbytes_per_element) {
51886 *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
51887 }
51888 return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
51889}
51890
51891static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
51892 JSValueConst this_val)
51893{
51894 JSObject *p;
51895 if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
51896 return JS_UNDEFINED;
51897 p = JS_VALUE_GET_OBJ(this_val);
51898 if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51899 p->class_id <= JS_CLASS_FLOAT64_ARRAY))
51900 return JS_UNDEFINED;
51901 return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
51902}
51903
51904static JSValue js_typed_array_set_internal(JSContext *ctx,
51905 JSValueConst dst,
51906 JSValueConst src,
51907 JSValueConst off)
51908{
51909 JSObject *p;
51910 JSObject *src_p;
51911 uint32_t i;
51912 int64_t src_len, offset;
51913 JSValue val, src_obj = JS_UNDEFINED;
51914
51915 p = get_typed_array(ctx, dst, 0);
51916 if (!p)
51917 goto fail;
51918 if (JS_ToInt64Sat(ctx, &offset, off))
51919 goto fail;
51920 if (offset < 0)
51921 goto range_error;
51922 if (typed_array_is_detached(ctx, p)) {
51923 detached:
51924 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51925 goto fail;
51926 }
51927 src_obj = JS_ToObject(ctx, src);
51928 if (JS_IsException(src_obj))
51929 goto fail;
51930 src_p = JS_VALUE_GET_OBJ(src_obj);
51931 if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
51932 src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
51933 JSTypedArray *dest_ta = p->u.typed_array;
51934 JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
51935 JSTypedArray *src_ta = src_p->u.typed_array;
51936 JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
51937 int shift = typed_array_size_log2(p->class_id);
51938
51939 if (src_abuf->detached)
51940 goto detached;
51941
51942 src_len = src_p->u.array.count;
51943 if (offset > (int64_t)(p->u.array.count - src_len))
51944 goto range_error;
51945
51946 /* copying between typed objects */
51947 if (src_p->class_id == p->class_id) {
51948 /* same type, use memmove */
51949 memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
51950 src_abuf->data + src_ta->offset, src_len << shift);
51951 goto done;
51952 }
51953 if (dest_abuf->data == src_abuf->data) {
51954 /* copying between the same buffer using different types of mappings
51955 would require a temporary buffer */
51956 }
51957 /* otherwise, default behavior is slow but correct */
51958 } else {
51959 if (js_get_length64(ctx, &src_len, src_obj))
51960 goto fail;
51961 if (offset > (int64_t)(p->u.array.count - src_len)) {
51962 range_error:
51963 JS_ThrowRangeError(ctx, "invalid array length");
51964 goto fail;
51965 }
51966 }
51967 for(i = 0; i < src_len; i++) {
51968 val = JS_GetPropertyUint32(ctx, src_obj, i);
51969 if (JS_IsException(val))
51970 goto fail;
51971 if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
51972 goto fail;
51973 }
51974done:
51975 JS_FreeValue(ctx, src_obj);
51976 return JS_UNDEFINED;
51977fail:
51978 JS_FreeValue(ctx, src_obj);
51979 return JS_EXCEPTION;
51980}
51981
51982static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val,
51983 int argc, JSValueConst *argv)
51984{
51985 JSObject *p;
51986 int64_t idx, len;
51987
51988 p = get_typed_array(ctx, this_val, 0);
51989 if (!p)
51990 return JS_EXCEPTION;
51991
51992 if (typed_array_is_detached(ctx, p)) {
51993 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
51994 return JS_EXCEPTION;
51995 }
51996
51997 if (JS_ToInt64Sat(ctx, &idx, argv[0]))
51998 return JS_EXCEPTION;
51999
52000 len = p->u.array.count;
52001 if (idx < 0)
52002 idx = len + idx;
52003 if (idx < 0 || idx >= len)
52004 return JS_UNDEFINED;
52005 return JS_GetPropertyInt64(ctx, this_val, idx);
52006}
52007
52008static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val,
52009 int argc, JSValueConst *argv)
52010{
52011 JSValue arr, val;
52012 JSObject *p;
52013 int64_t idx, len;
52014
52015 p = get_typed_array(ctx, this_val, /*is_dataview*/0);
52016 if (!p)
52017 return JS_EXCEPTION;
52018 if (typed_array_is_detached(ctx, p))
52019 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52020
52021 if (JS_ToInt64Sat(ctx, &idx, argv[0]))
52022 return JS_EXCEPTION;
52023
52024 len = p->u.array.count;
52025 if (idx < 0)
52026 idx = len + idx;
52027
52028 val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER);
52029 if (JS_IsException(val))
52030 return JS_EXCEPTION;
52031
52032 if (typed_array_is_detached(ctx, p) || idx < 0 || idx >= len)
52033 return JS_ThrowRangeError(ctx, "invalid array index");
52034
52035 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
52036 p->class_id);
52037 if (JS_IsException(arr)) {
52038 JS_FreeValue(ctx, val);
52039 return JS_EXCEPTION;
52040 }
52041 if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) {
52042 JS_FreeValue(ctx, arr);
52043 return JS_EXCEPTION;
52044 }
52045 return arr;
52046}
52047
52048static JSValue js_typed_array_set(JSContext *ctx,
52049 JSValueConst this_val,
52050 int argc, JSValueConst *argv)
52051{
52052 JSValueConst offset = JS_UNDEFINED;
52053 if (argc > 1) {
52054 offset = argv[1];
52055 }
52056 return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
52057}
52058
52059static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
52060 int argc, JSValueConst *argv, int magic)
52061{
52062 if (validate_typed_array(ctx, this_val))
52063 return JS_EXCEPTION;
52064 return js_create_array_iterator(ctx, this_val, argc, argv, magic);
52065}
52066
52067/* return < 0 if exception */
52068static int js_typed_array_get_length_internal(JSContext *ctx,
52069 JSValueConst obj)
52070{
52071 JSObject *p;
52072 p = get_typed_array(ctx, obj, 0);
52073 if (!p)
52074 return -1;
52075 if (typed_array_is_detached(ctx, p)) {
52076 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52077 return -1;
52078 }
52079 return p->u.array.count;
52080}
52081
52082#if 0
52083/* validate a typed array and return its length */
52084static JSValue js_typed_array___getLength(JSContext *ctx,
52085 JSValueConst this_val,
52086 int argc, JSValueConst *argv)
52087{
52088 BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
52089
52090 if (ignore_detached) {
52091 return js_typed_array_get_length(ctx, argv[0]);
52092 } else {
52093 int len;
52094 len = js_typed_array_get_length_internal(ctx, argv[0]);
52095 if (len < 0)
52096 return JS_EXCEPTION;
52097 return JS_NewInt32(ctx, len);
52098 }
52099}
52100#endif
52101
52102static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
52103 int argc, JSValueConst *argv)
52104{
52105 JSValue ret;
52106 int new_len;
52107 int64_t len;
52108
52109 ret = JS_CallConstructor(ctx, ctor, argc, argv);
52110 if (JS_IsException(ret))
52111 return ret;
52112 /* validate the typed array */
52113 new_len = js_typed_array_get_length_internal(ctx, ret);
52114 if (new_len < 0)
52115 goto fail;
52116 if (argc == 1) {
52117 /* ensure that it is large enough */
52118 if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
52119 goto fail;
52120 if (new_len < len) {
52121 JS_ThrowTypeError(ctx, "TypedArray length is too small");
52122 fail:
52123 JS_FreeValue(ctx, ret);
52124 return JS_EXCEPTION;
52125 }
52126 }
52127 return ret;
52128}
52129
52130#if 0
52131static JSValue js_typed_array___create(JSContext *ctx,
52132 JSValueConst this_val,
52133 int argc, JSValueConst *argv)
52134{
52135 return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
52136}
52137#endif
52138
52139static JSValue js_typed_array___speciesCreate(JSContext *ctx,
52140 JSValueConst this_val,
52141 int argc, JSValueConst *argv)
52142{
52143 JSValueConst obj;
52144 JSObject *p;
52145 JSValue ctor, ret;
52146 int argc1;
52147
52148 obj = argv[0];
52149 p = get_typed_array(ctx, obj, 0);
52150 if (!p)
52151 return JS_EXCEPTION;
52152 ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
52153 if (JS_IsException(ctor))
52154 return ctor;
52155 argc1 = max_int(argc - 1, 0);
52156 if (JS_IsUndefined(ctor)) {
52157 ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
52158 p->class_id);
52159 } else {
52160 ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
52161 JS_FreeValue(ctx, ctor);
52162 }
52163 return ret;
52164}
52165
52166static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
52167 int argc, JSValueConst *argv)
52168{
52169 // from(items, mapfn = void 0, this_arg = void 0)
52170 JSValueConst items = argv[0], mapfn, this_arg;
52171 JSValueConst args[2];
52172 JSValue iter, arr, r, v, v2;
52173 int64_t k, len;
52174 int mapping;
52175
52176 mapping = FALSE;
52177 mapfn = JS_UNDEFINED;
52178 this_arg = JS_UNDEFINED;
52179 r = JS_UNDEFINED;
52180 arr = JS_UNDEFINED;
52181 iter = JS_UNDEFINED;
52182
52183 if (argc > 1) {
52184 mapfn = argv[1];
52185 if (!JS_IsUndefined(mapfn)) {
52186 if (check_function(ctx, mapfn))
52187 goto exception;
52188 mapping = 1;
52189 if (argc > 2)
52190 this_arg = argv[2];
52191 }
52192 }
52193 iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
52194 if (JS_IsException(iter))
52195 goto exception;
52196 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
52197 uint32_t len1;
52198 if (!JS_IsFunction(ctx, iter)) {
52199 JS_ThrowTypeError(ctx, "value is not iterable");
52200 goto exception;
52201 }
52202 arr = js_array_from_iterator(ctx, &len1, items, iter);
52203 if (JS_IsException(arr))
52204 goto exception;
52205 len = len1;
52206 } else {
52207 arr = JS_ToObject(ctx, items);
52208 if (JS_IsException(arr))
52209 goto exception;
52210 if (js_get_length64(ctx, &len, arr) < 0)
52211 goto exception;
52212 }
52213 v = JS_NewInt64(ctx, len);
52214 args[0] = v;
52215 r = js_typed_array_create(ctx, this_val, 1, args);
52216 JS_FreeValue(ctx, v);
52217 if (JS_IsException(r))
52218 goto exception;
52219 for(k = 0; k < len; k++) {
52220 v = JS_GetPropertyInt64(ctx, arr, k);
52221 if (JS_IsException(v))
52222 goto exception;
52223 if (mapping) {
52224 args[0] = v;
52225 args[1] = JS_NewInt32(ctx, k);
52226 v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
52227 JS_FreeValue(ctx, v);
52228 v = v2;
52229 if (JS_IsException(v))
52230 goto exception;
52231 }
52232 if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
52233 goto exception;
52234 }
52235 goto done;
52236 exception:
52237 JS_FreeValue(ctx, r);
52238 r = JS_EXCEPTION;
52239 done:
52240 JS_FreeValue(ctx, arr);
52241 JS_FreeValue(ctx, iter);
52242 return r;
52243}
52244
52245static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
52246 int argc, JSValueConst *argv)
52247{
52248 JSValue obj;
52249 JSValueConst args[1];
52250 int i;
52251
52252 args[0] = JS_NewInt32(ctx, argc);
52253 obj = js_typed_array_create(ctx, this_val, 1, args);
52254 if (JS_IsException(obj))
52255 return obj;
52256
52257 for(i = 0; i < argc; i++) {
52258 if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
52259 JS_FreeValue(ctx, obj);
52260 return JS_EXCEPTION;
52261 }
52262 }
52263 return obj;
52264}
52265
52266static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
52267 int argc, JSValueConst *argv)
52268{
52269 JSObject *p;
52270 int len, to, from, final, count, shift;
52271
52272 len = js_typed_array_get_length_internal(ctx, this_val);
52273 if (len < 0)
52274 return JS_EXCEPTION;
52275
52276 if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
52277 return JS_EXCEPTION;
52278
52279 if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
52280 return JS_EXCEPTION;
52281
52282 final = len;
52283 if (argc > 2 && !JS_IsUndefined(argv[2])) {
52284 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
52285 return JS_EXCEPTION;
52286 }
52287
52288 count = min_int(final - from, len - to);
52289 if (count > 0) {
52290 p = JS_VALUE_GET_OBJ(this_val);
52291 if (typed_array_is_detached(ctx, p))
52292 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52293 shift = typed_array_size_log2(p->class_id);
52294 memmove(p->u.array.u.uint8_ptr + (to << shift),
52295 p->u.array.u.uint8_ptr + (from << shift),
52296 count << shift);
52297 }
52298 return JS_DupValue(ctx, this_val);
52299}
52300
52301static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
52302 int argc, JSValueConst *argv)
52303{
52304 JSObject *p;
52305 int len, k, final, shift;
52306 uint64_t v64;
52307
52308 len = js_typed_array_get_length_internal(ctx, this_val);
52309 if (len < 0)
52310 return JS_EXCEPTION;
52311 p = JS_VALUE_GET_OBJ(this_val);
52312
52313 if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
52314 int32_t v;
52315 if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
52316 return JS_EXCEPTION;
52317 v64 = v;
52318 } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
52319 uint32_t v;
52320 if (JS_ToUint32(ctx, &v, argv[0]))
52321 return JS_EXCEPTION;
52322 v64 = v;
52323 } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
52324 if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
52325 return JS_EXCEPTION;
52326 } else {
52327 double d;
52328 if (JS_ToFloat64(ctx, &d, argv[0]))
52329 return JS_EXCEPTION;
52330 if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
52331 union {
52332 float f;
52333 uint32_t u32;
52334 } u;
52335 u.f = d;
52336 v64 = u.u32;
52337 } else {
52338 JSFloat64Union u;
52339 u.d = d;
52340 v64 = u.u64;
52341 }
52342 }
52343
52344 k = 0;
52345 if (argc > 1) {
52346 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
52347 return JS_EXCEPTION;
52348 }
52349
52350 final = len;
52351 if (argc > 2 && !JS_IsUndefined(argv[2])) {
52352 if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
52353 return JS_EXCEPTION;
52354 }
52355
52356 if (typed_array_is_detached(ctx, p))
52357 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
52358
52359 shift = typed_array_size_log2(p->class_id);
52360 switch(shift) {
52361 case 0:
52362 if (k < final) {
52363 memset(p->u.array.u.uint8_ptr + k, v64, final - k);
52364 }
52365 break;
52366 case 1:
52367 for(; k < final; k++) {
52368 p->u.array.u.uint16_ptr[k] = v64;
52369 }
52370 break;
52371 case 2:
52372 for(; k < final; k++) {
52373 p->u.array.u.uint32_ptr[k] = v64;
52374 }
52375 break;
52376 case 3:
52377 for(; k < final; k++) {
52378 p->u.array.u.uint64_ptr[k] = v64;
52379 }
52380 break;
52381 default:
52382 abort();
52383 }
52384 return JS_DupValue(ctx, this_val);
52385}
52386
52387static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
52388 int argc, JSValueConst *argv, int mode)
52389{
52390 JSValueConst func, this_arg;
52391 JSValueConst args[3];
52392 JSValue val, index_val, res;
52393 int len, k, end;
52394 int dir;
52395
52396 val = JS_UNDEFINED;
52397 len = js_typed_array_get_length_internal(ctx, this_val);
52398 if (len < 0)
52399 goto exception;
52400
52401 func = argv[0];
52402 if (check_function(ctx, func))
52403 goto exception;
52404
52405 this_arg = JS_UNDEFINED;
52406 if (argc > 1)
52407 this_arg = argv[1];
52408
52409 k = 0;
52410 dir = 1;
52411 end = len;
52412 if (mode == ArrayFindLast || mode == ArrayFindLastIndex) {
52413 k = len - 1;
52414 dir = -1;
52415 end = -1;
52416 }
52417
52418 for(; k != end; k += dir) {
52419 index_val = JS_NewInt32(ctx, k);
52420 val = JS_GetPropertyValue(ctx, this_val, index_val);
52421 if (JS_IsException(val))
52422 goto exception;
52423 args[0] = val;
52424 args[1] = index_val;
52425 args[2] = this_val;
52426 res = JS_Call(ctx, func, this_arg, 3, args);
52427 if (JS_IsException(res))
52428 goto exception;
52429 if (JS_ToBoolFree(ctx, res)) {
52430 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) {
52431 JS_FreeValue(ctx, val);
52432 return index_val;
52433 } else {
52434 return val;
52435 }
52436 }
52437 JS_FreeValue(ctx, val);
52438 }
52439 if (mode == ArrayFindIndex || mode == ArrayFindLastIndex)
52440 return JS_NewInt32(ctx, -1);
52441 else
52442 return JS_UNDEFINED;
52443
52444exception:
52445 JS_FreeValue(ctx, val);
52446 return JS_EXCEPTION;
52447}
52448
52449#define special_indexOf 0
52450#define special_lastIndexOf 1
52451#define special_includes -1
52452
52453static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
52454 int argc, JSValueConst *argv, int special)
52455{
52456 JSObject *p;
52457 int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
52458 int64_t v64;
52459 double d;
52460 float f;
52461
52462 len = js_typed_array_get_length_internal(ctx, this_val);
52463 if (len < 0)
52464 goto exception;
52465 if (len == 0)
52466 goto done;
52467
52468 if (special == special_lastIndexOf) {
52469 k = len - 1;
52470 if (argc > 1) {
52471 if (JS_ToFloat64(ctx, &d, argv[1]))
52472 goto exception;
52473 if (isnan(d)) {
52474 k = 0;
52475 } else {
52476 if (d >= 0) {
52477 if (d < k) {
52478 k = d;
52479 }
52480 } else {
52481 d += len;
52482 if (d < 0)
52483 goto done;
52484 k = d;
52485 }
52486 }
52487 }
52488 stop = -1;
52489 inc = -1;
52490 } else {
52491 k = 0;
52492 if (argc > 1) {
52493 if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
52494 goto exception;
52495 }
52496 stop = len;
52497 inc = 1;
52498 }
52499
52500 p = JS_VALUE_GET_OBJ(this_val);
52501 /* if the array was detached, no need to go further (but no
52502 exception is raised) */
52503 if (typed_array_is_detached(ctx, p)) {
52504 /* "includes" scans all the properties, so "undefined" can match */
52505 if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0)
52506 res = 0;
52507 goto done;
52508 }
52509
52510 is_bigint = 0;
52511 is_int = 0; /* avoid warning */
52512 v64 = 0; /* avoid warning */
52513 tag = JS_VALUE_GET_NORM_TAG(argv[0]);
52514 if (tag == JS_TAG_INT) {
52515 is_int = 1;
52516 v64 = JS_VALUE_GET_INT(argv[0]);
52517 d = v64;
52518 } else
52519 if (tag == JS_TAG_FLOAT64) {
52520 d = JS_VALUE_GET_FLOAT64(argv[0]);
52521 if (d >= INT64_MIN && d < 0x1p63) {
52522 v64 = d;
52523 is_int = (v64 == d);
52524 }
52525 } else if (tag == JS_TAG_BIG_INT || tag == JS_TAG_SHORT_BIG_INT) {
52526 JSBigIntBuf buf1;
52527 JSBigInt *p1;
52528 int sz = (64 / JS_LIMB_BITS);
52529 if (tag == JS_TAG_SHORT_BIG_INT)
52530 p1 = js_bigint_set_short(&buf1, argv[0]);
52531 else
52532 p1 = JS_VALUE_GET_PTR(argv[0]);
52533
52534 if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
52535 if (p1->len > sz)
52536 goto done; /* does not fit an int64 : cannot be found */
52537 } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
52538 if (js_bigint_sign(p1))
52539 goto done; /* v < 0 */
52540 if (p1->len <= sz) {
52541 /* OK */
52542 } else if (p1->len == sz + 1 && p1->tab[sz] == 0) {
52543 /* 2^63 <= v <= 2^64-1 */
52544 } else {
52545 goto done;
52546 }
52547 } else {
52548 goto done;
52549 }
52550 if (JS_ToBigInt64(ctx, &v64, argv[0]))
52551 goto exception;
52552 d = 0;
52553 is_bigint = 1;
52554 } else {
52555 goto done;
52556 }
52557
52558 switch (p->class_id) {
52559 case JS_CLASS_INT8_ARRAY:
52560 if (is_int && (int8_t)v64 == v64)
52561 goto scan8;
52562 break;
52563 case JS_CLASS_UINT8C_ARRAY:
52564 case JS_CLASS_UINT8_ARRAY:
52565 if (is_int && (uint8_t)v64 == v64) {
52566 const uint8_t *pv, *pp;
52567 uint16_t v;
52568 scan8:
52569 pv = p->u.array.u.uint8_ptr;
52570 v = v64;
52571 if (inc > 0) {
52572 pp = memchr(pv + k, v, len - k);
52573 if (pp)
52574 res = pp - pv;
52575 } else {
52576 for (; k != stop; k += inc) {
52577 if (pv[k] == v) {
52578 res = k;
52579 break;
52580 }
52581 }
52582 }
52583 }
52584 break;
52585 case JS_CLASS_INT16_ARRAY:
52586 if (is_int && (int16_t)v64 == v64)
52587 goto scan16;
52588 break;
52589 case JS_CLASS_UINT16_ARRAY:
52590 if (is_int && (uint16_t)v64 == v64) {
52591 const uint16_t *pv;
52592 uint16_t v;
52593 scan16:
52594 pv = p->u.array.u.uint16_ptr;
52595 v = v64;
52596 for (; k != stop; k += inc) {
52597 if (pv[k] == v) {
52598 res = k;
52599 break;
52600 }
52601 }
52602 }
52603 break;
52604 case JS_CLASS_INT32_ARRAY:
52605 if (is_int && (int32_t)v64 == v64)
52606 goto scan32;
52607 break;
52608 case JS_CLASS_UINT32_ARRAY:
52609 if (is_int && (uint32_t)v64 == v64) {
52610 const uint32_t *pv;
52611 uint32_t v;
52612 scan32:
52613 pv = p->u.array.u.uint32_ptr;
52614 v = v64;
52615 for (; k != stop; k += inc) {
52616 if (pv[k] == v) {
52617 res = k;
52618 break;
52619 }
52620 }
52621 }
52622 break;
52623 case JS_CLASS_FLOAT32_ARRAY:
52624 if (is_bigint)
52625 break;
52626 if (isnan(d)) {
52627 const float *pv = p->u.array.u.float_ptr;
52628 /* special case: indexOf returns -1, includes finds NaN */
52629 if (special != special_includes)
52630 goto done;
52631 for (; k != stop; k += inc) {
52632 if (isnan(pv[k])) {
52633 res = k;
52634 break;
52635 }
52636 }
52637 } else if ((f = (float)d) == d) {
52638 const float *pv = p->u.array.u.float_ptr;
52639 for (; k != stop; k += inc) {
52640 if (pv[k] == f) {
52641 res = k;
52642 break;
52643 }
52644 }
52645 }
52646 break;
52647 case JS_CLASS_FLOAT64_ARRAY:
52648 if (is_bigint)
52649 break;
52650 if (isnan(d)) {
52651 const double *pv = p->u.array.u.double_ptr;
52652 /* special case: indexOf returns -1, includes finds NaN */
52653 if (special != special_includes)
52654 goto done;
52655 for (; k != stop; k += inc) {
52656 if (isnan(pv[k])) {
52657 res = k;
52658 break;
52659 }
52660 }
52661 } else {
52662 const double *pv = p->u.array.u.double_ptr;
52663 for (; k != stop; k += inc) {
52664 if (pv[k] == d) {
52665 res = k;
52666 break;
52667 }
52668 }
52669 }
52670 break;
52671 case JS_CLASS_BIG_INT64_ARRAY:
52672 if (is_bigint) {
52673 goto scan64;
52674 }
52675 break;
52676 case JS_CLASS_BIG_UINT64_ARRAY:
52677 if (is_bigint) {
52678 const uint64_t *pv;
52679 uint64_t v;
52680 scan64:
52681 pv = p->u.array.u.uint64_ptr;
52682 v = v64;
52683 for (; k != stop; k += inc) {
52684 if (pv[k] == v) {
52685 res = k;
52686 break;
52687 }
52688 }
52689 }
52690 break;
52691 }
52692
52693done:
52694 if (special == special_includes)
52695 return JS_NewBool(ctx, res >= 0);
52696 else
52697 return JS_NewInt32(ctx, res);
52698
52699exception:
52700 return JS_EXCEPTION;
52701}
52702
52703static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
52704 int argc, JSValueConst *argv, int toLocaleString)
52705{
52706 JSValue sep = JS_UNDEFINED, el;
52707 StringBuffer b_s, *b = &b_s;
52708 JSString *p = NULL;
52709 int i, n;
52710 int c;
52711
52712 n = js_typed_array_get_length_internal(ctx, this_val);
52713 if (n < 0)
52714 goto exception;
52715
52716 c = ','; /* default separator */
52717 if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
52718 sep = JS_ToString(ctx, argv[0]);
52719 if (JS_IsException(sep))
52720 goto exception;
52721 p = JS_VALUE_GET_STRING(sep);
52722 if (p->len == 1 && !p->is_wide_char)
52723 c = p->u.str8[0];
52724 else
52725 c = -1;
52726 }
52727 string_buffer_init(ctx, b, 0);
52728
52729 /* XXX: optimize with direct access */
52730 for(i = 0; i < n; i++) {
52731 if (i > 0) {
52732 if (c >= 0) {
52733 if (string_buffer_putc8(b, c))
52734 goto fail;
52735 } else {
52736 if (string_buffer_concat(b, p, 0, p->len))
52737 goto fail;
52738 }
52739 }
52740 el = JS_GetPropertyUint32(ctx, this_val, i);
52741 /* Can return undefined for example if the typed array is detached */
52742 if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
52743 if (JS_IsException(el))
52744 goto fail;
52745 if (toLocaleString) {
52746 el = JS_ToLocaleStringFree(ctx, el);
52747 }
52748 if (string_buffer_concat_value_free(b, el))
52749 goto fail;
52750 }
52751 }
52752 JS_FreeValue(ctx, sep);
52753 return string_buffer_end(b);
52754
52755fail:
52756 string_buffer_free(b);
52757 JS_FreeValue(ctx, sep);
52758exception:
52759 return JS_EXCEPTION;
52760}
52761
52762static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
52763 int argc, JSValueConst *argv)
52764{
52765 JSObject *p;
52766 int len;
52767
52768 len = js_typed_array_get_length_internal(ctx, this_val);
52769 if (len < 0)
52770 return JS_EXCEPTION;
52771 if (len > 0) {
52772 p = JS_VALUE_GET_OBJ(this_val);
52773 switch (typed_array_size_log2(p->class_id)) {
52774 case 0:
52775 {
52776 uint8_t *p1 = p->u.array.u.uint8_ptr;
52777 uint8_t *p2 = p1 + len - 1;
52778 while (p1 < p2) {
52779 uint8_t v = *p1;
52780 *p1++ = *p2;
52781 *p2-- = v;
52782 }
52783 }
52784 break;
52785 case 1:
52786 {
52787 uint16_t *p1 = p->u.array.u.uint16_ptr;
52788 uint16_t *p2 = p1 + len - 1;
52789 while (p1 < p2) {
52790 uint16_t v = *p1;
52791 *p1++ = *p2;
52792 *p2-- = v;
52793 }
52794 }
52795 break;
52796 case 2:
52797 {
52798 uint32_t *p1 = p->u.array.u.uint32_ptr;
52799 uint32_t *p2 = p1 + len - 1;
52800 while (p1 < p2) {
52801 uint32_t v = *p1;
52802 *p1++ = *p2;
52803 *p2-- = v;
52804 }
52805 }
52806 break;
52807 case 3:
52808 {
52809 uint64_t *p1 = p->u.array.u.uint64_ptr;
52810 uint64_t *p2 = p1 + len - 1;
52811 while (p1 < p2) {
52812 uint64_t v = *p1;
52813 *p1++ = *p2;
52814 *p2-- = v;
52815 }
52816 }
52817 break;
52818 default:
52819 abort();
52820 }
52821 }
52822 return JS_DupValue(ctx, this_val);
52823}
52824
52825static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val,
52826 int argc, JSValueConst *argv)
52827{
52828 JSValue arr, ret;
52829 JSObject *p;
52830
52831 p = get_typed_array(ctx, this_val, /*is_dataview*/0);
52832 if (!p)
52833 return JS_EXCEPTION;
52834 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
52835 p->class_id);
52836 if (JS_IsException(arr))
52837 return JS_EXCEPTION;
52838 ret = js_typed_array_reverse(ctx, arr, argc, argv);
52839 JS_FreeValue(ctx, arr);
52840 return ret;
52841}
52842
52843static void slice_memcpy(uint8_t *dst, const uint8_t *src, size_t len)
52844{
52845 if (dst + len <= src || dst >= src + len) {
52846 /* no overlap: can use memcpy */
52847 memcpy(dst, src, len);
52848 } else {
52849 /* otherwise the spec mandates byte copy */
52850 while (len-- != 0)
52851 *dst++ = *src++;
52852 }
52853}
52854
52855static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
52856 int argc, JSValueConst *argv)
52857{
52858 JSValueConst args[2];
52859 JSValue arr, val;
52860 JSObject *p, *p1;
52861 int n, len, start, final, count, shift;
52862
52863 arr = JS_UNDEFINED;
52864 len = js_typed_array_get_length_internal(ctx, this_val);
52865 if (len < 0)
52866 goto exception;
52867
52868 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52869 goto exception;
52870 final = len;
52871 if (!JS_IsUndefined(argv[1])) {
52872 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52873 goto exception;
52874 }
52875 count = max_int(final - start, 0);
52876
52877 p = get_typed_array(ctx, this_val, 0);
52878 if (p == NULL)
52879 goto exception;
52880 shift = typed_array_size_log2(p->class_id);
52881
52882 args[0] = this_val;
52883 args[1] = JS_NewInt32(ctx, count);
52884 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
52885 if (JS_IsException(arr))
52886 goto exception;
52887
52888 if (count > 0) {
52889 if (validate_typed_array(ctx, this_val)
52890 || validate_typed_array(ctx, arr))
52891 goto exception;
52892
52893 p1 = get_typed_array(ctx, arr, 0);
52894 if (p1 != NULL && p->class_id == p1->class_id &&
52895 typed_array_get_length(ctx, p1) >= count &&
52896 typed_array_get_length(ctx, p) >= start + count) {
52897 slice_memcpy(p1->u.array.u.uint8_ptr,
52898 p->u.array.u.uint8_ptr + (start << shift),
52899 count << shift);
52900 } else {
52901 for (n = 0; n < count; n++) {
52902 val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
52903 if (JS_IsException(val))
52904 goto exception;
52905 if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
52906 JS_PROP_THROW) < 0)
52907 goto exception;
52908 }
52909 }
52910 }
52911 return arr;
52912
52913 exception:
52914 JS_FreeValue(ctx, arr);
52915 return JS_EXCEPTION;
52916}
52917
52918static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
52919 int argc, JSValueConst *argv)
52920{
52921 JSValueConst args[4];
52922 JSValue arr, byteOffset, ta_buffer;
52923 JSObject *p;
52924 int len, start, final, count, shift, offset;
52925
52926 p = get_typed_array(ctx, this_val, 0);
52927 if (!p)
52928 goto exception;
52929 len = p->u.array.count;
52930 if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
52931 goto exception;
52932
52933 final = len;
52934 if (!JS_IsUndefined(argv[1])) {
52935 if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
52936 goto exception;
52937 }
52938 count = max_int(final - start, 0);
52939 byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
52940 if (JS_IsException(byteOffset))
52941 goto exception;
52942 shift = typed_array_size_log2(p->class_id);
52943 offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
52944 JS_FreeValue(ctx, byteOffset);
52945 ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
52946 if (JS_IsException(ta_buffer))
52947 goto exception;
52948 args[0] = this_val;
52949 args[1] = ta_buffer;
52950 args[2] = JS_NewInt32(ctx, offset);
52951 args[3] = JS_NewInt32(ctx, count);
52952 arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
52953 JS_FreeValue(ctx, ta_buffer);
52954 return arr;
52955
52956 exception:
52957 return JS_EXCEPTION;
52958}
52959
52960/* TypedArray.prototype.sort */
52961
52962static int js_cmp_doubles(double x, double y)
52963{
52964 if (isnan(x)) return isnan(y) ? 0 : +1;
52965 if (isnan(y)) return -1;
52966 if (x < y) return -1;
52967 if (x > y) return 1;
52968 if (x != 0) return 0;
52969 if (signbit(x)) return signbit(y) ? 0 : -1;
52970 else return signbit(y) ? 1 : 0;
52971}
52972
52973static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
52974 return *(const int8_t *)a - *(const int8_t *)b;
52975}
52976
52977static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
52978 return *(const uint8_t *)a - *(const uint8_t *)b;
52979}
52980
52981static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
52982 return *(const int16_t *)a - *(const int16_t *)b;
52983}
52984
52985static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
52986 return *(const uint16_t *)a - *(const uint16_t *)b;
52987}
52988
52989static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
52990 int32_t x = *(const int32_t *)a;
52991 int32_t y = *(const int32_t *)b;
52992 return (y < x) - (y > x);
52993}
52994
52995static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
52996 uint32_t x = *(const uint32_t *)a;
52997 uint32_t y = *(const uint32_t *)b;
52998 return (y < x) - (y > x);
52999}
53000
53001static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
53002 int64_t x = *(const int64_t *)a;
53003 int64_t y = *(const int64_t *)b;
53004 return (y < x) - (y > x);
53005}
53006
53007static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
53008 uint64_t x = *(const uint64_t *)a;
53009 uint64_t y = *(const uint64_t *)b;
53010 return (y < x) - (y > x);
53011}
53012
53013static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
53014 return js_cmp_doubles(*(const float *)a, *(const float *)b);
53015}
53016
53017static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
53018 return js_cmp_doubles(*(const double *)a, *(const double *)b);
53019}
53020
53021static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
53022 return JS_NewInt32(ctx, *(const int8_t *)a);
53023}
53024
53025static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
53026 return JS_NewInt32(ctx, *(const uint8_t *)a);
53027}
53028
53029static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
53030 return JS_NewInt32(ctx, *(const int16_t *)a);
53031}
53032
53033static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
53034 return JS_NewInt32(ctx, *(const uint16_t *)a);
53035}
53036
53037static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
53038 return JS_NewInt32(ctx, *(const int32_t *)a);
53039}
53040
53041static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
53042 return JS_NewUint32(ctx, *(const uint32_t *)a);
53043}
53044
53045static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
53046 return JS_NewBigInt64(ctx, *(int64_t *)a);
53047}
53048
53049static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
53050 return JS_NewBigUint64(ctx, *(uint64_t *)a);
53051}
53052
53053static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
53054 return __JS_NewFloat64(ctx, *(const float *)a);
53055}
53056
53057static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
53058 return __JS_NewFloat64(ctx, *(const double *)a);
53059}
53060
53061struct TA_sort_context {
53062 JSContext *ctx;
53063 int exception; /* 1 = exception, 2 = detached typed array */
53064 JSValueConst arr;
53065 JSValueConst cmp;
53066 JSValue (*getfun)(JSContext *ctx, const void *a);
53067 uint8_t *array_ptr; /* cannot change unless the array is detached */
53068 int elt_size;
53069};
53070
53071static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
53072 struct TA_sort_context *psc = opaque;
53073 JSContext *ctx = psc->ctx;
53074 uint32_t a_idx, b_idx;
53075 JSValueConst argv[2];
53076 JSValue res;
53077 int cmp;
53078
53079 cmp = 0;
53080 if (!psc->exception) {
53081 /* Note: the typed array can be detached without causing an
53082 error */
53083 a_idx = *(uint32_t *)a;
53084 b_idx = *(uint32_t *)b;
53085 argv[0] = psc->getfun(ctx, psc->array_ptr +
53086 a_idx * (size_t)psc->elt_size);
53087 argv[1] = psc->getfun(ctx, psc->array_ptr +
53088 b_idx * (size_t)(psc->elt_size));
53089 res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
53090 if (JS_IsException(res)) {
53091 psc->exception = 1;
53092 goto done;
53093 }
53094 if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
53095 int val = JS_VALUE_GET_INT(res);
53096 cmp = (val > 0) - (val < 0);
53097 } else {
53098 double val;
53099 if (JS_ToFloat64Free(ctx, &val, res) < 0) {
53100 psc->exception = 1;
53101 goto done;
53102 } else {
53103 cmp = (val > 0) - (val < 0);
53104 }
53105 }
53106 if (cmp == 0) {
53107 /* make sort stable: compare array offsets */
53108 cmp = (a_idx > b_idx) - (a_idx < b_idx);
53109 }
53110 if (unlikely(typed_array_is_detached(ctx,
53111 JS_VALUE_GET_PTR(psc->arr)))) {
53112 psc->exception = 2;
53113 }
53114 done:
53115 JS_FreeValue(ctx, (JSValue)argv[0]);
53116 JS_FreeValue(ctx, (JSValue)argv[1]);
53117 }
53118 return cmp;
53119}
53120
53121static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
53122 int argc, JSValueConst *argv)
53123{
53124 JSObject *p;
53125 int len;
53126 size_t elt_size;
53127 struct TA_sort_context tsc;
53128 void *array_ptr;
53129 int (*cmpfun)(const void *a, const void *b, void *opaque);
53130
53131 tsc.ctx = ctx;
53132 tsc.exception = 0;
53133 tsc.arr = this_val;
53134 tsc.cmp = argv[0];
53135
53136 if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
53137 return JS_EXCEPTION;
53138 len = js_typed_array_get_length_internal(ctx, this_val);
53139 if (len < 0)
53140 return JS_EXCEPTION;
53141
53142 if (len > 1) {
53143 p = JS_VALUE_GET_OBJ(this_val);
53144 switch (p->class_id) {
53145 case JS_CLASS_INT8_ARRAY:
53146 tsc.getfun = js_TA_get_int8;
53147 cmpfun = js_TA_cmp_int8;
53148 break;
53149 case JS_CLASS_UINT8C_ARRAY:
53150 case JS_CLASS_UINT8_ARRAY:
53151 tsc.getfun = js_TA_get_uint8;
53152 cmpfun = js_TA_cmp_uint8;
53153 break;
53154 case JS_CLASS_INT16_ARRAY:
53155 tsc.getfun = js_TA_get_int16;
53156 cmpfun = js_TA_cmp_int16;
53157 break;
53158 case JS_CLASS_UINT16_ARRAY:
53159 tsc.getfun = js_TA_get_uint16;
53160 cmpfun = js_TA_cmp_uint16;
53161 break;
53162 case JS_CLASS_INT32_ARRAY:
53163 tsc.getfun = js_TA_get_int32;
53164 cmpfun = js_TA_cmp_int32;
53165 break;
53166 case JS_CLASS_UINT32_ARRAY:
53167 tsc.getfun = js_TA_get_uint32;
53168 cmpfun = js_TA_cmp_uint32;
53169 break;
53170 case JS_CLASS_BIG_INT64_ARRAY:
53171 tsc.getfun = js_TA_get_int64;
53172 cmpfun = js_TA_cmp_int64;
53173 break;
53174 case JS_CLASS_BIG_UINT64_ARRAY:
53175 tsc.getfun = js_TA_get_uint64;
53176 cmpfun = js_TA_cmp_uint64;
53177 break;
53178 case JS_CLASS_FLOAT32_ARRAY:
53179 tsc.getfun = js_TA_get_float32;
53180 cmpfun = js_TA_cmp_float32;
53181 break;
53182 case JS_CLASS_FLOAT64_ARRAY:
53183 tsc.getfun = js_TA_get_float64;
53184 cmpfun = js_TA_cmp_float64;
53185 break;
53186 default:
53187 abort();
53188 }
53189 array_ptr = p->u.array.u.ptr;
53190 elt_size = 1 << typed_array_size_log2(p->class_id);
53191 if (!JS_IsUndefined(tsc.cmp)) {
53192 uint32_t *array_idx;
53193 void *array_tmp;
53194 size_t i, j;
53195
53196 /* XXX: a stable sort would use less memory */
53197 array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
53198 if (!array_idx)
53199 return JS_EXCEPTION;
53200 for(i = 0; i < len; i++)
53201 array_idx[i] = i;
53202 tsc.array_ptr = array_ptr;
53203 tsc.elt_size = elt_size;
53204 rqsort(array_idx, len, sizeof(array_idx[0]),
53205 js_TA_cmp_generic, &tsc);
53206 if (tsc.exception) {
53207 if (tsc.exception == 1)
53208 goto fail;
53209 /* detached typed array during the sort: no error */
53210 } else {
53211 array_tmp = js_malloc(ctx, len * elt_size);
53212 if (!array_tmp) {
53213 fail:
53214 js_free(ctx, array_idx);
53215 return JS_EXCEPTION;
53216 }
53217 memcpy(array_tmp, array_ptr, len * elt_size);
53218 switch(elt_size) {
53219 case 1:
53220 for(i = 0; i < len; i++) {
53221 j = array_idx[i];
53222 ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
53223 }
53224 break;
53225 case 2:
53226 for(i = 0; i < len; i++) {
53227 j = array_idx[i];
53228 ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
53229 }
53230 break;
53231 case 4:
53232 for(i = 0; i < len; i++) {
53233 j = array_idx[i];
53234 ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
53235 }
53236 break;
53237 case 8:
53238 for(i = 0; i < len; i++) {
53239 j = array_idx[i];
53240 ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
53241 }
53242 break;
53243 default:
53244 abort();
53245 }
53246 js_free(ctx, array_tmp);
53247 }
53248 js_free(ctx, array_idx);
53249 } else {
53250 rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
53251 if (tsc.exception)
53252 return JS_EXCEPTION;
53253 }
53254 }
53255 return JS_DupValue(ctx, this_val);
53256}
53257
53258static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val,
53259 int argc, JSValueConst *argv)
53260{
53261 JSValue arr, ret;
53262 JSObject *p;
53263
53264 p = get_typed_array(ctx, this_val, /*is_dataview*/0);
53265 if (!p)
53266 return JS_EXCEPTION;
53267 arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val,
53268 p->class_id);
53269 if (JS_IsException(arr))
53270 return JS_EXCEPTION;
53271 ret = js_typed_array_sort(ctx, arr, argc, argv);
53272 JS_FreeValue(ctx, arr);
53273 return ret;
53274}
53275
53276static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
53277 JS_CFUNC_DEF("from", 1, js_typed_array_from ),
53278 JS_CFUNC_DEF("of", 0, js_typed_array_of ),
53279 JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
53280 //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
53281 //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
53282 //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
53283};
53284
53285static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
53286 JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
53287 JS_CFUNC_DEF("at", 1, js_typed_array_at ),
53288 JS_CFUNC_DEF("with", 2, js_typed_array_with ),
53289 JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
53290 JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
53291 JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
53292 JS_CFUNC_DEF("set", 1, js_typed_array_set ),
53293 JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
53294 JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
53295 JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
53296 JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
53297 JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
53298 JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
53299 JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
53300 JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
53301 JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
53302 JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
53303 JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
53304 JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
53305 JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
53306 JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
53307 JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ),
53308 JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ),
53309 JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ),
53310 JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ),
53311 JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
53312 JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ),
53313 JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
53314 JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
53315 JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
53316 JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ),
53317 JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
53318 JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
53319 JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
53320 JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
53321 JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
53322 //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
53323};
53324
53325static JSValue js_typed_array_base_constructor(JSContext *ctx,
53326 JSValueConst this_val,
53327 int argc, JSValueConst *argv)
53328{
53329 return JS_ThrowTypeError(ctx, "cannot be called");
53330}
53331
53332/* 'obj' must be an allocated typed array object */
53333static int typed_array_init(JSContext *ctx, JSValueConst obj,
53334 JSValue buffer, uint64_t offset, uint64_t len)
53335{
53336 JSTypedArray *ta;
53337 JSObject *p, *pbuffer;
53338 JSArrayBuffer *abuf;
53339 int size_log2;
53340
53341 p = JS_VALUE_GET_OBJ(obj);
53342 size_log2 = typed_array_size_log2(p->class_id);
53343 ta = js_malloc(ctx, sizeof(*ta));
53344 if (!ta) {
53345 JS_FreeValue(ctx, buffer);
53346 return -1;
53347 }
53348 pbuffer = JS_VALUE_GET_OBJ(buffer);
53349 abuf = pbuffer->u.array_buffer;
53350 ta->obj = p;
53351 ta->buffer = pbuffer;
53352 ta->offset = offset;
53353 ta->length = len << size_log2;
53354 list_add_tail(&ta->link, &abuf->array_list);
53355 p->u.typed_array = ta;
53356 p->u.array.count = len;
53357 p->u.array.u.ptr = abuf->data + offset;
53358 return 0;
53359}
53360
53361
53362static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
53363 JSValueConst obj, JSValueConst method)
53364{
53365 JSValue arr, iter, next_method = JS_UNDEFINED, val;
53366 BOOL done;
53367 uint32_t k;
53368
53369 *plen = 0;
53370 arr = JS_NewArray(ctx);
53371 if (JS_IsException(arr))
53372 return arr;
53373 iter = JS_GetIterator2(ctx, obj, method);
53374 if (JS_IsException(iter))
53375 goto fail;
53376 next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
53377 if (JS_IsException(next_method))
53378 goto fail;
53379 k = 0;
53380 for(;;) {
53381 val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
53382 if (JS_IsException(val))
53383 goto fail;
53384 if (done)
53385 break;
53386 if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
53387 goto fail;
53388 k++;
53389 }
53390 JS_FreeValue(ctx, next_method);
53391 JS_FreeValue(ctx, iter);
53392 *plen = k;
53393 return arr;
53394 fail:
53395 JS_FreeValue(ctx, next_method);
53396 JS_FreeValue(ctx, iter);
53397 JS_FreeValue(ctx, arr);
53398 return JS_EXCEPTION;
53399}
53400
53401static JSValue js_typed_array_constructor_obj(JSContext *ctx,
53402 JSValueConst new_target,
53403 JSValueConst obj,
53404 int classid)
53405{
53406 JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
53407 uint32_t i;
53408 int size_log2;
53409 int64_t len;
53410
53411 size_log2 = typed_array_size_log2(classid);
53412 ret = js_create_from_ctor(ctx, new_target, classid);
53413 if (JS_IsException(ret))
53414 return JS_EXCEPTION;
53415
53416 iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
53417 if (JS_IsException(iter))
53418 goto fail;
53419 if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
53420 uint32_t len1;
53421 arr = js_array_from_iterator(ctx, &len1, obj, iter);
53422 JS_FreeValue(ctx, iter);
53423 if (JS_IsException(arr))
53424 goto fail;
53425 len = len1;
53426 } else {
53427 if (js_get_length64(ctx, &len, obj))
53428 goto fail;
53429 arr = JS_DupValue(ctx, obj);
53430 }
53431
53432 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
53433 len << size_log2);
53434 if (JS_IsException(buffer))
53435 goto fail;
53436 if (typed_array_init(ctx, ret, buffer, 0, len))
53437 goto fail;
53438
53439 for(i = 0; i < len; i++) {
53440 val = JS_GetPropertyUint32(ctx, arr, i);
53441 if (JS_IsException(val))
53442 goto fail;
53443 if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
53444 goto fail;
53445 }
53446 JS_FreeValue(ctx, arr);
53447 return ret;
53448 fail:
53449 JS_FreeValue(ctx, arr);
53450 JS_FreeValue(ctx, ret);
53451 return JS_EXCEPTION;
53452}
53453
53454static JSValue js_typed_array_constructor_ta(JSContext *ctx,
53455 JSValueConst new_target,
53456 JSValueConst src_obj,
53457 int classid)
53458{
53459 JSObject *p, *src_buffer;
53460 JSTypedArray *ta;
53461 JSValue obj, buffer;
53462 uint32_t len, i;
53463 int size_log2;
53464 JSArrayBuffer *src_abuf, *abuf;
53465
53466 obj = js_create_from_ctor(ctx, new_target, classid);
53467 if (JS_IsException(obj))
53468 return obj;
53469 p = JS_VALUE_GET_OBJ(src_obj);
53470 if (typed_array_is_detached(ctx, p)) {
53471 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53472 goto fail;
53473 }
53474 ta = p->u.typed_array;
53475 len = p->u.array.count;
53476 src_buffer = ta->buffer;
53477 src_abuf = src_buffer->u.array_buffer;
53478 size_log2 = typed_array_size_log2(classid);
53479 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
53480 (uint64_t)len << size_log2);
53481 if (JS_IsException(buffer))
53482 goto fail;
53483 /* necessary because it could have been detached */
53484 if (typed_array_is_detached(ctx, p)) {
53485 JS_FreeValue(ctx, buffer);
53486 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53487 goto fail;
53488 }
53489 abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
53490 if (typed_array_init(ctx, obj, buffer, 0, len))
53491 goto fail;
53492 if (p->class_id == classid) {
53493 /* same type: copy the content */
53494 memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
53495 } else {
53496 for(i = 0; i < len; i++) {
53497 JSValue val;
53498 val = JS_GetPropertyUint32(ctx, src_obj, i);
53499 if (JS_IsException(val))
53500 goto fail;
53501 if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
53502 goto fail;
53503 }
53504 }
53505 return obj;
53506 fail:
53507 JS_FreeValue(ctx, obj);
53508 return JS_EXCEPTION;
53509}
53510
53511static JSValue js_typed_array_constructor(JSContext *ctx,
53512 JSValueConst new_target,
53513 int argc, JSValueConst *argv,
53514 int classid)
53515{
53516 JSValue buffer, obj;
53517 JSArrayBuffer *abuf;
53518 int size_log2;
53519 uint64_t len, offset;
53520
53521 size_log2 = typed_array_size_log2(classid);
53522 if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
53523 if (JS_ToIndex(ctx, &len, argv[0]))
53524 return JS_EXCEPTION;
53525 buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
53526 len << size_log2);
53527 if (JS_IsException(buffer))
53528 return JS_EXCEPTION;
53529 offset = 0;
53530 } else {
53531 JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
53532 if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
53533 p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
53534 abuf = p->u.array_buffer;
53535 if (JS_ToIndex(ctx, &offset, argv[1]))
53536 return JS_EXCEPTION;
53537 if (abuf->detached)
53538 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53539 if ((offset & ((1 << size_log2) - 1)) != 0 ||
53540 offset > abuf->byte_length)
53541 return JS_ThrowRangeError(ctx, "invalid offset");
53542 if (JS_IsUndefined(argv[2])) {
53543 if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
53544 goto invalid_length;
53545 len = (abuf->byte_length - offset) >> size_log2;
53546 } else {
53547 if (JS_ToIndex(ctx, &len, argv[2]))
53548 return JS_EXCEPTION;
53549 if (abuf->detached)
53550 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53551 if ((offset + (len << size_log2)) > abuf->byte_length) {
53552 invalid_length:
53553 return JS_ThrowRangeError(ctx, "invalid length");
53554 }
53555 }
53556 buffer = JS_DupValue(ctx, argv[0]);
53557 } else {
53558 if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
53559 p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
53560 return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
53561 } else {
53562 return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
53563 }
53564 }
53565 }
53566
53567 obj = js_create_from_ctor(ctx, new_target, classid);
53568 if (JS_IsException(obj)) {
53569 JS_FreeValue(ctx, buffer);
53570 return JS_EXCEPTION;
53571 }
53572 if (typed_array_init(ctx, obj, buffer, offset, len)) {
53573 JS_FreeValue(ctx, obj);
53574 return JS_EXCEPTION;
53575 }
53576 return obj;
53577}
53578
53579static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
53580{
53581 JSObject *p = JS_VALUE_GET_OBJ(val);
53582 JSTypedArray *ta = p->u.typed_array;
53583 if (ta) {
53584 /* during the GC the finalizers are called in an arbitrary
53585 order so the ArrayBuffer finalizer may have been called */
53586 if (ta->link.next) {
53587 list_del(&ta->link);
53588 }
53589 JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
53590 js_free_rt(rt, ta);
53591 }
53592}
53593
53594static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
53595 JS_MarkFunc *mark_func)
53596{
53597 JSObject *p = JS_VALUE_GET_OBJ(val);
53598 JSTypedArray *ta = p->u.typed_array;
53599 if (ta) {
53600 JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
53601 }
53602}
53603
53604static JSValue js_dataview_constructor(JSContext *ctx,
53605 JSValueConst new_target,
53606 int argc, JSValueConst *argv)
53607{
53608 JSArrayBuffer *abuf;
53609 uint64_t offset;
53610 uint32_t len;
53611 JSValueConst buffer;
53612 JSValue obj;
53613 JSTypedArray *ta;
53614 JSObject *p;
53615
53616 buffer = argv[0];
53617 abuf = js_get_array_buffer(ctx, buffer);
53618 if (!abuf)
53619 return JS_EXCEPTION;
53620 offset = 0;
53621 if (argc > 1) {
53622 if (JS_ToIndex(ctx, &offset, argv[1]))
53623 return JS_EXCEPTION;
53624 }
53625 if (abuf->detached)
53626 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53627 if (offset > abuf->byte_length)
53628 return JS_ThrowRangeError(ctx, "invalid byteOffset");
53629 len = abuf->byte_length - offset;
53630 if (argc > 2 && !JS_IsUndefined(argv[2])) {
53631 uint64_t l;
53632 if (JS_ToIndex(ctx, &l, argv[2]))
53633 return JS_EXCEPTION;
53634 if (l > len)
53635 return JS_ThrowRangeError(ctx, "invalid byteLength");
53636 len = l;
53637 }
53638
53639 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
53640 if (JS_IsException(obj))
53641 return JS_EXCEPTION;
53642 if (abuf->detached) {
53643 /* could have been detached in js_create_from_ctor() */
53644 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53645 goto fail;
53646 }
53647 ta = js_malloc(ctx, sizeof(*ta));
53648 if (!ta) {
53649 fail:
53650 JS_FreeValue(ctx, obj);
53651 return JS_EXCEPTION;
53652 }
53653 p = JS_VALUE_GET_OBJ(obj);
53654 ta->obj = p;
53655 ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
53656 ta->offset = offset;
53657 ta->length = len;
53658 list_add_tail(&ta->link, &abuf->array_list);
53659 p->u.typed_array = ta;
53660 return obj;
53661}
53662
53663static JSValue js_dataview_getValue(JSContext *ctx,
53664 JSValueConst this_obj,
53665 int argc, JSValueConst *argv, int class_id)
53666{
53667 JSTypedArray *ta;
53668 JSArrayBuffer *abuf;
53669 BOOL littleEndian, is_swap;
53670 int size;
53671 uint8_t *ptr;
53672 uint32_t v;
53673 uint64_t pos;
53674
53675 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53676 if (!ta)
53677 return JS_EXCEPTION;
53678 size = 1 << typed_array_size_log2(class_id);
53679 if (JS_ToIndex(ctx, &pos, argv[0]))
53680 return JS_EXCEPTION;
53681 littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]);
53682 is_swap = littleEndian ^ !is_be();
53683 abuf = ta->buffer->u.array_buffer;
53684 if (abuf->detached)
53685 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53686 if ((pos + size) > ta->length)
53687 return JS_ThrowRangeError(ctx, "out of bound");
53688 ptr = abuf->data + ta->offset + pos;
53689
53690 switch(class_id) {
53691 case JS_CLASS_INT8_ARRAY:
53692 return JS_NewInt32(ctx, *(int8_t *)ptr);
53693 case JS_CLASS_UINT8_ARRAY:
53694 return JS_NewInt32(ctx, *(uint8_t *)ptr);
53695 case JS_CLASS_INT16_ARRAY:
53696 v = get_u16(ptr);
53697 if (is_swap)
53698 v = bswap16(v);
53699 return JS_NewInt32(ctx, (int16_t)v);
53700 case JS_CLASS_UINT16_ARRAY:
53701 v = get_u16(ptr);
53702 if (is_swap)
53703 v = bswap16(v);
53704 return JS_NewInt32(ctx, v);
53705 case JS_CLASS_INT32_ARRAY:
53706 v = get_u32(ptr);
53707 if (is_swap)
53708 v = bswap32(v);
53709 return JS_NewInt32(ctx, v);
53710 case JS_CLASS_UINT32_ARRAY:
53711 v = get_u32(ptr);
53712 if (is_swap)
53713 v = bswap32(v);
53714 return JS_NewUint32(ctx, v);
53715 case JS_CLASS_BIG_INT64_ARRAY:
53716 {
53717 uint64_t v;
53718 v = get_u64(ptr);
53719 if (is_swap)
53720 v = bswap64(v);
53721 return JS_NewBigInt64(ctx, v);
53722 }
53723 break;
53724 case JS_CLASS_BIG_UINT64_ARRAY:
53725 {
53726 uint64_t v;
53727 v = get_u64(ptr);
53728 if (is_swap)
53729 v = bswap64(v);
53730 return JS_NewBigUint64(ctx, v);
53731 }
53732 break;
53733 case JS_CLASS_FLOAT32_ARRAY:
53734 {
53735 union {
53736 float f;
53737 uint32_t i;
53738 } u;
53739 v = get_u32(ptr);
53740 if (is_swap)
53741 v = bswap32(v);
53742 u.i = v;
53743 return __JS_NewFloat64(ctx, u.f);
53744 }
53745 case JS_CLASS_FLOAT64_ARRAY:
53746 {
53747 union {
53748 double f;
53749 uint64_t i;
53750 } u;
53751 u.i = get_u64(ptr);
53752 if (is_swap)
53753 u.i = bswap64(u.i);
53754 return __JS_NewFloat64(ctx, u.f);
53755 }
53756 default:
53757 abort();
53758 }
53759}
53760
53761static JSValue js_dataview_setValue(JSContext *ctx,
53762 JSValueConst this_obj,
53763 int argc, JSValueConst *argv, int class_id)
53764{
53765 JSTypedArray *ta;
53766 JSArrayBuffer *abuf;
53767 BOOL littleEndian, is_swap;
53768 int size;
53769 uint8_t *ptr;
53770 uint64_t v64;
53771 uint32_t v;
53772 uint64_t pos;
53773 JSValueConst val;
53774
53775 ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
53776 if (!ta)
53777 return JS_EXCEPTION;
53778 size = 1 << typed_array_size_log2(class_id);
53779 if (JS_ToIndex(ctx, &pos, argv[0]))
53780 return JS_EXCEPTION;
53781 val = argv[1];
53782 v = 0; /* avoid warning */
53783 v64 = 0; /* avoid warning */
53784 if (class_id <= JS_CLASS_UINT32_ARRAY) {
53785 if (JS_ToUint32(ctx, &v, val))
53786 return JS_EXCEPTION;
53787 } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
53788 if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
53789 return JS_EXCEPTION;
53790 } else {
53791 double d;
53792 if (JS_ToFloat64(ctx, &d, val))
53793 return JS_EXCEPTION;
53794 if (class_id == JS_CLASS_FLOAT32_ARRAY) {
53795 union {
53796 float f;
53797 uint32_t i;
53798 } u;
53799 u.f = d;
53800 v = u.i;
53801 } else {
53802 JSFloat64Union u;
53803 u.d = d;
53804 v64 = u.u64;
53805 }
53806 }
53807 littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]);
53808 is_swap = littleEndian ^ !is_be();
53809 abuf = ta->buffer->u.array_buffer;
53810 if (abuf->detached)
53811 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53812 if ((pos + size) > ta->length)
53813 return JS_ThrowRangeError(ctx, "out of bound");
53814 ptr = abuf->data + ta->offset + pos;
53815
53816 switch(class_id) {
53817 case JS_CLASS_INT8_ARRAY:
53818 case JS_CLASS_UINT8_ARRAY:
53819 *ptr = v;
53820 break;
53821 case JS_CLASS_INT16_ARRAY:
53822 case JS_CLASS_UINT16_ARRAY:
53823 if (is_swap)
53824 v = bswap16(v);
53825 put_u16(ptr, v);
53826 break;
53827 case JS_CLASS_INT32_ARRAY:
53828 case JS_CLASS_UINT32_ARRAY:
53829 case JS_CLASS_FLOAT32_ARRAY:
53830 if (is_swap)
53831 v = bswap32(v);
53832 put_u32(ptr, v);
53833 break;
53834 case JS_CLASS_BIG_INT64_ARRAY:
53835 case JS_CLASS_BIG_UINT64_ARRAY:
53836 case JS_CLASS_FLOAT64_ARRAY:
53837 if (is_swap)
53838 v64 = bswap64(v64);
53839 put_u64(ptr, v64);
53840 break;
53841 default:
53842 abort();
53843 }
53844 return JS_UNDEFINED;
53845}
53846
53847static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
53848 JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
53849 JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
53850 JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
53851 JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
53852 JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
53853 JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
53854 JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
53855 JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
53856 JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
53857 JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
53858 JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
53859 JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
53860 JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
53861 JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
53862 JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
53863 JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
53864 JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
53865 JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
53866 JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
53867 JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
53868 JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
53869 JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
53870 JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
53871 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
53872};
53873
53874/* Atomics */
53875#ifdef CONFIG_ATOMICS
53876
53877typedef enum AtomicsOpEnum {
53878 ATOMICS_OP_ADD,
53879 ATOMICS_OP_AND,
53880 ATOMICS_OP_OR,
53881 ATOMICS_OP_SUB,
53882 ATOMICS_OP_XOR,
53883 ATOMICS_OP_EXCHANGE,
53884 ATOMICS_OP_COMPARE_EXCHANGE,
53885 ATOMICS_OP_LOAD,
53886} AtomicsOpEnum;
53887
53888static void *js_atomics_get_ptr(JSContext *ctx,
53889 JSArrayBuffer **pabuf,
53890 int *psize_log2, JSClassID *pclass_id,
53891 JSValueConst obj, JSValueConst idx_val,
53892 int is_waitable)
53893{
53894 JSObject *p;
53895 JSTypedArray *ta;
53896 JSArrayBuffer *abuf;
53897 void *ptr;
53898 uint64_t idx;
53899 BOOL err;
53900 int size_log2;
53901
53902 if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
53903 goto fail;
53904 p = JS_VALUE_GET_OBJ(obj);
53905 if (is_waitable)
53906 err = (p->class_id != JS_CLASS_INT32_ARRAY &&
53907 p->class_id != JS_CLASS_BIG_INT64_ARRAY);
53908 else
53909 err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
53910 p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
53911 if (err) {
53912 fail:
53913 JS_ThrowTypeError(ctx, "integer TypedArray expected");
53914 return NULL;
53915 }
53916 ta = p->u.typed_array;
53917 abuf = ta->buffer->u.array_buffer;
53918 if (!abuf->shared) {
53919 if (is_waitable == 2) {
53920 JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
53921 return NULL;
53922 }
53923 if (abuf->detached) {
53924 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53925 return NULL;
53926 }
53927 }
53928 if (JS_ToIndex(ctx, &idx, idx_val)) {
53929 return NULL;
53930 }
53931 /* RevalidateAtomicAccess(): must test again detached after JS_ToIndex() */
53932 if (abuf->detached) {
53933 JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53934 return NULL;
53935 }
53936 /* if the array buffer is detached, p->u.array.count = 0 */
53937 if (idx >= p->u.array.count) {
53938 JS_ThrowRangeError(ctx, "out-of-bound access");
53939 return NULL;
53940 }
53941 size_log2 = typed_array_size_log2(p->class_id);
53942 ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
53943 if (pabuf)
53944 *pabuf = abuf;
53945 if (psize_log2)
53946 *psize_log2 = size_log2;
53947 if (pclass_id)
53948 *pclass_id = p->class_id;
53949 return ptr;
53950}
53951
53952static JSValue js_atomics_op(JSContext *ctx,
53953 JSValueConst this_obj,
53954 int argc, JSValueConst *argv, int op)
53955{
53956 int size_log2;
53957 uint64_t v, a, rep_val;
53958 void *ptr;
53959 JSValue ret;
53960 JSClassID class_id;
53961 JSArrayBuffer *abuf;
53962
53963 ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
53964 argv[0], argv[1], 0);
53965 if (!ptr)
53966 return JS_EXCEPTION;
53967 rep_val = 0;
53968 if (op == ATOMICS_OP_LOAD) {
53969 v = 0;
53970 } else {
53971 if (size_log2 == 3) {
53972 int64_t v64;
53973 if (JS_ToBigInt64(ctx, &v64, argv[2]))
53974 return JS_EXCEPTION;
53975 v = v64;
53976 if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53977 if (JS_ToBigInt64(ctx, &v64, argv[3]))
53978 return JS_EXCEPTION;
53979 rep_val = v64;
53980 }
53981 } else {
53982 uint32_t v32;
53983 if (JS_ToUint32(ctx, &v32, argv[2]))
53984 return JS_EXCEPTION;
53985 v = v32;
53986 if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
53987 if (JS_ToUint32(ctx, &v32, argv[3]))
53988 return JS_EXCEPTION;
53989 rep_val = v32;
53990 }
53991 }
53992 if (abuf->detached)
53993 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
53994 }
53995
53996 switch(op | (size_log2 << 3)) {
53997
53998#define OP(op_name, func_name) \
53999 case ATOMICS_OP_ ## op_name | (0 << 3): \
54000 a = func_name((_Atomic(uint8_t) *)ptr, v); \
54001 break; \
54002 case ATOMICS_OP_ ## op_name | (1 << 3): \
54003 a = func_name((_Atomic(uint16_t) *)ptr, v); \
54004 break; \
54005 case ATOMICS_OP_ ## op_name | (2 << 3): \
54006 a = func_name((_Atomic(uint32_t) *)ptr, v); \
54007 break; \
54008 case ATOMICS_OP_ ## op_name | (3 << 3): \
54009 a = func_name((_Atomic(uint64_t) *)ptr, v); \
54010 break;
54011
54012 OP(ADD, atomic_fetch_add)
54013 OP(AND, atomic_fetch_and)
54014 OP(OR, atomic_fetch_or)
54015 OP(SUB, atomic_fetch_sub)
54016 OP(XOR, atomic_fetch_xor)
54017 OP(EXCHANGE, atomic_exchange)
54018#undef OP
54019
54020 case ATOMICS_OP_LOAD | (0 << 3):
54021 a = atomic_load((_Atomic(uint8_t) *)ptr);
54022 break;
54023 case ATOMICS_OP_LOAD | (1 << 3):
54024 a = atomic_load((_Atomic(uint16_t) *)ptr);
54025 break;
54026 case ATOMICS_OP_LOAD | (2 << 3):
54027 a = atomic_load((_Atomic(uint32_t) *)ptr);
54028 break;
54029 case ATOMICS_OP_LOAD | (3 << 3):
54030 a = atomic_load((_Atomic(uint64_t) *)ptr);
54031 break;
54032
54033 case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
54034 {
54035 uint8_t v1 = v;
54036 atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
54037 a = v1;
54038 }
54039 break;
54040 case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
54041 {
54042 uint16_t v1 = v;
54043 atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
54044 a = v1;
54045 }
54046 break;
54047 case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
54048 {
54049 uint32_t v1 = v;
54050 atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
54051 a = v1;
54052 }
54053 break;
54054 case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
54055 {
54056 uint64_t v1 = v;
54057 atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
54058 a = v1;
54059 }
54060 break;
54061 default:
54062 abort();
54063 }
54064
54065 switch(class_id) {
54066 case JS_CLASS_INT8_ARRAY:
54067 a = (int8_t)a;
54068 goto done;
54069 case JS_CLASS_UINT8_ARRAY:
54070 a = (uint8_t)a;
54071 goto done;
54072 case JS_CLASS_INT16_ARRAY:
54073 a = (int16_t)a;
54074 goto done;
54075 case JS_CLASS_UINT16_ARRAY:
54076 a = (uint16_t)a;
54077 goto done;
54078 case JS_CLASS_INT32_ARRAY:
54079 done:
54080 ret = JS_NewInt32(ctx, a);
54081 break;
54082 case JS_CLASS_UINT32_ARRAY:
54083 ret = JS_NewUint32(ctx, a);
54084 break;
54085 case JS_CLASS_BIG_INT64_ARRAY:
54086 ret = JS_NewBigInt64(ctx, a);
54087 break;
54088 case JS_CLASS_BIG_UINT64_ARRAY:
54089 ret = JS_NewBigUint64(ctx, a);
54090 break;
54091 default:
54092 abort();
54093 }
54094 return ret;
54095}
54096
54097static JSValue js_atomics_store(JSContext *ctx,
54098 JSValueConst this_obj,
54099 int argc, JSValueConst *argv)
54100{
54101 int size_log2;
54102 void *ptr;
54103 JSValue ret;
54104 JSArrayBuffer *abuf;
54105
54106 ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
54107 argv[0], argv[1], 0);
54108 if (!ptr)
54109 return JS_EXCEPTION;
54110 if (size_log2 == 3) {
54111 int64_t v64;
54112 ret = JS_ToBigIntFree(ctx, JS_DupValue(ctx, argv[2]));
54113 if (JS_IsException(ret))
54114 return ret;
54115 if (JS_ToBigInt64(ctx, &v64, ret)) {
54116 JS_FreeValue(ctx, ret);
54117 return JS_EXCEPTION;
54118 }
54119 if (abuf->detached)
54120 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
54121 atomic_store((_Atomic(uint64_t) *)ptr, v64);
54122 } else {
54123 uint32_t v;
54124 /* XXX: spec, would be simpler to return the written value */
54125 ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
54126 if (JS_IsException(ret))
54127 return ret;
54128 if (JS_ToUint32(ctx, &v, ret)) {
54129 JS_FreeValue(ctx, ret);
54130 return JS_EXCEPTION;
54131 }
54132 if (abuf->detached)
54133 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
54134 switch(size_log2) {
54135 case 0:
54136 atomic_store((_Atomic(uint8_t) *)ptr, v);
54137 break;
54138 case 1:
54139 atomic_store((_Atomic(uint16_t) *)ptr, v);
54140 break;
54141 case 2:
54142 atomic_store((_Atomic(uint32_t) *)ptr, v);
54143 break;
54144 default:
54145 abort();
54146 }
54147 }
54148 return ret;
54149}
54150
54151static JSValue js_atomics_isLockFree(JSContext *ctx,
54152 JSValueConst this_obj,
54153 int argc, JSValueConst *argv)
54154{
54155 int v, ret;
54156 if (JS_ToInt32Sat(ctx, &v, argv[0]))
54157 return JS_EXCEPTION;
54158 ret = (v == 1 || v == 2 || v == 4 || v == 8);
54159 return JS_NewBool(ctx, ret);
54160}
54161
54162typedef struct JSAtomicsWaiter {
54163 struct list_head link;
54164 BOOL linked;
54165 pthread_cond_t cond;
54166 int32_t *ptr;
54167} JSAtomicsWaiter;
54168
54169static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
54170static struct list_head js_atomics_waiter_list =
54171 LIST_HEAD_INIT(js_atomics_waiter_list);
54172
54173static JSValue js_atomics_wait(JSContext *ctx,
54174 JSValueConst this_obj,
54175 int argc, JSValueConst *argv)
54176{
54177 int64_t v;
54178 int32_t v32;
54179 void *ptr;
54180 int64_t timeout;
54181 struct timespec ts;
54182 JSAtomicsWaiter waiter_s, *waiter;
54183 int ret, size_log2, res;
54184 double d;
54185
54186 ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
54187 argv[0], argv[1], 2);
54188 if (!ptr)
54189 return JS_EXCEPTION;
54190 if (size_log2 == 3) {
54191 if (JS_ToBigInt64(ctx, &v, argv[2]))
54192 return JS_EXCEPTION;
54193 } else {
54194 if (JS_ToInt32(ctx, &v32, argv[2]))
54195 return JS_EXCEPTION;
54196 v = v32;
54197 }
54198 if (JS_ToFloat64(ctx, &d, argv[3]))
54199 return JS_EXCEPTION;
54200 /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */
54201 if (isnan(d) || d >= 0x1p63)
54202 timeout = INT64_MAX;
54203 else if (d < 0)
54204 timeout = 0;
54205 else
54206 timeout = (int64_t)d;
54207 if (!ctx->rt->can_block)
54208 return JS_ThrowTypeError(ctx, "cannot block in this thread");
54209
54210 /* XXX: inefficient if large number of waiters, should hash on
54211 'ptr' value */
54212 /* XXX: use Linux futexes when available ? */
54213 pthread_mutex_lock(&js_atomics_mutex);
54214 if (size_log2 == 3) {
54215 res = *(int64_t *)ptr != v;
54216 } else {
54217 res = *(int32_t *)ptr != v;
54218 }
54219 if (res) {
54220 pthread_mutex_unlock(&js_atomics_mutex);
54221 return JS_AtomToString(ctx, JS_ATOM_not_equal);
54222 }
54223
54224 waiter = &waiter_s;
54225 waiter->ptr = ptr;
54226 pthread_cond_init(&waiter->cond, NULL);
54227 waiter->linked = TRUE;
54228 list_add_tail(&waiter->link, &js_atomics_waiter_list);
54229
54230 if (timeout == INT64_MAX) {
54231 pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
54232 ret = 0;
54233 } else {
54234 /* XXX: use clock monotonic */
54235 clock_gettime(CLOCK_REALTIME, &ts);
54236 ts.tv_sec += timeout / 1000;
54237 ts.tv_nsec += (timeout % 1000) * 1000000;
54238 if (ts.tv_nsec >= 1000000000) {
54239 ts.tv_nsec -= 1000000000;
54240 ts.tv_sec++;
54241 }
54242 ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
54243 &ts);
54244 }
54245 if (waiter->linked)
54246 list_del(&waiter->link);
54247 pthread_mutex_unlock(&js_atomics_mutex);
54248 pthread_cond_destroy(&waiter->cond);
54249 if (ret == ETIMEDOUT) {
54250 return JS_AtomToString(ctx, JS_ATOM_timed_out);
54251 } else {
54252 return JS_AtomToString(ctx, JS_ATOM_ok);
54253 }
54254}
54255
54256static JSValue js_atomics_notify(JSContext *ctx,
54257 JSValueConst this_obj,
54258 int argc, JSValueConst *argv)
54259{
54260 struct list_head *el, *el1, waiter_list;
54261 int32_t count, n;
54262 void *ptr;
54263 JSAtomicsWaiter *waiter;
54264 JSArrayBuffer *abuf;
54265
54266 ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
54267 if (!ptr)
54268 return JS_EXCEPTION;
54269
54270 if (JS_IsUndefined(argv[2])) {
54271 count = INT32_MAX;
54272 } else {
54273 if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
54274 return JS_EXCEPTION;
54275 }
54276 if (abuf->detached)
54277 return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
54278
54279 n = 0;
54280 if (abuf->shared && count > 0) {
54281 pthread_mutex_lock(&js_atomics_mutex);
54282 init_list_head(&waiter_list);
54283 list_for_each_safe(el, el1, &js_atomics_waiter_list) {
54284 waiter = list_entry(el, JSAtomicsWaiter, link);
54285 if (waiter->ptr == ptr) {
54286 list_del(&waiter->link);
54287 waiter->linked = FALSE;
54288 list_add_tail(&waiter->link, &waiter_list);
54289 n++;
54290 if (n >= count)
54291 break;
54292 }
54293 }
54294 list_for_each(el, &waiter_list) {
54295 waiter = list_entry(el, JSAtomicsWaiter, link);
54296 pthread_cond_signal(&waiter->cond);
54297 }
54298 pthread_mutex_unlock(&js_atomics_mutex);
54299 }
54300 return JS_NewInt32(ctx, n);
54301}
54302
54303static const JSCFunctionListEntry js_atomics_funcs[] = {
54304 JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
54305 JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
54306 JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
54307 JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
54308 JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
54309 JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
54310 JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
54311 JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
54312 JS_CFUNC_DEF("store", 3, js_atomics_store ),
54313 JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
54314 JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
54315 JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
54316 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
54317};
54318
54319static const JSCFunctionListEntry js_atomics_obj[] = {
54320 JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
54321};
54322
54323void JS_AddIntrinsicAtomics(JSContext *ctx)
54324{
54325 /* add Atomics as autoinit object */
54326 JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
54327}
54328
54329#endif /* CONFIG_ATOMICS */
54330
54331void JS_AddIntrinsicTypedArrays(JSContext *ctx)
54332{
54333 JSValue typed_array_base_proto, typed_array_base_func;
54334 JSValueConst array_buffer_func, shared_array_buffer_func;
54335 int i;
54336
54337 ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
54338 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
54339 js_array_buffer_proto_funcs,
54340 countof(js_array_buffer_proto_funcs));
54341
54342 array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
54343 js_array_buffer_constructor, 1,
54344 ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
54345 JS_SetPropertyFunctionList(ctx, array_buffer_func,
54346 js_array_buffer_funcs,
54347 countof(js_array_buffer_funcs));
54348
54349 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
54350 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
54351 js_shared_array_buffer_proto_funcs,
54352 countof(js_shared_array_buffer_proto_funcs));
54353
54354 shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
54355 js_shared_array_buffer_constructor, 1,
54356 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
54357 JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
54358 js_shared_array_buffer_funcs,
54359 countof(js_shared_array_buffer_funcs));
54360
54361 typed_array_base_proto = JS_NewObject(ctx);
54362 JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
54363 js_typed_array_base_proto_funcs,
54364 countof(js_typed_array_base_proto_funcs));
54365
54366 /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
54367 JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
54368 /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
54369 JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
54370 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
54371
54372 typed_array_base_func = JS_NewCFunction2(ctx, js_typed_array_base_constructor,
54373 "TypedArray", 0, JS_CFUNC_constructor_or_func, 0);
54374 JS_SetPropertyFunctionList(ctx, typed_array_base_func,
54375 js_typed_array_base_funcs,
54376 countof(js_typed_array_base_funcs));
54377 JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
54378
54379 /* Used to squelch a -Wcast-function-type warning. */
54380 JSCFunctionType ft = { .generic_magic = js_typed_array_constructor };
54381 for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
54382 JSValue func_obj;
54383 char buf[ATOM_GET_STR_BUF_SIZE];
54384 const char *name;
54385
54386 ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
54387 JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
54388 "BYTES_PER_ELEMENT",
54389 JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
54390 0);
54391 name = JS_AtomGetStr(ctx, buf, sizeof(buf),
54392 JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
54393 func_obj = JS_NewCFunction3(ctx, ft.generic,
54394 name, 3, JS_CFUNC_constructor_magic, i,
54395 typed_array_base_func);
54396 JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
54397 JS_DefinePropertyValueStr(ctx, func_obj,
54398 "BYTES_PER_ELEMENT",
54399 JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
54400 0);
54401 }
54402 JS_FreeValue(ctx, typed_array_base_proto);
54403 JS_FreeValue(ctx, typed_array_base_func);
54404
54405 /* DataView */
54406 ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
54407 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
54408 js_dataview_proto_funcs,
54409 countof(js_dataview_proto_funcs));
54410 JS_NewGlobalCConstructorOnly(ctx, "DataView",
54411 js_dataview_constructor, 1,
54412 ctx->class_proto[JS_CLASS_DATAVIEW]);
54413 /* Atomics */
54414#ifdef CONFIG_ATOMICS
54415 JS_AddIntrinsicAtomics(ctx);
54416#endif
54417}
54418
54419/* WeakRef */
54420
54421typedef struct JSWeakRefData {
54422 JSWeakRefHeader weakref_header;
54423 JSValue target;
54424} JSWeakRefData;
54425
54426static void js_weakref_finalizer(JSRuntime *rt, JSValue val)
54427{
54428 JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF);
54429 if (!wrd)
54430 return;
54431 js_weakref_free(rt, wrd->target);
54432 list_del(&wrd->weakref_header.link);
54433 js_free_rt(rt, wrd);
54434}
54435
54436static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
54437{
54438 JSWeakRefData *wrd = container_of(wh, JSWeakRefData, weakref_header);
54439
54440 if (!js_weakref_is_live(wrd->target)) {
54441 js_weakref_free(rt, wrd->target);
54442 wrd->target = JS_UNDEFINED;
54443 }
54444}
54445
54446static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
54447 int argc, JSValueConst *argv)
54448{
54449 JSValueConst arg;
54450 JSValue obj;
54451
54452 if (JS_IsUndefined(new_target))
54453 return JS_ThrowTypeError(ctx, "constructor requires 'new'");
54454 arg = argv[0];
54455 if (!js_weakref_is_target(arg))
54456 return JS_ThrowTypeError(ctx, "invalid target");
54457 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF);
54458 if (JS_IsException(obj))
54459 return JS_EXCEPTION;
54460 JSWeakRefData *wrd = js_mallocz(ctx, sizeof(*wrd));
54461 if (!wrd) {
54462 JS_FreeValue(ctx, obj);
54463 return JS_EXCEPTION;
54464 }
54465 wrd->target = js_weakref_new(ctx, arg);
54466 wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF;
54467 list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list);
54468 JS_SetOpaque(obj, wrd);
54469 return obj;
54470}
54471
54472static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
54473{
54474 JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF);
54475 if (!wrd)
54476 return JS_EXCEPTION;
54477 if (js_weakref_is_live(wrd->target))
54478 return JS_DupValue(ctx, wrd->target);
54479 else
54480 return JS_UNDEFINED;
54481}
54482
54483static const JSCFunctionListEntry js_weakref_proto_funcs[] = {
54484 JS_CFUNC_DEF("deref", 0, js_weakref_deref ),
54485 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ),
54486};
54487
54488static const JSClassShortDef js_weakref_class_def[] = {
54489 { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */
54490};
54491
54492typedef struct JSFinRecEntry {
54493 struct list_head link;
54494 JSValue target;
54495 JSValue held_val;
54496 JSValue token;
54497} JSFinRecEntry;
54498
54499typedef struct JSFinalizationRegistryData {
54500 JSWeakRefHeader weakref_header;
54501 struct list_head entries; /* list of JSFinRecEntry.link */
54502 JSContext *ctx;
54503 JSValue cb;
54504} JSFinalizationRegistryData;
54505
54506static void js_finrec_finalizer(JSRuntime *rt, JSValue val)
54507{
54508 JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
54509 if (frd) {
54510 struct list_head *el, *el1;
54511 list_for_each_safe(el, el1, &frd->entries) {
54512 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
54513 js_weakref_free(rt, fre->target);
54514 js_weakref_free(rt, fre->token);
54515 JS_FreeValueRT(rt, fre->held_val);
54516 js_free_rt(rt, fre);
54517 }
54518 JS_FreeValueRT(rt, frd->cb);
54519 list_del(&frd->weakref_header.link);
54520 js_free_rt(rt, frd);
54521 }
54522}
54523
54524static void js_finrec_mark(JSRuntime *rt, JSValueConst val,
54525 JS_MarkFunc *mark_func)
54526{
54527 JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
54528 struct list_head *el;
54529 if (frd) {
54530 list_for_each(el, &frd->entries) {
54531 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
54532 JS_MarkValue(rt, fre->held_val, mark_func);
54533 }
54534 JS_MarkValue(rt, frd->cb, mark_func);
54535 }
54536}
54537
54538static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv)
54539{
54540 return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]);
54541}
54542
54543static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
54544{
54545 JSFinalizationRegistryData *frd = container_of(wh, JSFinalizationRegistryData, weakref_header);
54546 struct list_head *el, *el1;
54547
54548 list_for_each_safe(el, el1, &frd->entries) {
54549 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
54550
54551 if (!js_weakref_is_live(fre->token)) {
54552 js_weakref_free(rt, fre->token);
54553 fre->token = JS_UNDEFINED;
54554 }
54555
54556 if (!js_weakref_is_live(fre->target)) {
54557 JSValueConst args[2];
54558 args[0] = frd->cb;
54559 args[1] = fre->held_val;
54560 JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args);
54561
54562 js_weakref_free(rt, fre->target);
54563 js_weakref_free(rt, fre->token);
54564 JS_FreeValueRT(rt, fre->held_val);
54565 list_del(&fre->link);
54566 js_free_rt(rt, fre);
54567 }
54568 }
54569}
54570
54571static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target,
54572 int argc, JSValueConst *argv)
54573{
54574 JSValueConst cb;
54575 JSValue obj;
54576 JSFinalizationRegistryData *frd;
54577
54578 if (JS_IsUndefined(new_target))
54579 return JS_ThrowTypeError(ctx, "constructor requires 'new'");
54580 cb = argv[0];
54581 if (!JS_IsFunction(ctx, cb))
54582 return JS_ThrowTypeError(ctx, "argument must be a function");
54583
54584 obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY);
54585 if (JS_IsException(obj))
54586 return JS_EXCEPTION;
54587 frd = js_mallocz(ctx, sizeof(*frd));
54588 if (!frd) {
54589 JS_FreeValue(ctx, obj);
54590 return JS_EXCEPTION;
54591 }
54592 frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC;
54593 list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list);
54594 init_list_head(&frd->entries);
54595 frd->ctx = ctx; /* XXX: JS_DupContext() ? */
54596 frd->cb = JS_DupValue(ctx, cb);
54597 JS_SetOpaque(obj, frd);
54598 return obj;
54599}
54600
54601static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val,
54602 int argc, JSValueConst *argv)
54603{
54604 JSValueConst target, held_val, token;
54605 JSFinalizationRegistryData *frd;
54606 JSFinRecEntry *fre;
54607
54608 frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
54609 if (!frd)
54610 return JS_EXCEPTION;
54611 target = argv[0];
54612 held_val = argv[1];
54613 token = argc > 2 ? argv[2] : JS_UNDEFINED;
54614
54615 if (!js_weakref_is_target(target))
54616 return JS_ThrowTypeError(ctx, "invalid target");
54617 if (js_same_value(ctx, target, held_val))
54618 return JS_ThrowTypeError(ctx, "held value cannot be the target");
54619 if (!JS_IsUndefined(token) && !js_weakref_is_target(token))
54620 return JS_ThrowTypeError(ctx, "invalid unregister token");
54621 fre = js_malloc(ctx, sizeof(*fre));
54622 if (!fre)
54623 return JS_EXCEPTION;
54624 fre->target = js_weakref_new(ctx, target);
54625 fre->held_val = JS_DupValue(ctx, held_val);
54626 fre->token = js_weakref_new(ctx, token);
54627 list_add_tail(&fre->link, &frd->entries);
54628 return JS_UNDEFINED;
54629}
54630
54631static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
54632{
54633 JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
54634 JSValueConst token;
54635 BOOL removed;
54636 struct list_head *el, *el1;
54637
54638 if (!frd)
54639 return JS_EXCEPTION;
54640 token = argv[0];
54641 if (!js_weakref_is_target(token))
54642 return JS_ThrowTypeError(ctx, "invalid unregister token");
54643
54644 removed = FALSE;
54645 list_for_each_safe(el, el1, &frd->entries) {
54646 JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
54647 if (js_weakref_is_live(fre->token) && js_same_value(ctx, fre->token, token)) {
54648 js_weakref_free(ctx->rt, fre->target);
54649 js_weakref_free(ctx->rt, fre->token);
54650 JS_FreeValue(ctx, fre->held_val);
54651 list_del(&fre->link);
54652 js_free(ctx, fre);
54653 removed = TRUE;
54654 }
54655 }
54656 return JS_NewBool(ctx, removed);
54657}
54658
54659static const JSCFunctionListEntry js_finrec_proto_funcs[] = {
54660 JS_CFUNC_DEF("register", 2, js_finrec_register ),
54661 JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ),
54662 JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ),
54663};
54664
54665static const JSClassShortDef js_finrec_class_def[] = {
54666 { JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */
54667};
54668
54669void JS_AddIntrinsicWeakRef(JSContext *ctx)
54670{
54671 JSRuntime *rt = ctx->rt;
54672
54673 /* WeakRef */
54674 if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) {
54675 init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF,
54676 countof(js_weakref_class_def));
54677 }
54678 ctx->class_proto[JS_CLASS_WEAK_REF] = JS_NewObject(ctx);
54679 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_WEAK_REF],
54680 js_weakref_proto_funcs,
54681 countof(js_weakref_proto_funcs));
54682 JS_NewGlobalCConstructor(ctx, "WeakRef", js_weakref_constructor, 1, ctx->class_proto[JS_CLASS_WEAK_REF]);
54683
54684 /* FinalizationRegistry */
54685 if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) {
54686 init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY,
54687 countof(js_finrec_class_def));
54688 }
54689 ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY] = JS_NewObject(ctx);
54690 JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY],
54691 js_finrec_proto_funcs,
54692 countof(js_finrec_proto_funcs));
54693 JS_NewGlobalCConstructor(ctx, "FinalizationRegistry", js_finrec_constructor, 1, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY]);
54694}
54695